function createDerivedData(this, lhs, rhs, metrics, trace)                                
%

%   Copyright 2010-2020 The MathWorks, Inc.                               
    if lhs.simMode~=rhs.simMode
        error(message('Slvnv:simcoverage:cvdata:IncompSimModes', ...
           SlCov.CovMode.toString(lhs.simMode), SlCov.CovMode.toString(rhs.simMode)));
    end 

    data.type = 'DERIVED_DATA';
    data.metrics = add_metrics_fields(metrics);
    data.traceOn = lhs.traceOn || rhs.traceOn;
    if data.traceOn 
        data.trace = trace;
    end
    data.testSettings = merge_testSettings(lhs, rhs);
    data.filterData = merge_filterData(lhs, rhs);
    data.checksum = lhs.checksum;
    data.variantChecksum = lhs.variantChecksum;
    data.startTimeEnum = earliest_time(lhs.startTimeEnum, rhs.startTimeEnum);
    data.stopTimeEnum = latest_time(lhs.stopTimeEnum, rhs.stopTimeEnum);
    data.simulationStartTime = earliest_time(lhs.simulationStartTime, rhs.simulationStartTime);
    data.simulationStopTime = latest_time(lhs.simulationStopTime, rhs.simulationStopTime);

    % In local mode, the time is not a string!
    data.startTime = data.startTimeEnum;
    data.stopTime = data.stopTimeEnum;

    lhsTimeWindowStart = lhs.intervalStartTime;
    rhsTimeWindowStart = rhs.intervalStartTime;
    if lhsTimeWindowStart < rhsTimeWindowStart
        data.intervalStartTime = lhsTimeWindowStart;
    else
        data.intervalStartTime = rhsTimeWindowStart;
    end

    lhsTimeWindowStop = lhs.intervalStopTime;
    rhsTimeWindowStop = rhs.intervalStopTime;
    if lhsTimeWindowStop < rhsTimeWindowStop
        data.intervalStopTime = lhsTimeWindowStop;
    else
        data.intervalStopTime = rhsTimeWindowStop;
    end

    data.rootId = lhs.rootId;
    data.variantStates = lhs.getRootVariantStates();
    
    data.modelinfo =  match_modelinfo(lhs.getRawModelInfo(), rhs.getRawModelInfo());

    data.sfcnCovData = [];
    data.codeCovData = [];
    data.isExternalMATLABFile = lhs.isExternalMATLABFile;
    data.isSubsystem = lhs.isExternalMATLABFile;
    data.isSimulinkCustomCode = false;
    data.isSharedUtility = false;
    data.isCustomCode = false;
    data.covSFcnEnable = false;
    data.simMode = lhs.simMode;
    data.dbVersion = lhs.dbVersion;
    data = checkFilter(data, lhs, rhs);

    data.testRunInfo = [];
    
    if ~isempty(lhs.reqTestMapInfo)
        data.reqTestMapInfo = lhs.reqTestMapInfo;
    else
        data.reqTestMapInfo = rhs.reqTestMapInfo;
    end
        
    data.scopeDataToReqs = lhs.scopeDataToReqs || rhs.scopeDataToReqs;
    
    this.localData = data;

    this.localData.sfcnCovData = lhs.sfcnCovData;
    this.localData.codeCovData = lhs.codeCovData;
    this.localData.isSharedUtility = lhs.isSharedUtility;
    this.localData.isCustomCode = lhs.isCustomCode;
    this.localData.covSFcnEnable = lhs.covSFcnEnable;

    if SlCov.isSLCustomCodeCovFeatureOn()
        this.localData.isSimulinkCustomCode = lhs.isSimulinkCustomCode;
    end
    cvdata.aggregateDescription(this, lhs, rhs);
    this.setUniqueId;    
    
end
%=================================
function testSettings = merge_testSettings(lhs, rhs)
    testSettings_lhs = lhs.testSettings;
    testSettings_rhs = rhs.testSettings;
    allMetrics = fields(testSettings_lhs);
    testSettings = testSettings_lhs;
    for i=1:length(allMetrics)
        metric = allMetrics{i};
        testSettings.(metric) = testSettings_lhs.(metric) | testSettings_rhs.(metric);
    end
