% RETRIEVE retrieves a collection of objects from an LTPDA repository.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DESCRIPTION: This static method retrieves a collection of objects from an
%              LTPDA repository.
%
% CALL:    objs    = retrieve(conn, obj_id_1, obj_id_2)
%          [o1,o2] = retrieve(conn, obj_id_1, obj_id_2)
%          [o1,o2] = retrieve(conn, 'binary', obj_id_1, obj_id_2)
%          objs    = retrieve(conn, 'Collection', coll_id)
%          objs    = retrieve(conn, 'binary', 'Collection', coll_id)
%
% INPUTS:
%          conn       - database connection object
%          obj_id_N   - an object ID
%          coll_id    - a collection ID
%          'binary'   - to retrieve a binary representation of the object
%                       (if stored)
%
% OUTPUTS:
%          objs          - the retrieved object(s) as a cell array.*
%          o1,o2,...,oN  - returns the first N objects
%
%
% If more than one object is retrieved and only one output is specified
% then the output is a cell array of objects.
%
% If only a single object is requested, it is returned as an object,
% not packed in a cell array.
%
% VERSION:     $Id: retrieve.m,v 1.29 2011/11/30 06:49:01 hewitson Exp $
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function varargout = retrieve(varargin)
  
  % Check if this is a call for parameters
  if utils.helper.isinfocall(varargin{:})
    varargout{1} = getInfo(varargin{3});
    return
  end
  
  import utils.const.*
  utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename);
  
  if nargin == 0
    help(mfilename);
    error('### Incorrect inputs');
  end
  
  objs = [];
  conn = varargin{1};
  if ~isa(conn, 'mpipeline.repository.RepositoryConnection')
    error('### the first argument should be a mpipeline.repository.RepositoryConnection connection object.');
  end
  
  % Unlock the connection only if the connection was not locked
  unlockConnection = ~conn.isLocked();
  
  rm = LTPDARepositoryManager;
  
  try
    
    if ~conn.isConnected
      if isempty(conn.openConnection());
        return
      end
    end
    
    conn.setLocked(true);
    
    % reload connection table if we have a GUI
    if ~isempty(rm.gui)
      rm.gui.reloadConnectionTable();
    end
    
    % Get username and user id
    username = char(conn.getUsername);
    userid   = utils.jmysql.getUserID(conn, username);
    
    if ~isempty(userid)
      utils.helper.msg(msg.PROC1, 'got user id %d for user: %s', userid, username);
      if userid < 1 || isnan(userid)  || strcmp(userid, 'No Data') || ischar(userid)
        error('### Unknown username.');
      end
    else
      error('### Could not determine user id. Can not proceed.');
    end
    
    binary = false;
    if nargin >= 3 && ischar(varargin{2}) && strcmpi(varargin{2}, 'binary')
      %%%  retrieve(conn, 'binary', obj_id_1, obj_id_2)
      %%%  retrieve(conn, 'binary', 'Collection', coll_id)
      binary = true;
      if nargin == 4 && ischar(varargin{3}) && strcmpi(varargin{3}, 'Collection') && isnumeric(varargin{4}) && numel(varargin{4}) == 1
        cid = varargin{4};
        % Get a list of object IDs from the collection ID
        ids = mpipeline.repository.MySQLUtils.getObjectIDsFromCollectionID(conn, cid);
      elseif nargin >= 3 && isnumeric([varargin{3:end}])
        ids = [varargin{3:end}];
      else
        help(mfilename)
        error('### Incorrect usage');
      end
      
    elseif nargin == 3 && ischar(varargin{2}) && strcmpi(varargin{2}, 'Collection') && isnumeric(varargin{3}) && numel(varargin{3}) == 1
      %%%  retrieve(conn, 'Collection', coll_id)
      cid = varargin{3};
      % Get a list of object IDs from the collection ID
      ids = mpipeline.repository.MySQLUtils.getObjectIDsFromCollectionID(conn, cid);
      
    elseif nargin >= 2 && isnumeric([varargin{2:end}])
      %%%  retrieve(conn, obj_id_1, obj_id_2)
      ids = [varargin{2:end}];
      
    else
      help(mfilename)
      error('### Incorrect usage');
    end
    
    utils.helper.msg(msg.PROC1, ['retrieving objects ' utils.xml.mat2str(ids)]);
    
    v = ver('LTPDA');
    for j=1:length(ids)

      % It is only possible to download the object if the object in the
      % database was submitted with the same or lower LTPDA version as the
      % current version.
      q = sprintf('SELECT version FROM objmeta WHERE obj_id=%d', ids(j));
      try
      vDb = utils.jmysql.dbquery(conn, q);
      catch ME
        disp(ME);
      end
      if utils.helper.ver2num(v.Version) < utils.helper.ver2num(vDb{1})
        error('### The object with the ID %d was submitted with a higher LTPDA version than you use %s. Please update your LTPDA version.', ids(j), vDb{1});
      end
      
      % Get object
      if binary
        % Retrieve the bytes
        q =  sprintf('select mat from bobjs where obj_id="%d"', ids(j));
        results = conn.query(q);
