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