classdef CollectHandler < fxptui.Web.CallbackService.CallbackHandler
    % Class to handle the work for Collect Callback in FPT.
    
    % Copyright 2019 The MathWorks, Inc.
    
    methods
        function handle(this, clientData)
            
            try
                
                % collection Settings does additional validation of the objects
                % so wrap in try catch and throw error if validation fails
                collectionSettings = this.getCollectionSettings();
                this.prepare(clientData, collectionSettings);
                
                % Disable code-view if present
                this.FPTInstance.enableCodeView(false);
                
                % setting isFPTCallbackSource to true prevents fptoolcallback
                % updatesiglog from updating data before simulation is complete
                this.FPTInstance.isFPTCallbackSource = true;
                
                % collect
                this.execute(collectionSettings);
                
                this.handleSuccess(collectionSettings)
                
            catch e
                this.handleError(e);
                
            end
            
        end
    end
    
    methods(Hidden)
        
        function execute(this, collectionSettings)
            this.Converter.collect(collectionSettings);
        end
        
        function handleSuccess(this, collectionSettings)
            
            isMultiSim = this.isMultiSim(collectionSettings);
            
            % Add data to Spreadsheet, Visualizer
            if isMultiSim
                facade = this.DataLayer.getWorkflowTopologyFacade(this.Converter.TopModel);
                [~, runID] = this.DataLayer.getIdFromRunName(this.Converter.TopModel, collectionSettings.RunName);
                scenarioIDs = facade.query(runID, 'search', 'parents', 'type', 'Collection');
                scenarioNames = this.DataLayer.getRunNameFromID(this.Converter.TopModel, str2double(scenarioIDs));
                runsToUpdate = [{collectionSettings.RunName}, scenarioNames];
                this.FPTInstance.updateData('append', runsToUpdate);
            else
                this.FPTInstance.updateData('append', {collectionSettings.RunName});
            end
            
            if collectionSettings.RangeCollectionMode.isSimulation
                % re-enable code view
                this.FPTInstance.setupCodeView;
            else
                % re-enable code view
                this.FPTInstance.enableCodeView(true);
            end
            
            % reset isFPTCallbackSource to false to allow collection data
            % from simulink to appear in the FPT. It will be reset even
            % if the model compilation fails
            this.FPTInstance.isFPTCallbackSource = false;
            
        end
        
        function handleError(this, e)
            % TODO: g2057490 - improve test by checking that these methods
            % are called on error
            
            % reset isFPTCallbackSource to false to allow collection data
            % from simulink to appear in the FPT. It will be reset even
            % if the model compilation fails
            this.FPTInstance.isFPTCallbackSource = false;
            
            % re-enable codeview so that it is enabled for other runs
            this.FPTInstance.enableCodeView(true);
            
            % g1993741 - refresh RunBrowser after failed callback
            this.refreshRunBrowser();
            
            % g1986262 - refresh SimulationInputs list on the client
            this.resetSimulationInputsList();
            
            % Publish to stop the spinner
            this.FPTInstance.reEnableUI('');
            
            % Throw to diagnostic viewer error
            fxptui.showdialog('rangecollectionfailed', e);
        end
        
        function prepare(this, clientData, collectionSettings)
            
            % set the user provided runName for backwards compatability
            collectionSettings.RunName = clientData.runName;
            
            % convert the given base workspace variable to the object
            % it represents and set the object in collectionSettings
            simulationInputVariableName = clientData.simulationInputs;
            if ~isempty(simulationInputVariableName)
                
                % TODO: g2057490 - consolidate getting simulation input
                % objects from base workspace using a utility
                baseWorkspace = Simulink.data.BaseWorkspace;
                % list all Simulink.SimulationInput objects from the base workspace
                allSimulationInputs = baseWorkspace.whos('Simulink.SimulationInput');
                % assert that the given simulation input variable is still
                % in the base workspace
                assert(ismember(simulationInputVariableName, allSimulationInputs), ...
                    message('FixedPointTool:fixedPointTool:simulationInputError', simulationInputVariableName));
                
                % evaluate the variable to get the object
                simulationInput = evalin('base', simulationInputVariableName);
                collectionSettings.SimulationScenarios = simulationInput;
            end
            
            % set the collection Mode
            collectionMode = DataTypeWorkflow.RangeCollectionMode.clientKeywordToEnum(clientData.collectionMode);
            collectionSettings.RangeCollectionMode = collectionMode;
            
            % set the shortcut to apply
            shortcut = this.getShortcut(clientData);
            
            if strcmp(shortcut, this.Converter.ShortcutManager.CleanupShortcut)
                % declare the cleanupshortcut map to be the current
                % settings
                this.Converter.createSettingsMapFromSystem(shortcut);
            end
            
            % set the shortcuts as the one to apply
            collectionSettings.ShortcutToApply = shortcut;
            
        end
        
        function isMultiSim = isMultiSim(~, collectionSettings)
            isMultiSim = ~isempty(collectionSettings.SimulationScenarios);
        end
        
        function collectionSettings = getCollectionSettings(~)
            % create a fresh collection setting
            collectionSettings = DataTypeWorkflow.CollectionSettings;
        end
        
        function shortcut = getShortcut(this, clientData)
            % shortcutTo apply is given roundabout way by setting it as the
            % IdealizedShortcut property in ShortcutManager. ideally
            % the shortcut would come from the client
            if isfield(clientData, 'shortcut')
                shortcut = clientData.shortcut;
            else
                shortcut = this.Converter.ShortcutManager.getIdealizedBehaviorShortcut;
            end
        end
        
    end
    
end

% LocalWords:  simulationerror rangecollectionfailed
