function [M, movie] = tSeriesMovie(vw, scans, anatFlag, varargin)
% [M movie] = tSeriesMovie(vw, scans, anatFlag, [options]):
%
% Make a movie of the selected tSeries, using the current view's
% settings, and play using mplay viewer. Intended as a replacement
% for makeTSeriesMovie.
%
% NOTE: The code auto-resizes the movie to fit the screen nicely. Although
% this is a benefit, it usually causes the lower line of the mplay tool to
% turn black; this line contains some useful information like the current
% frame and image size. To get this info back, press the second button 
% from the left ("truesize") in the movie window's toolbar. 
%
% vw: the view struct. Currently supports inplane and flat level views.
%
% scans: set of scans to view from current data type.
%
% anatFlag: if 1, will threshold the tSeries and superimpose
% the parts above threshold on top of the view's anat image.
% Defaults to 0.
%
% Returns M, a matrix of the tSeries, and movie, an object-
% oriented struct generated by mplay used for programmatic
% control of the movie. If anatFlag is 0, M is size
% x by y by nFrames, where x and y are the view's slice dims.
% If anatFlag is 1, M is a 4-D array of size x by y by 3 by nFrames,
% where each 3-D subvolume is a truecolor image.
%
% Options:  [case-insensitive]
%   slices, [val]:          set slice numbers (defaults to current slice).
%                           WARNING: >1 slice can be very memory-hungry
%   zoom, [val]:            set zoom area (as with axis command)
%   detrend, [val]:         set detrend flag (see detrendTSeries for vals)
%   convertToPct,[flag]:    if 1, will convert to % signal change.
%   applyHisto,[flag]:      flag to apply standard histogram criterion (on
%                           by default) in which the tails of the intensity
%                           histogram are clipped.
%   compareFrames,[frame]:  if nonzero, will make a truecolor move in which 
%                           the red channel is the tSeries, and the 
%                           blue-green channel is a reference frame --
%                           e.g., if 1, the first frame of the tSeries
%   meanThresh,[val]:       set threshold for raw functionals -- any parts
%                           of the tSeries below this value (normalized 
%                           from 0 to 1 --> min to max of tSeries) will 
%                           be zeroed.
%   funcClip,[val]:         set clip values for functionals, if
%                           superimposing tSeries on anatomicals 
%                           (i.e., anatFlag = 1). Values outside
%                           this range won't be overlaid. Default [0.2 1]
%   fps,[val]:              Set default frames per second of playback
%                           in the movie player.
%   saveAvi,[path]:         export the tSeries movie to an .AVI file
%                           in the specified path. If path is set to
%                           'dialog', will pop up a dialog to interactively
%                           set the target file. Note that because of
%                           codecs, exporting on Windows produces smaller
%                           files than on linux (at least in my
%                           experience). Uses movie2avi.
%
%
% More info on the mplay movie player for matlab:
%
% http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=3250&objectType=file
%
% Thanks Don Orofino!
%
% ras 12/04: wrote it.
% ras 03/05: added avi export.
% ras 11/06: added multiple slices option.
if notDefined('scans'),    scans = viewGet(vw, 'curScan'); end
if notDefined('anatFlag'), anatFlag = 0;                   end

%%%%% default params
slices = viewGet(vw, 'curSlice');
sDims  = viewGet(vw, 'sliceDims',scans(1));
if checkfields(vw,'ui','zoom')
    zoom = vw.ui.zoom;
    rsFactor = upSampleFactor(vw,scans(1));
    zoom(3:4) = zoom(3:4) ./ rsFactor(1);
    zoom(1:2) = zoom(1:2) ./ rsFactor(2);
    zoom = round(zoom);
    zoom(zoom==0) = 1;
else
    zoom = [1 sDims(2) 1 sDims(1)];
