function [m,ok] = leaf_savemodel( m, varargin )
%[m,ok] = leaf_savemodel( m, modelname, projectdir, ... )
%   Save the model to a model directory.
%
%   MODELNAME is the name of the model folder.  This must not be a full
%   path name, just the base name of the folder itself.  It will be looked
%   for in the folder PROJECTDIR, if specified, otherwise in the parent
%   directory of m, if any, otherwise the current directory.
%
%   If MODELNAME is not specified or empty, the user will be prompted for a
%   name using the standard file dialog.  
%
%   The model directory will be created if it does not exist.
%
%   If the model is being saved into its own model directory:
%       If it is in the initial state (i.e. no simulation steps have been
%       performed, and the initialisation function has not been called) then
%       it is saved into the file MODELNAME.mat.
%       If it is in a later state, it will be saved to MODELNAME_Snnnn.mat,
%       where nnnn is the current simulation time as a floating point
%       number with the decimal point replaced by a 'd'.
%
%   If the model is being saved into a new directory:
%       The current state will be saved as an initial state or a later
%       stage file as above.
%       If the current state is not the initial state, then the initial
%       state will be copied across.  Furthermore, the initial state of the
%       new project will be loaded.
%       The interaction function and notes file will be copied, if they
%       exist.  If the notes file exists, the new notes file will also be
%       opened in the editor.
%       Stage files, movies, and snapshots are NOT copied across.
%
%   If for any reason the model cannot be saved, OK will be false.
%
%   Options:
%       new:   If true, the mesh will be saved as the initial state of a
%              project, even if it is not the initial state of the current
%              simulation.  The default is false.
%       strip:    If true, as many fields as possible of the mesh will be
%              deleted before saving.  They will be reconstructed as far as
%              possible when the mesh is loaded.  The only information that
%              is lost is the residual strains and effective growth tensor
%              from the last iteration.  The default is false.
%       static: If true, the static file of the project will
%               also be written.  If false, not.  The default is the value
%               of ~m.globalDynamicProps.staticreadonly.
%
%   Equivalent GUI operation: the "Save As..." button prompts for a
%   directory to save a new project to; the "Save" button saves the current
%   state to its own model directory.  The "strip" option can be toggled
%   with the "Misc/Strip Saved Meshes" menu command.
%
%   Topics: Project management.

    if isempty(m), return; end
    setGlobals();
    global gFULLSTATICFIELDS
    oldprojectdir = m.globalProps.projectdir;
    oldmodelname = m.globalProps.modelname;
    oldmodeldir = fullfile( oldprojectdir, oldmodelname );
    
    s = struct();
    if length(varargin) < 1
        newmodelname = '';
    else
        newmodelname = varargin{1};
    end
    if length(varargin) < 2
        foldername = oldprojectdir;
        asNew = false;
    else
        foldername = varargin{2};
        if isGFtboxProjectDir( foldername )
            foldername = fileparts( foldername );
        end
        [s,ok] = safemakestruct( mfilename(), {varargin{3:end}} );
        if ~ok, return; end
        s = defaultfields( s, 'new', false, 'strip', false, ...
                'static', ~m.globalDynamicProps.staticreadonly );
        ok = checkcommandargs( mfilename(), s, 'exact', ...
                'new', 'strip', 'static' );
        if ~ok, return; end
        asNew = s.new;
    end
    
    ok = false;
    % If no model name, ask for one.
    if isempty( newmodelname )
        x = performRSSSdialogFromFile( ...
                findGFtboxFile( 'guilayouts/projectname.txt' ) );
        if isempty(x)
            return;
        end
        newmodelname = [ x.prefix, x.basename, x.suffix ];
        while true
            newprojectdir = uigetdir( foldername, 'Create model in directory:' );
            if newprojectdir==0
                return;
            end
            if isGFtboxProjectDir( newprojectdir )
                queryDialog( 1, 'Invalid directory', ...
                    'You cannot create a project inside another project directory.' );
                continue;
            end
            newmodeldir = fullfile( newprojectdir, newmodelname );
            if strcmpi( oldmodeldir, newmodeldir )
                queryDialog( 1, 'Invalid directory', ...
                    'You cannot save a model onto itself.' );
            elseif exist( newmodeldir, 'dir' )
                % ask if sure
                x = queryDialog( 2, 'Directory exists', ...
                    'The directory already exists, overwrite its contents?' );
                if x==1
                    break;
                end
            elseif exist( newmodeldir, 'file' )
                queryDialog( 1, 'Invalid directory', ...
                    'There is already a file of that name.' );
            else
                break;
            end
        end
      % [newprojectdir,newmodelname] = fileparts(newmodeldir);
    else
        newprojectdir = fullpath( foldername );
        newmodeldir = fullfile( newprojectdir, newmodelname );
    end

    saveasbase = asNew || (m.globalProps.allowsave && (m.globalDynamicProps.currentIter==0));
    if ~saveasbase
        stagesuffix = makestagesuffixf( m.globalDynamicProps.currenttime );
        m.stagetimes = addStages( m.stagetimes, m.globalDynamicProps.currenttime );
        saveStaticPart( m );
    end
    newProject = ~strcmpi( getModelDir( m ), newmodeldir );
    if newProject
        oldnotesname = makeNotesName( m );
        if ~exist( oldnotesname, 'file' )
            oldnotesname = '';
        end
    else
        oldnotesname = '';
    end

    if isfield(m.globalProps,'RecordMeshes') ...
            && m.globalProps.RecordMeshes.flag ...
            && m.globalProps.RecordMeshes.saveframe
