function [paths,files] = dsLocateModelFiles(input)
%dsLocateModelFiles - locate mechanism files associated with DynaSim specifications.
%
% Usage:
%   [paths,files]=dsLocateModelFiles(input)
%
% Input: DynaSim specification or model structure or string or cell array of
%        strings listing mechanism names or files.
%
% Outputs:
%   - paths: unique paths to mechanism files
%   - files: full names of files containing mechanism sub-models
%
% See also (used by): dsParseModelEquations, dsCheckHostPaths, dsCreateBatch
% 
% Author: Jason Sherfey, PhD <jssherfey@gmail.com>
% Copyright (C) 2016 Jason Sherfey, Boston University, USA

% First looks for absolute path , then in current dir, then ds models dir,
% then in ds models sub dirs, then in full matlab path. First looks for string, 
% then extensions .eqns, .mech, .txt, and .m (in that order).

% extract list of mechanisms from input
if ischar(input)
  % convert to cell array of strings
  mechanism_list={input};
elseif iscellstr(input)
  mechanism_list=input;
elseif isstruct(input)
  if isfield(input,'specification')
    % extract specification from DynaSim model structure
    input=input.specification;
  elseif isfield(input,'model') && isfield(input.model,'specification') 
    % extract specification from DynaSim data structure
    input=input.model.specification;
  elseif isfield(input,'base_model') && isfield(input.base_model,'specification') 
    % extract specification from DynaSim studyinfo structure
    input=input.base_model.specification;
  else
    % this is probably a specification structure
  end
  
  mechanism_list={};
  if isfield(input,'populations') && isstruct(input.populations)
    % extract mechanism_list from populations in DynaSim specification structure
    
    m={};
    for i=1:length(input.populations)
      for j=1:length(input.populations(i).mechanism_list)
        name=input.populations(i).mechanism_list{j};
        if ~isfield(input.populations,'mechanisms') ...
            || isempty(input.populations(i).mechanisms) ...
            || ~ismember(name,{input.populations(i).mechanisms.name})
          m=cat(2,name,m);
        end
      end
    end
    
    if ~isempty(m)
      if iscell(m{1})
        m=unique_wrapper([m{:}],'stable');
      else
        m=unique_wrapper(m,'stable');
      end
      mechanism_list=cat(2,mechanism_list,m);
    end
    
    % add equations to mechanism_list in case it contains a .eqns file
    for i=1:length(input.populations)
      if ~isempty(input.populations(i).equations)
        mechanism_list{end+1}=input.populations(i).equations;
      end
    end
  end
  
  if isfield(input,'connections') && isstruct(input.connections)
    % extract mechanism_list connections in DynaSim specification structure
    m={};
    for i=1:length(input.connections)
      for j=1:length(input.connections(i).mechanism_list)
        name=input.connections(i).mechanism_list{j};
        if ~isfield(input.connections,'mechanisms') ...
            || isempty(input.connections(i).mechanisms) ...
            || ~ismember(name,{input.connections(i).mechanisms.name})
          m=cat(2,name,m);
        end
      end
    end
%     m={input.connections.mechanism_list};
    if ~isempty(m)
      if iscell(m{1})
        m=unique_wrapper([m{:}],'stable');
      else
        m=unique_wrapper(m,'stable');
      end
      mechanism_list=cat(2,mechanism_list,m);
    end        
  end
  
  % remove mechanisms present in specification structure
  if ~isempty(mechanism_list) && isfield(input,'mechanisms') && isfield(input.mechanisms,'name')
    mech_names=regexp(mechanism_list,'^[^@]+','match');
    mechanism_list=mechanism_list(~ismember([mech_names{:}],{input.mechanisms.name}));
  end    
end

% remove @ pointers from mechanism identifiers
if any(~cellfun(@isempty,regexp(mechanism_list,'@','once')))
  mechanism_list=regexp(mechanism_list,'^([^@]+)@?','tokens','once');
  mechanism_list=[mechanism_list{:}];
end

% exclude elements with non-word characters (these are not file names)
keep = cellfun(@isempty,regexp(mechanism_list,'[^\w\.\-/]'));
mechanism_list = mechanism_list(keep);

% search in dynasim toolbox model directory
dynasim_path = dsGetRootPath();
model_dir = fullfile(dynasim_path,'models'); % models dir is at root level
search_paths = regexp(genpath(model_dir),pathsep,'split'); % look in all dynasim directories

% add current directory
search_paths=cat(2,pwd,search_paths); % look in current directory first

% exclude .git directories
keep = cellfun(@isempty,regexp(search_paths,'.git'));
search_paths = search_paths(keep);

% remove empty paths
keep = ~cellfun(@isempty,search_paths);
search_paths = search_paths(keep);

% % add single empty path (in case mech given as absolute path)
% search_paths = cat(2,{''},search_paths);

% get num paths and increment 1 for when searching without specific path
num_paths=length(search_paths)+1;

% locate mechanism files
files={};
if iscellstr(mechanism_list)
  for f = 1:length(mechanism_list) % loop over mechanisms
    for s = 1:num_paths
      % determine name of mechanism file (assuming recommended extensions)
      if s == num_paths
        % if not found in paths, just use the mech string as given
        mech = mechanism_list{f};
      else
        % prepend current search path to mech string
        mech = fullfile(search_paths{s}, mechanism_list{f});
      end
      
      % see if any extension matches current mech string
      if exist(mech,'file')
        file = mech;
      elseif exist([mech '.eqns'],'file')
        file = [mech '.eqns'];
      elseif exist([mech '.mech'],'file')
        file = [mech '.mech'];
      elseif exist([mech '.txt'],'file')
        file = [mech '.txt'];
      elseif exist([mech '.m'],'file')
        file = [mech '.m'];
      else
        file = '';
      end
      
      % use 'which' to get full filename of file in Matlab path
      file = which(file);
      
      if s == num_paths && isempty(file)
        % looked through all paths at this point
        % if still empty, then wrong name or not on path
        warning('Could not find mechanism ''%s'' on path (with standard extensions).', mech);
      end
      
      if ~isempty(file)
        % add mech filepath to files cell array
        files{end+1}=file;
        
        % stop searching through paths for mech
        break;
      end
    end
  end
end

% extract unique paths to mechanism files
paths = unique_wrapper(cellfun(@fileparts2,files,'uni',0),'stable');