end
detrend         = 0;
convertToPct    = 0;
applyHisto      = 0; % use histogram-based thresholding like makeTSeriesMovie
compareFrame    = 0; % compare to a reference frame (if 0, don't compare)
meanThresh      = 0; % normalized raw intensity threshold
funcClip        = [0.2 1]; % clipping values for overlaying functionals, if anatFlag==1
autoCmap        = 1; % flag to use the full cmap range (if anatFlag==1)
movieFlag       = 1; % call the mplay GUI
fps             = 15;
saveAvi         = '';
viewType        = viewGet(vw,'viewType');
frames       = true; % true = use all frames from each scan; false = use 1st / last frame only

%%%%% parse the option flags
opts = unNestCell(varargin);  % to enable recursive passing of options
for i = 1:length(opts)
    if ischar(opts{i})
        switch lower(opts{i})
            case 'slices',          slices      = opts{i+1};
            case 'zoom',            zoom        = opts{i+1};
            case 'detrend',         detrend     = opts{i+1};
            case 'converttopct',    convertToPct= opts{i+1};
            case 'applyhisto',      applyHisto  = opts{i+1};
            case 'compareframes',   compareFrame= opts{i+1};
            case 'meanthresh',      meanThresh  = opts{i+1};
            case 'funcclip',        funcClip    = opts{i+1};
            case 'fps',             fps         = opts{i+1};
            case 'saveavi',         saveAvi     = opts{i+1};
            case 'frames',          frames      = opts{i+1};
            case 'autocmap',        autoCmap    = opts{i+1};
            case 'nomovie',         movieFlag   = 0;
        end
    end
end

%%%%% check view type
if isequal(viewType,'Flat')
    [M, movie] = flatLevelMovie(vw,scans,saveAvi);
    return
elseif isequal(viewType,'Volume') || isequal(viewType,'Gray')
    error('Volume/Gray support not provided yet.')
end

%%%%% init M intensity array
M = [];

%%%%% if >1 slices, run recursively
if length(slices) > 1
    [M, movie] = tSeriesMovieSlices(vw, scans, slices, anatFlag, opts);
    return
end

%%%%% get tSeries from selected scans
nFrames = 0;
hwait = mrvWaitbar(0,'Loading tSeries for movie...');
for scan = scans
    tSeries = loadtSeries(vw,scan,slices(1));
    if frames,  % get just the requested frames, throw out the rest
        % make sure we don't try to get more frames than exist (could
        % happen if the number of frames differs across scans)
        validframes = frames <= viewGet(vw, 'nframes', scan);
        % now restrict the tseries
        tSeries = tSeries(frames(validframes), :);  
    end
    
    % update frame count
    nFrames = nFrames + size(tSeries,1);    
    
    % detrend if selected
    if detrend ~= 0
        tSeries = detrendTSeries(tSeries, detrend, 20);
    end
	        
    % append to M
    M = [M; tSeries];
    
    mrvWaitbar(find(scans==scan)/length(scans),hwait);
end
close(hwait);

%%%%% apply histogram criterion
if applyHisto==1
    M = histoThresh(M);
end


% if a mean intensity threshold is set, mask out stuff 
% that doesn't have strong mean signal
if meanThresh > 0
    meanImg = mean(M,1);
    meanImg = rescale2(meanImg,[],[0 1000]);
    mask = (meanImg > 1000*meanThresh);
end

% convert to pct if selected
if convertToPct==1
    M = M ./ repmat(mean(M,1),[nFrames 1]);
end

% now apply mask if selected
if meanThresh > 0
    mask = repmat(mask,[nFrames 1]);
    M(mask==0) = min(M(:))-1;
end

