classdef MLArrayViewerModel < handle
    % This class provides workspace functionaly.
    
    %   Based on work by Murad Abu-Khalaf , July 30, 2010
    %   Copyright 2011 The MathWorks, Inc.
    
    properties (Access = private)
        %  ID - unique id for isequal
        ID
        data
        expression
        workspaceID
    end
    
    methods (Access = public)
        
        % Constructor
        function this = MLArrayViewerModel(varargin)
        % Setup a unique ID
        this.ID = now;
        this.data = struct();
        this.expression = '';
        this.workspaceID = workspacefunc('getworkspaceid',this);
        end
        
        % Equality test
        function out = isequal(obj, in)
        out = builtin('isequal',obj,in) && isequal(obj.ID, in.ID);
        end
        
        % Disp nicely
        function disp(this)
            variables = fieldnames(this.data);
            if isempty(variables)
                builtin('disp', ...
                    sprintf('\t%s object with no variables.\n', class(this)))
            else
                variablesString = '';
                for i = 1:length(variables)
                    variablesString = [variablesString variables{i} ' ']; %#ok<AGROW>
                end
                builtin('disp', ...
                    sprintf('\t%s object with variables: %s \n', ...
                    class(this), variablesString))
            end
        end
        
        function id = getWorkspaceID(this)
            id = this.workspaceID;
        end
        
        function assignin(this, name, value)
        % ASSIGNIN  Assigns a variable in the workspace.
        %
        %   ASSIGNIN(OBJ,VARNAME, VARVALUE) assigns the value VARVALUE
        %   to the variable VARNAME and stores it
        this.data.(name) = value;
        end
        
        function varargout = evalin(this, expression)
        % EVALIN  Evaluate expression in the workspace.
        %
        %   EVALIN(OBJ,'expression') evaluates 'expression' in the
        %   context of theis object.
        %
        this.expression = expression;
        clear expression
        
        % assign variables from data into this function's workspace
        compiler.internal.MLArrayViewerModel.assigner(this.data);
        compiler.internal.MLArrayViewerModel.currentThis(this);
        clear this
        
        try
            if (builtin('nargout') == 0)
                eval(compiler.internal.MLArrayViewerModel.currentThis().expression);
            else
                varargout{1:nargout} = eval(compiler.internal.MLArrayViewerModel.currentThis().expression);
            end
        catch e
            rethrow(e)
        end
        
        whoList = who;
        shortWhoList = cell(length(whoList) -2, 1);
        this = compiler.internal.MLArrayViewerModel.currentThis();
        compiler.internal.MLArrayViewerModel.currentThis(this);

        % store new variables back to data
        j = 1;
        for i = 1:length(whoList)
            switch whoList{i}
                case {'ans','varargout'}
                    % don't store these
                    continue
            end
            shortWhoList{j} = whoList{i};
            j = j + 1;
            this.data.(whoList{i}) = eval(whoList{i});
        end
        
        % clear variables that on longer exist
        fieldlist = fieldnames(this.data);
        if ~isequal(fieldlist, shortWhoList)
            for i = 1:length(fieldlist)
                if ~any(cellfun(@(a) isequal(a,fieldlist{i}), whoList))
                    this.data = rmfield(this.data, fieldlist{i});
                end
            end
        end
        
        end
        
        function delete(obj)
            obj.data = [];
            if ~isempty(obj.workspaceID)
                % g1229201 ssegench
                % removed the call to clear the workspace id
                % it appeared to be triggering another call to this delete method
                % when that handle (the last handle) was destroyed
                % which was causing bad things to happen if a new workspace
                % other than this one was assigned the same id as this one.
                obj.workspaceID = [];
            end
            delete@handle(obj);
        end
        
    end
    
    methods (Static = true, Access = public)
        % Since the MLArrayViewerModel creates the workspace ID
        % in the call to workspacefunc('getworkspaceid',this);
        % is should offer a method for clearing it. 
        function disposeModel(workspaceID)
            workspacefunc('clearstoredworkspace', workspaceID);
        end
    
    end
    
    methods (Static = true, Access = private)
        
        function out = currentThis(in)
        persistent storedThis;
        if nargin
            storedThis = in;
        else
            out = storedThis;
        end
        end
        
        function assigner(data)
        fieldlist = fieldnames(data);
        for i = 1:length(fieldlist)
            assignin('caller',fieldlist{i},data.(fieldlist{i}))
        end
        end
        
    end
end
