function [expt] = constructExperiment(m, tF, u, tStop, events, nEvents, name, periodic, steadyState, firstEvent, ic)
% CONSTRUCTEXPERIMENT constructs a new experiment
%
% This function creates an experiment object. At a minimum this defines 
% the length of time for a simulation but can also be used to describe
% more complex behaviour such as the location and timing of events.
%
% expt = constructExperiment(m, tF) constructs a new experiment object that 
%   runs for tF time.  Use this for models that do not have inputs.
%
% expt = constructExperiment(m, tF, u) constructs a new experiment object that 
%   runs for tF time with constant input u. Use this for models that have
%   inputs. u should be the same length as u in your model.
% 
% expt = constructExperiment(m, tF, @u) constructs a new experiment object that 
%   runs for tF time with variable input u. Use this for models that have
%   inputs. u should be a function of time.
%
% expt = constructExperiment(m, tF, u, tStop) constructs a new 
%   experiment object that runs for tF with input u (variable or fixed) 
%   where the simulation should stop at times designated in the vector
%   tStop.  If tStop is empty, simulation ignores it.
% 
% expt = constructExperiment(m, tF, u, tStop, @eventFun) constructs a new 
%   experiment object that runs for tF with input u (variable or fixed) 
%   where the simulation should stop at events defined by @eventFun, and at
%   the times in vector tStop.  @eventFun should have the signature:
%
%       [val, isTerminal, direction] = eventFun(t,x,u)
%
%   See MATLAB's ode function help for more information on events
%   functions.
% 
% expt = constructExperiment(m, tF, u, tStop, @eventFun, nEvents) constructs a 
%   new experiment object that runs for tF with input u (variable or fixed) 
%   where the simulation should stop at events defined by @eventFun, and
%   should terminate integration after nEvents such events have occurred.
% 
% expt = constructExperiment(m, tF, u, tStop, @eventFun, nEvents, name) constructs a 
%   new experiment object that runs for tF with input u (variable or fixed) 
%   where the simulation should stop at events defined by @eventFun, and
%   should terminate integration after nEvents such events have occurred.
%   It also names the experiment with 'name', rather than the default name.
% 
% This work is licensed under the Creative Commons Attribution-Noncommercial-No Derivative 
% Works 3.0 United States License. To view a copy of this license, visit 
% http://creativecommons.org/licenses/by-nc-nd/3.0/us/ or send a letter to Creative Commons,
% 171 Second Street, Suite 300, San Francisco, California, 94105, USA.

% Clean up inputs
if nargin < 2
    tF = [];
    if nargin < 1
        m = [];
    end
end

% Special case: return empty structure array if inputs are numeric
if isnumeric(m)
    temp = struct('tF', [], 'update', [], 'u', [], 'tStop', [], 'events', [], 'nEvents', [], 'name', [], 'periodic', [], 'steadyState', [], 'firstEvent', [], 'ic', []);
    if isempty(m)
        m = 1;
    end
    if ~isscalar(m)
        varargout{1}(prod(m),1) = temp;
        expt = reshape(varargout{1}, m);
        return
    end
    if isempty(tF)
        tF = 1;
    end
    expt(m, tF) = temp;
    return
end

% Gut m
temp = m;
clear m
m.ic = temp.ic;
clear temp

% Setup Experiment Time
expt.tF     = tF;
expt.update = @update;

% Setup Inputs
if(nargin < 3)
    expt.u       = @(t)zeros(0,length(t));
    expt.tStop   = [];
    expt.events  = [];
    expt.nEvents = [];
else
    if isempty(u)
        expt.u   = @(t)zeros(0,length(t));
    elseif isnumeric(u)
        expt.u   = @(t) u*ones(1, length(t));
    else
        expt.u   = u;
    end
end

% set up stop times
if nargin<4
    expt.tStop   = [];
    expt.events  = [];
    expt.nEvents = [];
else
    expt.tStop   = vec(tStop);
end

% Setup Events
if(nargin < 5)
    expt.events  = [];
    expt.nEvents = [];
else
    expt.events  = events;
end

% Setup number of events
if(nargin < 6)
    expt.nEvents = [];
else
    expt.nEvents = nEvents;
end

% Setup number of events
if(nargin < 7)
    expt.name = 'Experiment';
else
    expt.name = name;
end

% Setup number of events
if(nargin < 8)
    expt.periodic = 0;
else
    expt.periodic = periodic;
end
if nargin < 9
    expt.steadyState = 0;
else
    expt.steadyState = steadyState;
end
if nargin < 10
    expt.firstEvent = 0;
else
    expt.firstEvent = firstEvent;
end

% Setup initial conditions
if nargin < 11
    expt.ic = m.ic;
else
    expt.ic = ic;
end

% End of function
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    function exptNew = update(mNew, icNew)
        % Clean up inputs
        if nargin < 2
            icNew = [];
        end
        
        if isempty(icNew)
            if ~isempty(expt.events)
                expt.events = events.update(mNew);
            end
            exptNew = constructExperiment(mNew, expt.tF, expt.u, expt.tStop, expt.events, expt.nEvents, expt.name, expt.periodic, expt.steadyState, expt.firstEvent, expt.ic);
        else
            if ~isempty(expt.events)
                expt.events = events.update(mNew);
            end
            exptNew = constructExperiment(mNew, expt.tF, expt.u, expt.tStop, expt.events, expt.nEvents, expt.name, expt.periodic, expt.steadyState, expt.firstEvent, icNew);
        end
    end
end