function [outputs, errorInfo, elapsed] = RunScript(parentFolder, scriptName, inputs, inputNames, outputNames)
% RunScript  Run a script from a folder.  parentFolder must be an 
% absolute path or empty.  When parentFolder is empty, MATLAB searchs 
% scriptName in its search path.
%
% When executing a script, inputs is a cell array whose elements are expanded
% into base workspace with names defined in inputNames.  outputs is a cell
% array of base workspace variables after the script has been called.  Only the
% variables named in outputNames will be returned in outputs.
%
% Returns in outputs, a cell array of output arguments.  If outputNames is
% empty, then outputs is an empty cell array.  The errorInfo output returns an
% error if the script produced an error or an empty cell array if there were no
% errors.  The errorInfo cell array contains 2 char vectors.  The first is the
% error message.  The second cell is the error message ID string.  The elapsed
% is a numeric array containing performance metrics.
%
% Copyright (c) 1984-2018, The MathWorks, Inc.
% All rights reserved.
% Redistribution and use in source and binary forms, with or without 
% modification, are permitted provided that the following conditions are 
% met:
% 1. Redistributions of source code must retain the above copyright notice,
%    this list of conditions and the following disclaimer.
% 2. Redistributions in binary form must reproduce the above copyright 
%    notice, this list of conditions and the following disclaimer in the
%    documentation and/or other materials provided with the distribution.
% 3. In all cases, the software is, and all modifications and derivatives 
%    of the software shall be, licensed to you solely for use in 
%    conjunction with MathWorks products and service offerings. 
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
% TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
% PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 
% OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
% EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
% PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
% LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    outputs = {};
    errorInfo = {};
    elapsed = zeros(1, 3);

    if isstring(parentFolder)
        parentFolder = char(parentFolder);
    end
    if isstring(scriptName)
        scriptName = char(scriptName);
    end

    try
        % If the input had a path, CD to that path if it exists
        if ~isempty(parentFolder)
            cd(parentFolder);
        end

        [outputs, elapsed] = localCallScript(scriptName, inputs, inputNames, outputNames);
    catch ME
        errorInfo{1} = ME.message;
        if strcmp(ME.identifier, 'NI:')
            errorInfo{2} = ME.identifier;
        elseif length(ME.stack) >= 3 && strcmp(ME.stack(end-2).name, 'create_variables__')
            errorInfo{2} = 'NI:SetVariable';
        elseif length(ME.stack) >= 3 && strcmp(ME.stack(end-2).name, 'gather_variables__')
            errorInfo{2} = 'NI:GetVariable';
        elseif length(ME.stack) >= 2 && strcmp(ME.stack(end-1).name, 'localCallScript')
            % Error from user file
            errorInfo{2} = ME.identifier;
        else
            errorInfo{2} = 'NI:Unexpected';
        end
    end
end

%Call a script
function [outputs__, elapsed__] = localCallScript(scriptName__, inputs__, inputNames__, outputNames__)
    elapsed__ = zeros(1, 3);
    t0 = tic;
    create_variables__(inputs__, inputNames__);
    elapsed__(1) = toc(t0);
    t0 = tic;
    evalin('base', scriptName__);
    elapsed__(2) = toc(t0);
    t0 = tic;
    outputs__ = gather_variables__(outputNames__);
    elapsed__(3) = toc(t0);
end

function create_variables__(vars, names)
% Push each variables into the base workspace
    for k = 1:length(names)
        vars{k} = niifm.UnescapeForCom(vars{k});
        assignin('base', names{k}, vars{k});
    end
end

function vars = gather_variables__(outputNames)
% Gather all the variables from the base workspace
    vars = cell(size(outputNames));
    for k = 1:length(outputNames)
        vars{k} = evalin('base', outputNames{k}); 
        vars{k} = niifm.EscapeForCom(vars{k});
    end
end

