% CALLERISMETHOD(varargin) checks if a method was called by another LTPDA method.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DESCRIPTION: CALLERISMETHOD(varargin) checks if the method it is inserted in,
% was called by another LTPDA method
%
% CALL:                         callerIsMethod = utils.helper.callerIsMethod()
%                  [callerIsMethod, className] = utils.helper.callerIsMethod()
%      [callerIsMethod, className, methodName] = utils.helper.callerIsMethod()
%
% The name of the caller function is retrieved from a call to dbstack
%
% The name of the higher level caller function, if any is tested against the structure
% /../../classes/@class_name/method_name.m
%
% If the higher level caller is found to be a method of LTPDA classes, out is TRUE
% If the caller is found not to be a method of LTPDA classes, out is FALSE
%
% Optional outputs:
%     className     the name of the class the caller methods is associated
%     methodName    the name of the caller method is associated
%
% HISTORY: 09-02-11 M Hueller
%            Creation
%
% VERSION: $Id: callerIsMethod.m,v 1.16 2011/04/09 06:58:47 hewitson Exp $
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function varargout = callerIsMethod(varargin)

  % This is just some thinking about make a scheme that can override the
  % behaviour of callerIsMethod. The idea is to create a variable called
  % 'callerIsMethod' in the calling function and pass it in here. We would
  % have to pass varargin and inputnames down the chain always. It almost
  % works but will be too much work for the moment.  
  %   if nargin == 2
  %
  %     inputs = varargin{1};
  %     names = varargin{2};
  %
  %     idx = strcmpi('callerIsMethod', names);
  %     if any(idx)
  %       varargout{1} = inputs{idx};
  %       return
  %     end
  %   end
  
  persistent exceptions;
  
  if isempty(exceptions)
    exceptions = {'compute', 'rebuild', 'collect_values', 'generic_getInfo', 'executeCommands'};
  end
  
  stack = dbstack('-completenames');
  index = 2;
  
  if size (stack, 1) < index
    error('### This utility can only be used inside other functions!');
  end
  
  if size (stack, 1) == index
    
    % This is the highest level function call.
    % The caller is not a method
    callerIsMethod  = false;
    className       = [];
    methodName      = [];
    
  else
    % This is not the highest level function call.
    
    % Serching for class methods assuming the folder/files structure is:
    % /../../classes/@class_name/method_name.m
        
    % In the case that we are inside a nested function, we need to climb up
    % until we get out of the calling file.
    firstFile = stack(index).file;
    firstName = stack(index).name;
    
    while index < length(stack) && strcmp(firstFile, stack(index).file) && ~strcmp(firstName, stack(index+1).name)
      index = index+1;
    end
    
    % capture method name
    methodName = stack(index).name;
      
    if index == length(stack) && length(stack)>3
      callerIsMethod = false;
      className = '';
    else
      % filesep is quite slow, so we cache the value and use it three times.
      % The \ character is also a command in regexp, so we need to go for a trick
      if strncmp(computer, 'PC', 2)
        fs = '\\';
      else
        fs = '/';
      end
      
      % capture class name
      tokenStr  = regexp(stack(index).file, ['.*' fs '@(\w+)' fs '\w+.m$'], 'tokens');

      % *********
      % At the moment this bit of code is a bad thing because models which
      % create objects in their default plists (AOs for example) can't be
      % rebuilt because the objects have no history. An example of this is
      % smodel_model_psd_feeps.
      % *********
      
%       % If we are being called deep inside a built-in model constructor,
%       % then the stack will contain fromModel and we are called from a
%       % method.
%       if utils.helper.ismember('fromModel', {stack(:).name})
%         callerIsMethod = true;
%         className = 'ltpda_uo';
%         methodName = 'fromModel';
%       
%       % If the method is a unit test (test_*) then we add history
%       else


      if strncmp(methodName, 'test_', 5)
        callerIsMethod = false;
        if ~isempty(tokenStr)
          className = tokenStr{1}{1};
        end
        
      % if we get a match and if the caller is not in the exception list
      % then we were called by a method.
      elseif ~isempty(tokenStr) && ~any(strcmp(stack(index).name, exceptions))
        callerIsMethod = true;
        className = tokenStr{1}{1};
      else
        callerIsMethod = false;
        className = '';
      end
    end
    
  end
  
  % Assigning outputs
  switch nargout
    case 0
    case 1
      varargout{1} = callerIsMethod;
    case 2
      varargout{1} = callerIsMethod;
      varargout{2} = className;
    case 3
      varargout{1} = callerIsMethod;
      varargout{2} = className;
      varargout{3} = methodName;
    otherwise
      error('### Incorrect number of outputs');
  end
end
