function Sched=iterate_timed(nextfn,X0,T,varargin)
% iterate_timed - Iterate function under control of timer
%
% iterate_timed :: 
%    (A=>A)          ~'state transformer action',
%    A               ~'initial state',
%    real            ~'timer between updates in seconds'
%    options {
%       drawnow   :: {0,1} /0 ~'call drawnow after each iteration';
%       busy_mode :: {'queue','drop'} /'queue'  ~'See TIMER';
%       exec_mode :: {'fixedRate','fixedDelay','fixedSpacing'} /'fixedRate' ~'See TIMER';
%       its       :: natural / inf ~'iteration limit';
%       onstart   :: (A=>void)/ @nop ~'do this when timer starts';
%       onstop    :: (A=>void)/ @nop ~'do this when timer stops';
%       onfinish  :: (A=>void)/ @nop ~'do this when iterator terminates';
%       pre       :: (A=>void)/ @nop ~'do before each iteration';
%       post      :: (A=>void)/ @nop ~'do after each iteration';
%       defer     :: bool / 0      ~'if true then don't start the timer';
% -> timer, (A=>void) ~'function to seek to given state'.
%
% NB: Unlike ITERATE, this does NOT respect the id, save, and recover properties.
% Neither does it respect the OPTPAUSE property 'pause'.

	opts=options('its',inf,'drawnow',0, 'quiet', 0, 'defer', 0, ...
		'busy_mode','queue','exec_mode','fixedRate', ...
		'onfinish',@nop,'onstart',@nop,'onstop',@nop,varargin{:});

	it={nextfn,X0}; 
	if isfield(opts,'pre') || isfield(opts,'post')
		it=bracket_it(it, getparam(opts,'pre',@nop), getparam(opts,'post',@nop));
	end
	if opts.drawnow, it=drawnow_it(it); end
	stfn=it{1};
	
	Sched=rsched(@action,it{2},T,nows,opts,'defer',1,...
			'onstart',@onstart,'onstop',@onstop,'onfinish',@onfinish);
	if ~opts.defer, Sched.start(); end

	function [err,s]=action(t_sched,dt,s), err=nows-t_sched; s=stfn(s); end
	function onstart(X,t_sched,t_actual), status('starting',X,t_actual); opts.onstart(X); end
	function onstop(X,t_actual), status('stopping',X,t_actual); opts.onstop(X); end
	function onfinish(t_actual), status('finishing',[],t_actual); opts.onfinish(); end
	function status(msg,x,t),
		if ~opts.quiet
			fprintf('| %s at %s with %s\n',msg,mat2str(t),tostring(x)); 
		end
	end
end

function it=drawnow_it(it), 
	f=it{1}; it{1}=@fdraw;
	function x=fdraw(x), x=f(x); drawnow; end
end

