%ORGANIZER class definition
%
%   Supported options:
%      repository: directory where all models will be saved
%      prefix:     prefix for saved model names
%      steps:      requested steps
%      trunkprefix:prefix of previous run with a different prefix. Used to branch.
%
%   Usage:
%      org = organizer(varargin)
%
%   Examples:
%      org = organizer('repository','Models/','prefix','AGU2015','steps',0);  %build an empty organizer object with a given repository

classdef organizer < handle
    properties (SetAccess=private) 
		% {{{
		currentstep   =0;
	end
    properties (SetAccess=public) 
		repository    ='./';
		prefix        ='model.';
		trunkprefix   ='';
		steps         =[];
		requestedsteps=[0];
		upload_server='';
		upload_path='';
		upload_login='';
		upload_port=0;
		download=0;
		%}}}
	end
	methods
		function org=organizer(varargin) % {{{

			%process options
			options=pairoptions(varargin{:});

			%Get prefix
			prefix=getfieldvalue(options,'prefix','model.');
			if ~ischar(prefix),                            error('prefix is not a string'); end
			if ~strcmp(regexprep(prefix,'\s+',''),prefix), error('prefix should not have any white space'); end
			org.prefix=prefix;

			%Get repository
			repository=getfieldvalue(options,'repository','./');
			if ~ischar(repository),        error('repository is not a string'); end
			if exist(repository,'dir')~=7, error(['Directory ' repository ' not found']), end
			org.repository=repository;

			%Get steps
			org.requestedsteps=getfieldvalue(options,'steps',0);

			%Get trunk prefix (only if provided by user)
			if exist(options,'trunkprefix'),
				trunkprefix=getfieldvalue(options,'trunkprefix','');
				if ~ischar(trunkprefix),                                 error('trunkprefix is not a string'); end
				if ~strcmp(regexprep(trunkprefix,'\s+',''),trunkprefix), error('trunkprefix should not have any white space'); end
				org.trunkprefix=trunkprefix;
			end

			%Get upload information, if provided
			org.upload_server=getfieldvalue(options,'upload_server','');
			org.upload_path=getfieldvalue(options,'upload_path','');
			org.upload_login=getfieldvalue(options,'upload_login','');
			org.upload_port=getfieldvalue(options,'upload_port',0);
			org.download=getfieldvalue(options,'download',0);

		end
		%}}}
		function disp(org) % {{{
			disp(sprintf('   Repository: ''%s''',org.repository));
			disp(sprintf('   Prefix:     ''%s''',org.prefix));
			if isempty(org.steps)
				disp('   no step');
			else
				for i=1:length(org.steps),
					disp(sprintf('   step #%2i: ''%s''',org.steps(i).id,org.steps(i).string));
				end
			end
			if isempty(org.upload_server),
				disp('   no upload info');
			else
				disp(sprintf('   upload_server:     ''%s''',org.upload_server));
				disp(sprintf('   upload_login:     ''%s''',org.upload_login));
				disp(sprintf('   upload_path:     ''%s''',org.upload_path));
				disp(sprintf('   upload_port:     ''%i''',org.upload_port));
			end
		end
		%}}}
		function md=load(org,string),% {{{

			%Get model path
			if ~ischar(string), error('argument provided is not a string'); end
			path=[org.repository '/' org.prefix string];

			%figure out if the model is there
			if exist(path,'file'),
				struc=load(path,'-mat');
				name=char(fieldnames(struc));
				md=struc.(name);
				if nargout,
					varargout{1}=md;
				end
			else
				error(['Could not find ' path ]);
			end
		end%}}}
		function md=loadmodel(org,string),% {{{

			%Get model path
			if ~ischar(string), error('argument provided is not a string'); end
			path=[org.repository '/' org.prefix string];

			%figure out if the model is there, otherwise, we have to use the default path supplied by user.
			if exist(path,'file') | exist([path '.mat'],'file'),
				md=loadmodel(path);
				return;
			end

			%If we are here, the model has not been found. Try trunk prefix if provided
			if ~isempty(org.trunkprefix),
				path2=[org.repository '/' org.trunkprefix string];
				if ~exist(path2,'file'),
					error(['Could find neither ' path ', nor ' path2]);
				else
					disp(['--> Branching ' org.prefix ' from trunk ' org.trunkprefix]);
					md=loadmodel(path2);
					return;
				end
			else
				error(['Could not find ' path ]);
			end
		end%}}}
		function loaddata(org,string),% {{{

			%Get model path
			if ~ischar(string), error('argument provided is not a string'); end
			path=[org.repository '/' org.prefix string];

			%figure out if the data is there, otherwise, we have to use the default path supplied by user.
			if exist(path,'file') | exist([path '.mat'],'file'),
				evalin('caller',['load -mat ' path]);
				return;
			end

			%If we are here, the data has not been found. Try trunk prefix if provided
			if ~isempty(org.trunkprefix),
				path2=[org.repository '/' org.trunkprefix string];
				if ~exist(path2,'file'),
					error(['Could find neither ' path ', nor ' path2]);
				else
					disp(['--> Branching ' org.prefix ' from trunk ' org.trunkprefix]);
					evalin('caller',['load -mat ' path2]);
					return;
				end
			else
				error(['Could not find ' path ]);
			end
		end%}}}
		function bool=perform(org,string) % {{{

			bool=false;

			%Some checks
			if ~ischar(string),                            error('Step provided should be a string'); end
			if ~strcmp(regexprep(string,'\s+',''),string), error('Step provided should not have any white space'); end
			if (org.currentstep>0 & ismember({string},{org.steps.string})) 
				error(['Step ' string ' already present. Change name']); 
			end

			%Add step
			org.steps(end+1).id=length(org.steps)+1;
			org.steps(end).string=string;
			org.currentstep=org.currentstep+1;

			%if requestedsteps = 0, print all steps in org 
			if any(org.requestedsteps==0),
				if org.currentstep==1,
					disp(sprintf('   prefix: %s',org.prefix));
				end
				disp(sprintf('   step #%2i : %s',org.steps(org.currentstep).id,org.steps(org.currentstep).string));
			end

			%Ok, now if currentstep is a member of steps, return true
			if ismember(org.currentstep,org.requestedsteps),
				disp(sprintf('\n   step #%i : %s\n',org.steps(org.currentstep).id,org.steps(org.currentstep).string));
				bool=true;
			end

			%But if download is requested, we are downloading and skipping the step: 
			if ismember(org.currentstep,org.requestedsteps) & org.download,
				%load the model if it exists, and download
				name=[org.repository '/' org.prefix org.steps(org.currentstep).string ];
				if exist(name,'file'),
					md=loadmodel(name); 
					if isa(md,'model'),
						if ~isempty(md.settings.upload_filename),
							disp(sprintf('   downloading model'));
							md=download(md);
							save(name,'md','-v7.3');
						end
					end
				end

				%reset bool to false, so we stick with only downloading
				bool=false;
			end

		end%}}}
		function savemodel(org,md) % {{{

			%check
			if (org.currentstep==0), error('Cannot save model because organizer (org) is empty! Make sure you did not skip any perform call'); end
			if (org.currentstep>length(org.steps)), error('Cannot save model because organizer (org) is not up to date!'); end

			name=[org.repository '/' org.prefix org.steps(org.currentstep).string ];
			disp(['saving model as: ' name]);

			%check that md is a model
			if ~isa(md,'model'), warning('second argument is not a model'); end
			if (org.currentstep>length(org.steps)), error(['organizer error message: element with id ' num2str(org.currentstep) ' not found']); end

			%save model
			save(name,'md','-v7.3');
		end%}}}
		function savedata(org,varargin) % {{{

			%check
			if (org.currentstep==0), error('Cannot save data because organizer (org) is empty! Make sure you did not skip any perform call'); end
			if (org.currentstep>length(org.steps)), error('Cannot save data because organizer (org) is not up to date!'); end

			name=[org.repository '/' org.prefix org.steps(org.currentstep).string ];
			disp(['saving data in: ' name]);

			%check that md is a model
			if (org.currentstep>length(org.steps)), error(['organizer error message: element with id ' num2str(org.currentstep) ' not found']); end

			%list of variable names: 
			variables='';
			for i=2:nargin, 
				variables=[variables ',' '''' inputname(i) ''''];
				eval([inputname(i) '= varargin{' num2str(i-1) '};']);
			end
			eval(['save(''' name '''' variables ',''-v7.3'');']);
		end%}}}
	end
end
