classdef AutoscalerVerifyStrategy < fxptds.WorkflowTopology.SimpleSimulationStrategy
    %% AUTOSCALERVERIFYSTRATEGY class
    
    % Copyright 2018-2019 The MathWorks, Inc.
    
    % Access is protected and extended to Facade and to the test class
    methods (Access = {?fxptds.WorkflowTopology.Strategy, ?fxptds.WorkflowTopology.Facade, ?WorkflowTopologyTestCase})
        
        function execute(this)
            
            % call the SimpleSimulationStategy
            execute@fxptds.WorkflowTopology.SimpleSimulationStrategy(this);
            
            rootName = this.TopModel;
            targetName = this.FPTRunID;
            
            verifyNode = this.Store.getNodeObject(targetName);
            
            % find the collection between the rootNode and new verification
            % excluding the verification run itself to give the baseline
            % run name
            % query for the baseline Name
            query = this.createDefaultQuery();
            query.Type = "Collection";
            query.Limit = 1;
            query.Search = 'path';
            query.Target = targetName;
            query.Exclude = 'target';
            
            % execute the query and extract value from cell array
            extractedData = this.executeQuery(rootName, query);
            baselineName = this.processQueryData(extractedData);
            
            
            % find the proposal between the rootNode and new verification
            % to give the proposal used
            % query for the proposal Name
            query = this.createDefaultQuery();
            query.Type = "Proposal";
            query.Limit = 1;
            query.Search = 'path';
            query.Target = targetName;
            
            % execute the query and extract value from cell array
            extractedData = this.executeQuery(rootName, query);
            proposalName = this.processQueryData(extractedData);
            
            constraintSettings = DataTypeWorkflow.ConstraintSettings;
            if ~isempty(proposalName)
                proposalNode = this.Store.getNodeObject(proposalName);
                proposalSettings = proposalNode.Settings;
                constraintSettings.injectOptimizationOptions(proposalSettings.getOptimizationOptions);
            end
            
            % setVerificationResultStatus must be done first because
            % setting report details depends on the status
            originalReport = verifyNode.Report;
            reportWithStatus = this.setVerificationResultStatus(originalReport, constraintSettings, baselineName);
            
            % setVerificationResultDetails must be done second so the
            % status is set
            reportWithDetails = this.setVerificationResultDetails(reportWithStatus);
            
            % set the result on the autoscalerNode
            verifyNode.updateReport(reportWithDetails);
            
        end
        
    end
    
    % Access is private and extended to the test class
    methods (Access = {?WorkflowTopologyTestCase})
        
        function report = setVerificationResultStatus(this, report, constaintSettings, baselineName)
            % updates the result struct 'Status' property with the correct
            % status
            
            report.ConstraintSettings = constaintSettings;
            optimizationOptions = constaintSettings.getOptimizationOptions;
            if isempty(baselineName)
                % unable to connect the verification run to a known
                % baseline
                report.VerificationStatus = fxptds.VerificationStatus.UnknownBaseline;
            else
                % baseline exists so get the collection object
                collectObject = this.Store.getNodeObject(baselineName);
                if collectObject.IsMerged
                    query = this.createDefaultQuery();
                    query.Search = "parents";
                    query.Type = "Collection";
                    % executeQuery finds the baselines parents that are of
                    % a collection type, return their names
                    parentNames = this.executeQuery(baselineName, query);
                    % if parentNames contains names of Collection nodes
                    hasCollectionParents = ~(numel(parentNames) == 1 && isempty(parentNames{1}));
                    if hasCollectionParents
                        % check the collection object predecessors
                        parentObjects = this.Store.getNodeObject(parentNames);
                        collectObject = parentObjects([parentObjects.Settings] == this.Settings);
                    end
                end
                
                report.BaselineFPTID = collectObject.FPTRunID;
                
                % create comparisonUtil
                baselineComparisonUtil = DataTypeOptimization.SDIBaselineComparison();
                
                if collectObject.IsMerged
                    % merge min/max is on resulting in multiple SDI IDs
                    % under the collection
                    report.VerificationStatus = fxptds.VerificationStatus.MergeSimulationWarning;
                elseif ~baselineComparisonUtil.validateSDIRun(collectObject.getSDIRunID) && ~baselineComparisonUtil.validateSDIRun(this.SDIRunID)
                    % both the collection SDI run ID and the incoming
                    % verification run SDI ID are invalid (empty) so signal
                    % logging is disabled
                    report.VerificationStatus = fxptds.VerificationStatus.NoSignalsLogged;
                elseif isempty(optimizationOptions.Constraints)
                    % not tolerance values have been set
                    report.VerificationStatus = fxptds.VerificationStatus.UnsetTolerance;
                else
                    % get the baseline SDI Run ID
                    baselineSDIRun = collectObject.getSDIRunID;
                    validBaseline = baselineComparisonUtil.validateSDIRun(baselineSDIRun);
                    
                    % get the verify SDI Run ID
                    verificationSDIRun = this.SDIRunID;
                    
                    % if the baseline has a valid SDI Run ID
                    signalsWithTolerances = {};
                    if validBaseline
                        baselineComparisonUtil.bindConstraints(baselineSDIRun, optimizationOptions.Constraints.values);
                        signalsWithTolerances = baselineComparisonUtil.signalPathIDs;
                    end
                    
                    if baselineComparisonUtil.hasMatchingSignals(baselineSDIRun, verificationSDIRun, signalsWithTolerances)
                        report.VerificationStatus = fxptds.VerificationStatus.Verifiable; % intermediate state
                    else
                        report.VerificationStatus = fxptds.VerificationStatus.MisalignedSignalWarning;
                    end
                end
            end
            
        end
        
        function report = setVerificationResultDetails(this, report)
            % set the result proposal settings so that it can be used in
            % VerificationResult.explore to bind the tolerances and launch
            % SDI
            
            % only continue if the the verification status set in
            % setVerificaitonResultStatus was set to Verifiable
            if report.VerificationStatus == fxptds.VerificationStatus.Verifiable
                baselineName = string(report.BaselineFPTID);
                baselineObject = this.Store.getNodeObject(baselineName);
                baselineSDIRunID = baselineObject.getSDIRunID;
                verificationSDIRunID = this.SDIRunID;
                
                % bind the constraints on the baseline run
                optOptions = report.ConstraintSettings.getOptimizationOptions;
                baselineComparisonUtil = DataTypeOptimization.SDIBaselineComparison();
                % g1882842 - clearTolerances before continuing so not
                % to use user specificed tolerances in SDI in
                % evaluation
                baselineComparisonUtil.clearSignalTolerances(baselineSDIRunID);
                baselineComparisonUtil.bindConstraints(baselineSDIRunID, optOptions.Constraints.values);
                [pass, maxDifference] = baselineComparisonUtil.evaluateConstraints(baselineSDIRunID, verificationSDIRunID);
                if pass
                    report.VerificationStatus = fxptds.VerificationStatus.Pass;
                else
                    report.VerificationStatus = fxptds.VerificationStatus.Fail;
                end
                report.MaxDifference = maxDifference;
                
            end
            
        end
        
    end
end