%%%%% reshape and apply zoom
M = reshape(M', [sDims(1) sDims(2) nFrames]);
M = M(zoom(3):zoom(4),zoom(1):zoom(2),:);    

%%%%% if anat flag set, convert M to 4D truecolor x time array
if anatFlag==1 
	hwait = mrvWaitbar(0,'Superimposing over anat image...');
 
    % get cmap from map mode
    modeInfo    = viewGet(vw,'mapMode');
    numColors   = modeInfo.numColors;
    numGrays    = modeInfo.numGrays;
    cmap        = 255 .* modeInfo.cmap(numGrays+1:end,:);
    
    % make an overlay, indexing into the rows of cmap
    clipVals = min(M(:)) + funcClip .* (max(M(:))-min(M(:)));
    overlay  = uint8(rescale2(M, clipVals, [0 numColors]));
    if ~isequal(dataSize(vw,1),viewGet(vw,'Size'))
        
        % adjust zoom to be of view size again, not data size
        rsFactor  = upSampleFactor(vw,scans(1));
        zoom(3:4) = zoom(3:4) .* rsFactor(1);
        zoom(1:2) = zoom(1:2) .* rsFactor(2);
        
        % now up-sample the overlay to match the view resolution
        newSize = [zoom(4)-zoom(3) zoom(2)-zoom(1) nFrames];
        overlay = upSampleRep(overlay,newSize);
        
    end
    mrvWaitbar(1/4,hwait);
    
    % re-initialize M as a 4D array w/ the anat background
    anatImg = recomputeAnatImage(vw);
    anatImg = anatImg(zoom(3):zoom(4),zoom(1):zoom(2));
    anatImg = normalize(anatImg,0,255);
    M       = uint8(repmat(anatImg,[1 1 nFrames 3]));
    
    mrvWaitbar(1/2,hwait);
    
    % get the values for which the overlay > 0
    locs3D = find(overlay>0);
    [yy, xx, zz] = ind2sub(size(overlay),locs3D);
    
    % auto-scale the colormap
    if autoCmap==1
        overlay(locs3D) = rescale2(overlay(locs3D),[],[1 numColors]);
    end
        
    % for each color channel, plug in the appropriate
    % values from the cmap into the overlay locations
    for col = 1:3
        locs4D      = sub2ind(size(M),yy,xx,zz,repmat(col,size(yy)));
        M(locs4D)   = cmap(overlay(locs3D),col);        
        mrvWaitbar(1/2+col/6,hwait);
    end
    close(hwait);
    
    % mplay likes the 4D truecolor array rows x cols x 3 x frames,
    % so permute to this order:
    M = permute(M,[1 2 4 3]);
elseif compareFrame > 0
    % make an R G B movie in which the red channel is the tSeries,
    % and the G,B channels are the reference frame
    ref = repmat(M(:,:,compareFrame), [1 1 nFrames 2]);
    M = cat(4, M, ref);
    M = permute(M, [1 2 4 3]);
    M = uint8(rescale2(M,[],[0 2^8-1]));    
else
    % just reduce it to a uint8, for the purpose of rendering
    % (seems tricky to find a way around this bottleneck):
    M = uint8(rescale2(M,[],[0 2^8-1]));
end

%%%%% plug into mplayer
if movieFlag==1
    global mrSESSION dataTYPES;
    movie = implay(M,fps);
    
    ttltxt = sprintf('Movie: %s %s %s, Slice %i', ...
                        mrSESSION.sessionCode, ...
                        dataTYPES(vw.curDataType).name, ...
                        num2str(scans), slices(1));
    %set(get(movie,'hfig'),'Name',ttltxt);
    
    % play the movie
    %movie.play
end

%%%%% export to AVI if selected
if ~isempty(saveAvi)
    if isequal(saveAvi,'dialog')
        % put up a dialog to get path
        [f p] = uiputfile('*.avi','Save .avi movie as...');
        saveAvi = fullfile(p,f);
    end
    
    % check for .avi extension
    [~, p, ext] = fileparts(saveAvi);
    if ~isequal(lower(ext),'.avi')
        saveAvi = [saveAvi '.avi'];
    end
    
    % construct a matlab movie struct for the export
    mov = repmat(struct('cdata',[],'colormap',[]),1,nFrames);
    for frame = 1:nFrames
        if ndims(M)==4
            mov(frame).cdata = M(:,:,:,frame);
        else
            mov(frame).cdata = repmat(M(:,:,frame),[1 1 3]);
        end
    end
    
    % now export the movie
    if ispc
        codec = 'Indeo3';
    else
        codec = 'None';
    end
    movie2avi(mov,saveAvi,'Compression',codec,'Quality',100,'FPS',fps);
    fprintf('Exported movie to %s.\n',saveAvi);
end


return
% /-------------------------------------------------------------------/ %




% /-------------------------------------------------------------------/ %
function [M, movie] = tSeriesMovieSlices(vw, scans, slices, anatFlag, varargin)
% code to recursively call the main function (tSeriesMovie)
% for each slice, and assemble a montage across slices.
% Can be very memory-intensive.
M = []; movie = [];

%%%%% params
movieFlag = 1; % call the mplay GUI
fps = 15;
saveAvi = '';

% remove the 'slices' specification for recursion
opts = unNestCell(varargin);
% for i=1:length(opts), opts{i} = lower(opts{i}); end
ii = cellfind(opts, 'slices'); 
ok = setdiff(1:length(opts), [ii ii+1]);
opts = opts(ok);  

% parse movie options
for i = 1:length(opts)
    if ischar(opts{i})
        switch lower(opts{i})
            case 'fps', fps = opts{i+1};
            case 'saveavi',
				% if there's a flag to save an .AVI file, we only want to
				% do that saving here, not in the recursive call to
				% tSeriesMovie below. So, mark it here, but removeit from
				% the options list
				saveAvi = opts{i+1};
				opts{i} = []; opts{i+1} = [];
            case 'nomovie', movieFlag = 0;
        end
    end
end

% get movie data for each slice
hwait = mrvWaitbar(0, 'Creating Montage Across Slices...');
set(hwait, 'Position', get(hwait, 'Position')+[0 100 0 0]);

nSlices = length(slices);
for i = 1:nSlices
    slice{i} = tSeriesMovie(vw, scans, anatFlag, 'slices', slices(i), ...
                            'nomovie', opts);
    mrvWaitbar(.8 * (i/nSlices), hwait);
end


%%%%%% make montage
nRows = ceil(sqrt(nSlices));
nCols = ceil(nSlices/nRows);
if nSlices < nRows*nCols    % pad out extra images
    for i = nSlices+1:nRows*nCols
        slice{i} = zeros(size(slice{1}));
    end
end
slice = reshape(slice, [nCols nRows])';

mrvWaitbar(.95, hwait);

for r = 1:nRows
    row = [];
    for c = 1:nCols
        row = cat(2, row, slice{1,c});
    end

    % be memory-efficient: clear the row we just grabbed
    slice = slice(2:end,:);
    
    M = cat(1, M, row);
end

close(hwait);


%%%%% show movie if selected
if movieFlag==1
    global mrSESSION dataTYPES;
    movie = mplay(M,fps);
    ttltxt = sprintf( 'Movie: %s %s %s, Slices %s', ...
                       mrSESSION.sessionCode, ...
                       dataTYPES(vw.curDataType).name, ...
                       num2str(scans), num2str(slices) );
    set(get(movie,'hfig'), 'Name', ttltxt);
end

%%%%% export to AVI if selected
if ~isempty(saveAvi)
    if isequal(saveAvi,'dialog')
        % put up a dialog to get path
        [f p] = uiputfile('*.avi','Save .avi movie as...');
        saveAvi = fullfile(p,f);
    end
    
    % check for .avi extension
    [f p ext] = fileparts(saveAvi);
    if ~isequal(lower(ext),'.avi')
        saveAvi = [saveAvi '.avi'];
    end
    
    % construct a matlab movie struct for the export
    nFrames = size(M, ndims(M));
    mov = repmat( struct('cdata',[], 'colormap',[]), 1, nFrames );
    for frame = 1:nFrames
        if ndims(M)==4
            mov(frame).cdata = M(:,:,:,frame);
        else
            mov(frame).cdata = repmat(M(:,:,frame),[1 1 3]);
        end
	end
    
	% allow the movie path to refer to directories that have not yet been
	% created (like 'Movies/')
	ensureDirExists( fileparts(fullpath(saveAvi)) );
	
    % now export the movie
    if ispc
        codec = 'Indeo5';
    else
        codec = 'None';
    end
    movie2avi(mov, saveAvi, 'Compression', codec, 'Quality', 100, 'FPS', fps);
    fprintf('Exported movie to %s.\n', saveAvi);
end

%% lastly, play the movie if it exists
if movieFlag==1
    movie.play
end

return