%         m.globalProps.RecordMeshes.saveframe=false;   
        % we are actually saving this copy as part of an iModel movie
        if ~exist('stagesuffix')
            stagesuffix = makestagesuffixf( m.globalDynamicProps.currenttime );
        end
        modelfilename = [ newmodelname, stagesuffix, '.mat' ];
        % ensure that there is a subdirectory in runs ready for these
        % frames
        if (~isempty(m.globalProps.mov)) && isfield( m.globalProps.mov, 'VideoName' )
            subdname=m.globalProps.mov.VideoName;
            newmodeldir=fullfile(newmodeldir,'runs',subdname(1:end-4));
            if ~(exist(newmodeldir,'file'))
                mkdir(newmodeldir);
            end
        end
    else
        if saveasbase
            modelfilename = [ newmodelname, '.mat' ];
        else
            modelfilename = [ newmodelname, stagesuffix, '.mat' ];
            saved_laststagesuffix = m.globalDynamicProps.laststagesuffix;
            m.globalDynamicProps.laststagesuffix = stagesuffix;
        end
        if ~exist(newprojectdir,'dir')
            complain( '%s: Parent folder %s does not exist.  Model not saved.\n', ...
                mfilename(), newprojectdir );
            return;
        end
        if ~exist(newmodeldir,'dir')
            mkdir(newmodeldir);
        end

        % If it has an interaction function, save it to an M file.
        if ~isempty(m.globalProps.mgen_interactionName)
            newIFname = makeIFname( newmodelname );
            m = rewriteInteractionSkeleton( m, newIFname, newmodeldir, mfilename() );
        end
        % If there is a notes file, copy it across and open it in the editor.
        if ~isempty( oldnotesname )
            newnotesname = makeNotesName( m );
            if ~strcmpi( oldnotesname, newnotesname )
                fprintf( 1, '%s: copying notes file %s\n    to %s\n', ...
                    mfilename(), oldnotesname, newnotesname );
                [ok,msg,msgid] = copyfile( oldnotesname, newnotesname );
                if ok
                    try
                        edit( newnotesname )
                    catch
                        complain( 'New notes file %s could not be edited.\n', ...
                            newnotesname );
                    end
                else
                    complain( 'Could not copy old notes file %s\n to %s:\n    %s (%s).\n', ...
                        oldnotesname, newnotesname, msg, msgid );
                end
            end
        end
        % If there is a thumbnail file, copy it across.
        if ~isempty( oldmodeldir )
            oldthumbname = fullfile( oldmodeldir, 'GPT_thumbnail.png' );
            if exist( oldthumbname, 'file' )
                newthumbname = fullfile( newmodeldir, 'GPT_thumbnail.png' );
                if ~strcmpi( oldthumbname, newthumbname )
                    fprintf( 1, '%s: copying thumbnail file %s\n    to %s\n', ...
                        mfilename(), oldthumbname, newthumbname );
                    [ok,msg,msgid] = copyfile( oldthumbname, newthumbname );
                    if ~ok
                        complain( 'Could not copy thumbnail file %s\n to %s:\n    %s (%s).\n', ...
                            oldthumbname, newthumbname, msg, msgid );
                    end
                end
            end
        end
    end

    % When saving as a new project, adjust a few fields of the leaf before
    % saving it.  Record their values in case the save fails and we have to
    % restore the previous state.
    if asNew
        replacePropsOnFailure = struct( 'projectdir', newprojectdir, ...
                                        'modelname', newmodelname, ...
                                        'readonly', false, ...
                                        'currentIter', 0, ...
                                        'maxIters', 0 );
        [m.globalProps,replacePropsOnFailure] = ...
            saveFields( m.globalProps, replacePropsOnFailure );
    else
        replacePropsOnFailure = struct();
    end
    
    % Make temporary copy of parameters that should be cleared before
    % saving and restored after:
    %   Saved models should not contain any plot handles.
    %   Saved models should not be in the middle of recording movies.
    %   Models saved as start states should have the allowsave flag set.
    saved_plothandles = m.plothandles;
    m.plothandles = struct();
    saved_pictures = m.pictures;
    saved_callbacks = struct();
    if is_function_handle( m.globalProps.bioApresplitproc )
        saved_callbacks.bioApresplitproc = m.globalProps.bioApresplitproc;
        m.globalProps.bioApresplitproc = [];
    end
    if is_function_handle( m.globalProps.bioApostsplitproc )
        saved_callbacks.bioApostsplitproc = m.globalProps.bioApostsplitproc;
        m.globalProps.bioApostsplitproc = [];
    end
    if is_function_handle( m.plotdefaults.userplotproc )
        saved_callbacks.userplotproc = m.plotdefaults.userplotproc;
        m.plotdefaults.userplotproc = [];
    end
    m.pictures = [];
    
    replaceProps = struct( 'mov', [], ...
                           'makemovie', 0, ...
                           'moviefile', '', ...
                           'allowsave', saveasbase );
    [m.globalProps,replaceProps] = saveFields( m.globalProps, replaceProps );
    
    % Save the leaf into a MAT file.
    success = true;
    savefilename = fullfile( newmodeldir, modelfilename );
    try
        fprintf( 1, '%s: Saving current state to %s in %s\n', ...
            mfilename(), modelfilename, newmodeldir );
        if s.strip
            m1 = m;
            m = stripmesh(m);
        end
        save( savefilename, 'm' );
        if s.strip
            m = m1;
            clear m1;
        end
        ok = true;
    catch
        success = false;
        le = lasterror();
        warning(le.identifier, '%s', le.message);
        fprintf( 1, '%s: Could not write model file %s to %s.\nModel not saved.\n', ...
            mfilename, modelfilename, newmodeldir );
    end
    if success && s.static
        modelstaticfilename = staticBaseName( newmodelname );
        savestaticfilename = fullfile( newmodeldir, modelstaticfilename );
        mstatic = splitstruct( m, gFULLSTATICFIELDS );
        fprintf( 1, '%s: Saving static state to %s in %s\n', ...
            mfilename(), modelstaticfilename, newmodeldir );
        try
            save( savestaticfilename, '-struct', 'mstatic' );
        catch
            success = false;
            le = lasterror();
            warning(le.identifier, '%s', le.message);
            fprintf( 1, '%s: Could not write model static file %s to %s.\nModel not saved.\n', ...
                mfilename, modelstaticfilename, newmodeldir );
        end
    end
    
    % For a new project, copy other project files across.
    % We determine if it is a new project by comparing the full path names
    % of the old and new.  Ideally, we should compare the actual identities
    % of the directories: on Unix we could compare inodes, but I don't know
    % if Windows has anything similar.
    isnewproject = ~strcmp( ...
        fullfile( oldprojectdir, oldmodelname ), ...
        fullfile( newprojectdir, newmodelname ) );
    wasoldproject = ~isempty( oldprojectdir );
    if isnewproject && wasoldproject
        % Saving an existing project as a new project.
        % If the current state is not the initial state, copy the initial
        % state across.
        % Do not copy any existing stage files.
        fprintf( 1, '%s: Copying old saved states from %s to %s:\n', ...
            mfilename(), oldmodelname, newmodelname );
        if ~saveasbase
            oldinitialfile = fullfile( oldmodeldir, [ oldmodelname, '.mat' ] );
            newinitialfile = fullfile( newmodeldir, [ newmodelname, '.mat' ] );
            copyfile( oldinitialfile, newinitialfile );
        end
        % Copy over all files and folders that are not named with the old
        % project name and do not match a certain list of patterns.
        copyFilesAndFolders( oldmodeldir, newmodeldir, [], ...
            { ['^' oldmodelname], ...
               '^\.', ...
               '^runs$', ...
               '^movies$', ...
               '^snapshots$', ...
               '\~$', ...
               '\.asv$', ...
               'BAK\.m$', ...
               '\.tmp$'
            } );
    end

    % Restore parameters.
    m.globalProps = setFromStruct( m.globalProps, replaceProps );
    if ok
        m.globalProps.modelname = newmodelname;
        m.globalProps.projectdir = newprojectdir;
    end
    
    m.plothandles = saved_plothandles;
    m.pictures = saved_pictures;
    if asNew && ~success
        m.globalProps = setFromStruct( m.globalProps, replacePropsOnFailure );
    end
    if (~saveasbase) && (~success)
        m.globalDynamicProps.laststagesuffix = saved_laststagesuffix;
    end
    if isfield( saved_callbacks, 'bioApresplitproc' )
        m.globalProps.bioApresplitproc = saved_callbacks.bioApresplitproc;
    end
    if isfield( saved_callbacks, 'bioApostsplitproc' )
        m.globalProps.bioApostsplitproc = saved_callbacks.bioApostsplitproc;
    end
    if isfield( saved_callbacks, 'userplotproc' )
        m.plotdefaults.userplotproc = saved_callbacks.userplotproc;
    end

%     if ~(isfield(m.globalProps,'RecordMeshes') && m.globalProps.RecordMeshes.saveframe)
    if isfield(m.globalProps,'RecordMeshes') ...
            && m.globalProps.RecordMeshes.flag ...
            && m.globalProps.RecordMeshes.saveframe
        m.globalProps.RecordMeshes.saveframe=false;   
    else
        % Save a snapshot.
        if success
            if saveasbase
                snapshotname = 'Initial.png';
            else
                snapshotname = [ 'Stage', stagesuffix, '.png' ];
            end
            m = leaf_snapshot( m, snapshotname, 'newfile', 0 );
            if isinteractive(m)
                hh = guidata( m.pictures(1) );
                remakeStageMenu( hh );
            end
        end
    end
end