%         dd = '';
        while results.next
          dd = results.getObject(1);
        end
        
        if strcmp(dd, 'No Data') || isempty(dd)
          error('Failed to get binary data for object %d', ids(j));
        end
        % Write bytes out to a temp MAT file
        fname = [tempname '.mat'];
        fd = fopen(fname, 'w+');
        fwrite(fd, dd, 'int8');
        fclose(fd);
        % Load the MAT data to a structure
        obj = load(fname);
        % Delete temp file
        delete(fname);
        % Get the struct out
        obj = obj.objs;
        % Get the object class
        scl = char(mpipeline.repository.MySQLUtils.getObjectTypeForID(conn, ids(j)));

        % Check if the retrieved object is a struct
        if isstruct(obj)
          % Call the constructor with this struct
          fcn_name   = [scl '.update_struct'];
          obj = feval(fcn_name, obj, obj.tbxver);
          obj = feval(scl, obj);
        end
        % Add tyo object array
        objs = [objs {obj}];
      else
        xdoc = utils.jmysql.getXdoc(conn, ids(j));
        if ~isempty(xdoc)
          obj = utils.xml.xmlread(xdoc);
          if j==1
            objs = {obj};
          else
            objs = [objs {obj}];
          end
          
          % make transaction entry
          t     = time();
          tdate = t.format('yyyy-mm-dd HH:MM:SS');
          try
            message = utils.jmysql.insert(conn, ...
              'transactions',...
              'obj_id', ids(j),...
              'user_id', userid,...
              'transdate', tdate,...
              'direction', 'out'...
              );
            
            utils.helper.msg(msg.PROC1, 'updated transactions table');
          catch
            error('### Failed to make entry in transactions table');
          end
        else
          warning('!!! Error retrieving object: %d', ids(j));
        end % End empty Xdoc
      end % End binary
    end % End id loop
    
  catch ME
    fprintf(2, [ME.message, '\n\n']);
    conn.setLocked(false);
    rethrow(ME)
  end
  
  if unlockConnection
    conn.setLocked(false);
  end
  
  % reset Timer
  LTPDARepositoryManager.resetTimer(rm.timerClearPass, conn);
  LTPDARepositoryManager.resetTimer(rm.timerDisconnect, conn);
      
  % Set outputs
  if nargout == 1
    if length(objs) == 1
      varargout{1} = objs{1};
    else
      varargout{1} = objs;
    end
  else
    for k=1:nargout
      varargout{k} = objs{k};
    end
  end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                               Local Functions                               %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
function ii = getInfo(varargin)
  if nargin == 1 && strcmpi(varargin{1}, 'None')
    sets = {};
    pl   = [];
  else
    sets = {'Default'};
    pl   = getDefaultPlist;
  end
  % Build info object
  ii = minfo(mfilename, 'ltpda_uo', 'ltpda', utils.const.categories.internal, '$Id: retrieve.m,v 1.29 2011/11/30 06:49:01 hewitson Exp $', sets, pl);
  ii.setModifier(false);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
function plout = getDefaultPlist()
  persistent pl;  
  if exist('pl', 'var')==0 || isempty(pl)
    pl = buildplist();
  end
  plout = pl;  
end

function plo = buildplist()
  plo = plist();
  
  p = param({'conn', 'A database object'}, paramValue.EMPTY_DOUBLE);
  plo.append(p);
  
  p = param({'ids', 'IDs which should be collected'}, paramValue.EMPTY_DOUBLE);
  plo.append(p);
end