end
%=================================
function newFilterData = merge_filterData(lhs, rhs)
%can be different based on concatOp

    newFilterData = [];
    if isempty(lhs.filterData) || isempty(rhs.filterData)
        if ~isempty(lhs.filterData)
            concatOp = lhs.filterData.concatOp;
        elseif ~isempty(rhs.filterData)
            concatOp = rhs.filterData.concatOp;
        else
            return;
        end
        if concatOp == 0 %or
           newFilterData = [lhs.filterData rhs.filterData] ;
        elseif concatOp == 1 %and
           newFilterData = [];
        end
        return;
    end
    filterData = [lhs.filterData rhs.filterData] ;
    types = unique({filterData.type});
    newFilterData = struct('id', '', 'type', types, 'rules', '', 'concatOp', '');
    for idx = 1 : numel(types)
        cFD = filterData({filterData.type} == string(types{idx}));
        if numel(cFD) == 2
            guidStr = net.jini.id.UuidFactory.generate();
            newFilterData(idx).id = char(guidStr.toString());
            concatOp = cFD(1).concatOp;
            newFilterData(idx).concatOp = concatOp;
            if concatOp == 0 %or
                newFilterData(idx).rules = union(cFD(1).rules, cFD(2).rules);
            elseif concatOp == 1 %and
                newFilterData(idx).rules = intersect(cFD(1).rules, cFD(2).rules);
            end
        else     
            %nothing to merge
            newFilterData(idx) = cFD;
        end
    end
end
%=================================
function data = checkFilter(data, lhs, rhs)
    data.covFilter = '';
    data.filterApplied = '';
    data.covFilter = combine_filter(lhs.filter, rhs.filter);
    data.filterApplied = combine_filter(lhs.filterApplied, rhs.filterApplied);
end

%=================================
function combined = combine_filter(lfileName, rfileName)
    if isempty(lfileName)
        combined = rfileName;
    elseif isempty(rfileName)
        combined = lfileName;
    else
        if ~iscell(lfileName)
            lfileName = {lfileName};
        end
        if ~iscell(rfileName)
            rfileName = {rfileName};
        end
        combined = unique([lfileName rfileName]);
    end
end
%=================
function modelinfo = match_modelinfo(lhs, rhs)
    fieldNames = fieldnames(lhs);
    for fn = fieldNames(:)'
        if ~isequal(lhs.(fn{1}),rhs.(fn{1}))
            if strcmpi(fn, 'analyzedModel')
                lhs.(fn{1}) = checkAnalyzedModel(lhs,rhs);
            elseif strcmpi(fn, 'reducedBlocks')
                if numel(lhs.(fn{1})) < numel(rhs.(fn{1}))
                    lhs.(fn{1}) = rhs.(fn{1});
                end
            else
                lhs.(fn{1}) = 'notUnique';
            end
        end
    end
    modelinfo = lhs;

end

%=================
function metrics = add_metrics_fields(metrics)

    [metricNames, toMetricNames] = cvi.MetricRegistry.getAllMetricNames;

    for i=1:length(metricNames)
        if ~isfield(metrics,metricNames{i})
            metrics.(metricNames{i}) = [];
        end
    end
    if isfield(metrics, 'testobjectives')
        for i=1:length(toMetricNames)
            if ~isfield(metrics.testobjectives, toMetricNames{i})
                metrics.testobjectives.(toMetricNames{i}) = [];
            end
        end
    else
        metrics.testobjectives = [];
    end

end

%=================
function res = earliest_time(time1,time2)

    if isempty(time1) 
       res = time2;
    elseif isempty(time2)
         res = time1;
    else
        try
            if(time1 < time2)
                res = time1;
            else
               res = time2;
            end
        catch Mex %#ok<NASGU>
            res = 0;
        end
    end

end

%=================
function res = latest_time(time1,time2)

    if isempty(time1) 
        res = time2;
    elseif isempty(time2)
         res = time1;
    else
        try
            if(time1>time2)
                res = time1;
            else
                res = time2;
            end
        catch Mex %#ok<NASGU>
            res = '';
        end
    end
end
%==============================
function analyzedModel = checkAnalyzedModel(lhs, rhs)
    analyzedModel =  'notUnique'; 
    ownerModel = lhs.ownerModel; 
    if ~isempty(ownerModel) &&  strcmpi(ownerModel, rhs.ownerModel)
        lhsAM = lhs.analyzedModel;
        rhsAM = rhs.analyzedModel;
        lhsI = strfind(lhsAM, '/');
        rhsI = strfind(rhsAM, '/');
        if ~isempty(lhsI) && ~isempty(rhsI)
            lhsAM = lhsAM(lhsI(1) + 1: end);
            rhsAM = rhsAM(rhsI(1) + 1: end);
            if strcmpi(lhsAM, rhsAM)
                analyzedModel =  [ownerModel '/' lhsAM];
            end
        end
    end
end
