classdef MergeSimulationStrategy < fxptds.WorkflowTopology.SimpleSimulationStrategy
    %% MERGESIMULATIONSTRATEGY class
    % Handles the Collect Ranges action in FPT when Simulation is selected
    % as the collection method and merge min/max is selected
    %
    % Possible scenarios:
    % 1) Merge min/max is turned on, simulation(s) take place
    % 2) 1 simulation takes place, merge min/max is turned on, more
    % simulation(s) take place
    
    % Copyright 2018-2019 The MathWorks, Inc.
    
    properties(Dependent)
        ScenarioFPTRunID
    end
    
    methods
        function value = get.ScenarioFPTRunID(this)
            value = num2str(this.WorkflowContext.ScenarioFPTRunID);
        end
    end
    
    
    methods (Access = {?fxptds.WorkflowTopology.Strategy, ?fxptds.WorkflowTopology.Facade, ?WorkflowTopologyTestCase})
        
        function execute(this)
            % declare the target node Name as FPTRunID (unique key)
            rootName = this.TopModel;
            targetName = this.FPTRunID;
            
            if this.Store.doesNodeExist(targetName)
                % Get the collectObject which may be either SimulateAction
                % node of DeriveActionNode
                collectObject = this.Store.getNodeObject(targetName);
                collectObject.toggleIsSim();
                collectObject.setSimulationSettings(this.WorkflowContext.Settings);
                
                % if the node Name has already been declared in the Store,
                % invalidate all Actions that succeed the target node ex. A
                % user simulates to create run name Run1 and then proposes data
                % types for that run. The user then derives ranges using the
                % same run name Run1. The original SimulateAction and Proposal
                % from that derive should be invalidated
                nodesToInvalidate = this.getNodesToInvalidate(targetName);
                % nodesToInvalidate is an array of node names to invalidate
                this.invalidateNodes(nodesToInvalidate);
            else
                
                % CollectAction node does not yet exist (see scenario 1
                % above). So create a SimulateAction node
                type = fxptds.WorkflowTopology.element.NodeType.SimulateAction;
                collectObject = this.createAndAddNodeByType(type);
                collectObject.setVerificationStatus(fxptds.VerificationStatus.Null);
                collectObject.setMaxDifference(inf);
            end
            
            % determine source for node
            if isempty(this.ScenarioFPTRunID)
                % the scenario was not given so use the top
                % starting at the root, find the most recent root or autoscaler
                % design that has been created
                query = this.createDefaultQuery();
                query.Type = "Design";
                query.Limit = 1;
                
                % execute the query and extract value from cell array
                extractedData = this.executeQuery(rootName, query);
                sourceName = this.processQueryData(extractedData);
                
                % add the SDI given
                collectObject.addToSDIRunIDs(this.SDIRunID);
                
                % reset the status to NotApplicable
                collectObject.setVerificationStatus(fxptds.VerificationStatus.NotApplicable);
                
                % reset the MaxDifference
                collectObject.setMaxDifference(NaN);
                
                % connect root -> mergedSim or scenarioSim -> mergedSim
                if ~this.Store.doesEdgeExist(sourceName, targetName)
                    this.Store.connect(sourceName, targetName);
                end
            else
                sourceName = this.ScenarioFPTRunID;
                % connect root -> mergedSim or scenarioSim -> mergedSim
                if ~this.Store.doesEdgeExist(sourceName, targetName)
                    this.Store.connect(sourceName, targetName);
                end
                this.updateCollectionWithScenario(sourceName, collectObject);
            end
            
        end
        
    end
    
    % Access is private and extended to the test class
    methods (Access = {?WorkflowTopologyTestCase})
        
        function updateCollectionWithScenario(this, scenarioName, collectObject)
            
            % get all nodeObject for the scenario given
            scenarioNode = this.Store.getNodeObject(scenarioName);
            
            % set the scenario "parent" to be the given merge collection name
            scenarioNode.setParentID(collectObject.Name);
            
            % get the VerificationStatus for each node to compare and
            % insert into statuses array
            scenarioReport = scenarioNode.Report;
            statuses = [scenarioReport.VerificationStatus, collectObject.Report.VerificationStatus];
            
            % get the ranking of VerificationStatus as rankedStatuses array
            status = fxptds.VerificationStatus.getDominantStatus(statuses);
            collectObject.setVerificationStatus(status);
            
            % set the baseline for the merge node by getting the latest
            % collection between the root and the scenario
            baselineName = this.getBaselineFPTRunID(scenarioName);
            if ~isempty(baselineName)
                collectObject.setBaselineFPT(baselineName);
            end
            
            % set the max
            maxDifferences = [scenarioReport.MaxDifference, collectObject.Report.MaxDifference];
            dominantMaxDifference = this.getDominantMaxDifference(maxDifferences);
            collectObject.setMaxDifference(dominantMaxDifference);
            
            % add the scenario's Report to the merged node's list of
            % related scenario's reports
            collectObject.addScenarioReport(scenarioReport);
            
            % add the scenario's SDI Run ID to this one
            collectObject.addToSDIRunIDs(scenarioNode.SDIRunID);
            
        end
        
        function maxDifference = getDominantMaxDifference(~, maxDifferences)
            % check if any of the given max diffs are NaN
            if any(isnan(maxDifferences))
                % set the maxDifference as NaN
                maxDifference = NaN;
            else
                % set the maxDifference as the minimum of the max values
                maxDifference = min(maxDifferences);
            end
        end
        
        function baselineFPT = getBaselineFPTRunID(this, scenarioName)
            % set the baseline for the merge node by getting the latest
            % collection between the root and the scenario
            query = this.createDefaultQuery();
            query.Type = "Collection";
            query.Limit = 1;
            query.Search = 'path';
            query.Target = scenarioName;
            query.Exclude = 'target';
            % execute the query and extract value from cell array
            extractedData = this.executeQuery(this.TopModel, query);
            baselineName = this.processQueryData(extractedData);
            if isempty(baselineName)
                baselineFPT = [];
            else
                baselineFPT = str2double(baselineName);
            end
        end
        
    end
    
    
end

