%

%   Copyright 2019-2020 The MathWorks, Inc.

classdef aggregation < cv.cvdatagroup
    properties (GetAccess=public)
    end
    properties (GetAccess=public, Hidden=true)        
    end
    properties(Access = private)
        allData = []
        results = []
        reqInfo = []
    end
    properties (GetAccess=public, SetAccess=public, Dependent=true, Hidden=true)
    end
methods (Hidden)
    function res = isTraceOn(~)
       res = strcmpi(cv('Feature', 'Trace'), 'on'); 
    end
end
methods
    
    function this = aggregation()
        this.allData = [];
        this.reqInfo = [];
    end
    %======================
    function addData(this, cvd, assoc)
        if nargin  < 3
            assoc = '';
        end
        td = struct('cvd', {cvd}, 'assoc', {assoc});

        if ~isempty(this.allData)
            this.allData(end + 1)= td;
        else
            this.allData = td;
        end
    end
    %======================
    cvdsum = getSumWithAddSubsys(this)
    %======================
    function sum(this, param)
        if ~isfield(param, 'subsystemUnits')
            param.subsystemUnits = {};
        end
        % 1 - units excluded
        % 0 - units aggregated
        if ~isfield(param, 'mode')
            param.mode = 0;
        end

        this.results = containers.Map('KeyType', 'double', 'ValueType', 'any');
        this.sumVariantModels(this.allData, param);
        this.rollUpSubsystems(this.allData, param);
        this.createResultsCvdatagroup;
    end
    %======================
    rollUpSubsystems(this, modelData, param)
    %======================
    sumVariantModels(this, modelData, param)
    
    %======================
    function createResultsCvdatagroup(this)
        values = this.results.values;
        for idx = 1 : numel(values)
            v = values{idx};
            setAnalyzedModel(v.cvd, v.analyzedModel);
            name = SlCov.CoverageAPI.mangleModelcovName(v.analyzedModel, SlCov.CovMode.Normal, v.cvd.dbVersion);
            this.m_data(name) = v.cvd;
        end
    end

    %======================
    function cvdsum = getSum(this)
    % Sum together all of the coverage data that has been added to this 
    % object and then returns the results.

    % If applicable, will consider provided context to determine how to 
    % aggregate results (e.g. using requirements mapping to remove 
    % incidental coverage).
    
        cvdsum = cvdata.empty;
        numData = numel(this.allData);
        isTraceOn = (numData > 1) && this.isTraceOn;
        
        for idx = 1 : numData
            data = this.allData(idx).cvd;
            assoc = this.allData(idx).assoc;

            data.traceOn = isTraceOn;
            
            if isempty(cvdsum)
                cvdsum = data;
            else
                if ~isempty(assoc)
                    tmpCvdsum = cvdsum.addSubsystem(assoc, data);
                    %might be empty, e.g. couldn't find matching checksum subsytem in the model cvdata 
                    %don't do anything
                    if ~isempty(tmpCvdsum)
                        cvdsum = tmpCvdsum;
                    end
                else
                    cvdsum = cvdsum + data;
                end

            end
        end
        
        % Finalize and hold on to requirements mapping info so that this 
        % can later be shown in coverage reports (if applicable).
        if ~isempty(cvdsum) && ~isempty(this.reqInfo)
            cvdsum.processAgregatedInfo;
            this.buildReqInfoSimulationStruct(cvdsum);
            cvdsum.reqTestMapInfo = this.prepReqInfoForSave();
        end
    end
    
    %======================
    function setRequirementsMapping(this, reqInfo)
        this.reqInfo = [];
        
        if isfield(reqInfo, 'Requirement') && ~isempty(reqInfo.Requirement)
            this.reqInfo = reqInfo;
        end
    end
end
methods (Hidden)

%-------------------------------------
% Requirements Traceability - helper functions

    function buildReqInfoSimulationStruct(this, cvdsum)
    % Construct Simulation struct based on aggregated coverage results and
    % link into the existing requirements mapping info.
        
        % Reset array of simulations
        this.reqInfo.Simulation = struct('RunId', {}, 'TestIdx', {}, 'URL', {}, 'Label', {});
        for i=1:length(this.reqInfo.Test)
            this.reqInfo.Test(i).SimulationInd = [];
        end
        
        ati = cvdsum.aggregatedTestInfo;
        for i=1:numel(ati)
            runInfo = ati(i).testRunInfo;
            testInds = this.testId2ReqInfoTestIdx(runInfo.testId);
            if ~isempty(testInds)
                runId = runInfo.runId;
                url = "matlab:stm.internal.util.highlightTestResult('" + runId + "');";
                label = cvdsum.getTraceLabel(i);
                
                simItem = struct('RunId', runId, 'TestIdx', testInds, 'URL', url.char, 'Label', label);  
                this.reqInfo.Simulation(end + 1) = simItem;
                simIdx = length(this.reqInfo.Simulation);
                
                for t = 1:numel(testInds)
                    tIdx = testInds(t);
                    this.reqInfo.Test(tIdx).SimulationInd(end+1) = simIdx;
                end
            end  
        end
    end

    function testIdx = testId2ReqInfoTestIdx(this, testUUID)
    % Get reqInfo.Test index matching specified testUUID.
        testIdx = [];
        if ~isempty(testUUID) && this.reqInfo.testUuid2IdxMap.isKey(testUUID)
            testIdx = this.reqInfo.testUuid2IdxMap(testUUID);
        end
    end
    
    reqInfoOut = prepReqInfoForSave(this);
    
%-------------------------------------
end
methods (Static)
    [cutCvd, extractCvd] =  extractAndCutVariantSubsystems(inCvdata);
    
    function map = setResult(map, ccvd)
        chks = ccvd.checksum;
        chksK = chks.u1 + chks.u2 + chks.u3 + chks.u4; 
        tvalue.cvd = ccvd;
        tvalue.analyzedModel = ccvd.modelinfo.analyzedModel;  
        map(chksK) = tvalue; 
    end

    function map = addToResult(map, ccvd)
        chks = ccvd.checksum;
        chksK = chks.u1 + chks.u2 + chks.u3 + chks.u4; 
        tvalue = [];
        if map.isKey(chksK)
            tvalue = map(chksK);
            tvalue.cvd = tvalue.cvd + ccvd;
            if isempty(tvalue.analyzedModel)
                tvalue.analyzedModel = ccvd.modelinfo.analyzedModel;  
            end
        else
            tvalue.cvd = ccvd;
            tvalue.analyzedModel = ccvd.modelinfo.analyzedModel;  
        end
        map(chksK) = tvalue; 
    end

end
end
