% arrow_sched - Instantiate arrow system to run on background timer and drop into REPL.
%
% arrow_sched ::
%    arrow({},_,_)  ~'arbitrary arrow with no inputs',
%    nonneg         ~'period of timer',
%    options {
%       sched_fig :: handle /0 ~'figure number for scheduler control UI';
%       draw      :: boolean /false ~'do drawnow after each iteration';
%       exec_mode :: string / 'fixedSpacing' ~'timer execution mode'
%    }
% -> {...}.
%
% This function provides a command line with access to a live
% system instantiated from the given arrow. The command line
% has the prompt 'with_sched>> ' to distinguish it from the normal
% Matlab prompt, but in other respects, is the same. The only
% commands that function differently are 'quit' and 'exit',
% which are trapped and behave as 'return' does, returning control
% to this function. Arbitrary values can be returned through to
% the called of arrow_repl using ret, eg
%   unit>> ret(get_state(),45);  
% returns two values: the current state of the system and the
% value 45.
%
% The environment contains the following variables of interest:
%    sys  :: arrow(A,B,S).          % the original arrow
%    unit :: unit(A,B,S).           % the live processing unit
%    sched :: struct.               % scheduler structure (see rsched)
%
% The following functions are also available:
%    get_state  :: () -> S.             % returns the state of the system
%    set_state  :: S -> action ().      % sets the state of the system
%    stop       :: () -> action ().     % stop processing in timer thread.
%    start      :: () -> action ().     % start processing in timer thread.
%    startat    :: time -> action ().   % start processing at given time (see nows).
%    set_period :: nonneg -> action (). % set period of timer (stops and restarts)
%
% While running, the background thread calls unit.process() on each
% iteration, so the system must have zero inputs. It can have any
% number of outputs--these are all discarded while running.

function varargout=arrow_sched(sys,period,varargin)
	opts=prefs('sched_fig',0,'draw',0,'exec_mode','fixedSpacing','start',0,varargin{:});
	[varargout{1:nargout}]=with_arrow(sys,@with_sched,{},opts,'keyboard',0);

	function varargout=with_sched(unit)
		if opts.draw, stepfn=@step_draw; else stepfn=@step; end

		returns={};
		uihandles=[];
		sched=rsched(stepfn,1,period,0,'onstart',@onstart,'onstop',@onstop,'defer',1);
		try
			set(sched.timer(),'ExecutionMode',opts.exec_mode);
			if opts.sched_fig>0
				figure(opts.sched_fig);
				uihandles=sched_ui(sched,12);
			end

			fprintf('\n');
			if opts.start, 
				fprintf('\nStarting automatically as requested.\n');
				start;
			end

			fprintf('\nEntering REPL. Type "return","exit", or "quit" to finish.\n');
			fprintf('The function ret(..) sets return values.\n\n');
			cont=true;
			while cont,
				str=input('with_sched>> ','s');
				if strcmp(str,'quit') break; end
				if strcmp(str,'exit') break; end
				if strcmp(str,'return') break; end
				try, eval(str)
				catch ex
					fprintf('\n%s\n',getReport(ex));
				end
			end
		catch ex
			if ~isempty(uihandles), delete(uihandles); end
			if sched.isrunning(), sched.stop(); end
			sched.dispose();
			rethrow(ex);
		end

		if ~isempty(uihandles), delete(uihandles); end
		if sched.isrunning(), sched.stop(); end
		sched.dispose();
		varargout=returns;

		function step1
			if sched.isrunning(), error('Already running');
			else
				unit.starting();
				unit.process();
				unit.stopping();
			end
		end

		function set_period(d)
			if sched.isrunning(), stop; set(sched.timer(),'Period',d); start;
			else set(sched.timer(),'Period',d); end
		end

		function startat(t), sched.startat(t); end
		function start, sched.start(); end
		function stop, sched.stop(); end
		function set_state(s), unit.set_state(s); end
		function s=get_state, s=unit.get_state(); end
		function onstart(s,t,t0), unit.starting(); end
		function onstop(s,t), unit.stopping(); end
		function ret(varargin), returns=varargin; cont=false; end
		function run_for(its)
			set(sched.timer(),'TasksToExecute',its);
			start;
		end

		function [s,t0]=step(s,per,t0,dt), 
			unit.process(); 
		end

		function [s,t0]=step_draw(s,per,t0,dt)
			unit.process();
			drawnow;
		end
	end
end
