%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
    properties (SetAccess=private) 
		 % {{{
		 currentstep   =0;
	 end
    properties (SetAccess=public) 
		 repository    ='./';
		 prefix        ='model.';
		 trunkprefix   ='';
		 steps         =[];
		 requestedsteps=[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 repository
			 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
		 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
		 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 neither find ' 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 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

			 %assign org back to calling workspace
			 assignin('caller',inputname(1),org);

		 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('third 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%}}}
	end
end
