function [Objectives,...
    mGoalToLinkinfoMap,...
    objectiveToGoalMap,...
    goalIdToObjectiveIdMap,...
    obsInfo] = getObjectivesInformation(modelH,testComp, ...
    allGoals, mGoalToLinkinfoMap)
%GETOBJECTIVESINFORMATION Summary of this function goes here
%   Detailed explanation goes here

%   Copyright 2023 The MathWorks, Inc.

tcActiveSettings = testComp.activeSettings;
rangeData = testComp.getRanges();

GoalCnt = numel(allGoals);

ObjFact = Sldv.ReportObjectiveFactory(tcActiveSettings);
Objectives = [];
ObjectiveIndex = 1;
objectiveToGoalMap = containers.Map('KeyType', 'double', 'ValueType', 'any');
goalIdToObjectiveIdMap = containers.Map('KeyType', 'double', 'ValueType', 'double');

obsInfo = [];
obsInfo.extObjToGoalMap = containers.Map('KeyType', 'double', 'ValueType', 'any');
obsInfo.goalIdToExtObjIdMap = containers.Map('KeyType', 'double', 'ValueType', 'double');
obsInfo.goalIdToPathObjIdMap = containers.Map('KeyType', 'double', 'ValueType', 'any');
obsInfo.PathObjectives = [];
obsInfo.extObjectives = [];
   

if GoalCnt > 0

    emptyObjective = ObjFact.createEmptyObjective();

    % Batch allocate all Objectives using Tony's trick
    Objectives = emptyObjective;
    Objectives(1:GoalCnt) = emptyObjective;

    for currentGoal = allGoals(:)'
        % Adding the linkStorage info on the data objectives itself to
        % remove the dependency on the test component. The linkStorage info
        % should be redundant.
        if ~mGoalToLinkinfoMap.isKey(currentGoal.getGoalMapId)
            % info is not used now
            [info, storeInData] = storeLink(currentGoal); %#ok<ASGLU>
            mGoalToLinkinfoMap(currentGoal.getGoalMapId) = storeInData;
        else
            storeInData =  mGoalToLinkinfoMap(currentGoal.getGoalMapId);
        end

        % read goal type as 'path objective'
        if strcmp(currentGoal.type, 'AVT_GOAL_PATH_OBJECTIVE')
            % path goal
            obsInfo.PathObjectives = addPathObjective(currentGoal, obsInfo,...
                goalIdToObjectiveIdMap, storeInData,...
                rangeData, ObjFact);
        elseif strcmp(currentGoal.type,'AVT_GOAL_CUSBLKCOV')
            obsInfo.extObjectives = addExtObjective(currentGoal, obsInfo.extObjectives,...
                obsInfo.goalIdToExtObjIdMap, storeInData,...
                rangeData, ObjFact,...
                obsInfo.extObjToGoalMap, modelH);
        else
            addObjective(storeInData);
        end
    end
    % Prune any unused but allocated objectives
    if ObjectiveIndex<=numel(Objectives)
        Objectives(ObjectiveIndex:end) = [];
    end
end

%This function here is a nested function and is using multiple
%variables from the parent function i.e currentGoal, rangeData, modelH,
%Objectives, ObjectiveIndex, goalIdToObjectiveIdMap, objectiveToGoalMap
    function addObjective(linkInfo)
        if ~goalIdToObjectiveIdMap.isKey(currentGoal.getGoalMapId)
            newObjective = ObjFact.createObjective(currentGoal, linkInfo, rangeData, modelH);
            Objectives(ObjectiveIndex) = newObjective;
            goalIdToObjectiveIdMap(currentGoal.getGoalMapId) = ObjectiveIndex;
            objectiveToGoalMap(ObjectiveIndex) = currentGoal;
            ObjectiveIndex = ObjectiveIndex + 1;
        end
    end


end
function [lss, storeInData] = storeLink(goalH)
blkCondGoalTypes = Sldv.utils.getSupportedBlockConditions;

switch(goalH.type)
    case [{'AVT_GOAL_CUSTEST', 'AVT_GOAL_CUSPROOF', 'AVT_GOAL_OVERFLOW', ...
            'AVT_GOAL_FLOAT_INF', 'AVT_GOAL_FLOAT_NAN', 'AVT_GOAL_FLOAT_SUBNORMAL', ...
            'AVT_GOAL_DIV0', 'AVT_GOAL_ASSERT', 'AVT_GOAL_RANGE', 'AVT_GOAL_DESRANGE', ...
            'AVT_GOAL_TRANS_CNFCT', 'AVT_GOAL_STATE_CONS', 'AVT_GOAL_SFARRAY_BNDS', ...
            'AVT_GOAL_EMLARRAY_BNDS', 'AVT_GOAL_SELECT_BNDS', 'AVT_GOAL_MPSWITCH_BNDS', ...
            'AVT_GOAL_INVALID_CAST', 'AVT_GOAL_MERGE_CNFCT', 'AVT_GOAL_UNINIT_DSR', ...
            'AVT_GOAL_ARRBOUNDS', 'AVT_GOAL_RBW_HAZARD', 'AVT_GOAL_WAR_HAZARD', ...
            'AVT_GOAL_WAW_HAZARD', ...
            'AVT_GOAL_BLOCK_INPUT_RANGE_VIOLATION', 'AVT_GOAL_OBJECTIVE_COMPOSITION', ...
            'AVT_GOAL_SFCN_COND', 'AVT_GOAL_SFCN_DEC', 'AVT_GOAL_SFCN_MCDC',...
            'AVT_GOAL_SFCN_ENTRY', 'AVT_GOAL_SFCN_EXIT', 'AVT_GOAL_SFCN_RELATIONAL_BOUNDARIES',...
            'AVT_GOAL_CODE_COND', 'AVT_GOAL_CODE_DEC', 'AVT_GOAL_CODE_MCDC',...
            'AVT_GOAL_CODE_ENTRY', 'AVT_GOAL_CODE_EXIT', 'AVT_GOAL_CODE_RELATIONAL_BOUNDARIES',...
            'AVT_GOAL_SFCN_RTE', 'AVT_GOAL_PATH_OBJECTIVE', 'AVT_GOAL_CUSBLKCOV', ...
            'AVT_GOAL_CODE_RTE', 'AVT_GOAL_SUT_EXEC', 'AVT_GOAL_REQTABLE'...
            } blkCondGoalTypes]
        % Custom objectives - modelObj is the parent
        modelObj = goalH.up;
        cvId = 0;
    otherwise
        % Coverage objectives - modelObj is the grand-parent
        modelObj = goalH.up.up;
        cvId = sldvprivate('getCvId',goalH.up);
end

storeInData = [];

if isempty(modelObj)
    return;
end

lss = [];
if modelObj.isSlBlock
    lss.blockH = modelObj.slBlkH;
elseif modelObj.sfObjID > 0
    sfId = modelObj.sfObjID;
    if (sf('get', sfId, '.isa') == sf('get', 'default', 'state.isa')) && sf('get', sfId, '.eml.isEML')
        % MATLAB
        chartName = sf('get',sf('get',sfId,'.chart'),'.name');
        if any(chartName == '/')
            chartName = fliplr(strtok(fliplr(chartName),'/'));
        end

        if cvId>0
            lineInfo = sldvprivate('get_eml_line_info',sfId, cvId);
        else
            lineInfo = sldvprivate('get_eml_line_info',sfId, 0, goalH.emlLineNumber);
        end
        linkText = [chartName ' #' num2str(lineInfo.lineNum)];
        lss = struct('objId', sfId, 'objName', linkText,  'startIdx',  lineInfo.startIdx, 'endIdx', lineInfo.endIdx);
        %Store additional link information in the data
        storeInData.isEml = true;
        storeInData.startIdx = lineInfo.startIdx;
        storeInData.endIdx = lineInfo.endIdx;
    elseif sf('get', sfId, '.isa') == sf('get', 'default', 'script.isa')
        chartName = '';
        if (~strcmp(goalH.type,'AVT_GOAL_TESTGEN'))
            lineInfo = sldvprivate('get_eml_line_info',sfId, 0, goalH.emlLineNumber);
        else
            lineInfo = sldvprivate('get_eml_line_info',sfId, cvId);
        end
        linkText = [chartName ' #' num2str(lineInfo.lineNum)];
        lss = struct('objId', sfId, 'objName', linkText,  'startIdx',  lineInfo.startIdx, 'endIdx', lineInfo.endIdx);
        storeInData.isEml = true;
        storeInData.startIdx = lineInfo.startIdx;
        storeInData.endIdx = lineInfo.endIdx;

    elseif modelObj.isTruthTableGen && ...
            (sf('get', sfId, '.isa') ~= sf('get', 'default', 'data.isa'))
        % Truth table

        lss.sfId = sfId;
        mappingInfo = sf('get', sfId, '.autogen.mapping');
        if isfield(mappingInfo, 'index')
            % Stateflow
            lss.decIdx = mappingInfo.index;
            storeInData.isEml = false;
            storeInData.index = mappingInfo.index;
        else
            % MATLAB
            chart = sf('get', sfId, '.chart');
            name = sf('get', chart, '.name');
            if sf('get', chart, '.type') == 0 % Not a truth table block
                name = [ name '.' sf('get', sfId, '.name') ];
            end

            if any(name == '/')
                name = fliplr(strtok(fliplr(name),'/'));
            end
            if (cvId==0)
                ttItem = sldvprivate('get_script_to_truth_table_map', mappingInfo, goalH.emlLineNumber);
            else
                lineInfo = sldvprivate('get_eml_line_info',sfId, cvId);
                ttItem = sldvprivate('get_script_to_truth_table_map', mappingInfo, lineInfo.lineNum);
            end

            index = 0;
            type = 0;

            if ~isempty(ttItem)
                index = ttItem.index;
                type = ttItem.type;
            end
            lss = struct('objId', sfId, 'objName', name, 'startIdx', type, 'endIdx', index);

            storeInData.isEml = true;
            storeInData.startIdx = type;
            storeInData.endIdx = index;
        end
    elseif modelObj.isStateflow && ...
            (sf('get', sfId, '.isa') == sf('get', 'default', 'transition.isa') || ... % For objectives on transition
            sf('get', sfId, '.isa') == sf('get', 'default', 'state.isa'))  && ...      % For objectives on states
            ~(goalH.startCharIdx == -1 || goalH.endCharIdx == -1)
        storeInData.isEml = false;
        storeInData.startIdx = goalH.startCharIdx;
        storeInData.endIdx = goalH.endCharIdx;
        if ~isfield(storeInData, 'index')
            storeInData.index = 0;
        end
        chart = sf('get', sfId, '.chart');
        name = sf('get', chart, '.name');
        lss = struct('objId', sfId, 'objName', name, 'startIdx', storeInData.startIdx, 'endIdx', storeInData.endIdx);
    else
        lss.sfId = sfId;
    end
else
    % External MATLAB files with sfObjID = 0
    if strcmp(goalH.type,'AVT_GOAL_TESTGEN')
        lineInfo = sldvprivate('get_eml_line_info', 0, cvId);
    else
        lineInfo = sldvprivate('get_eml_line_info', 0, 0, goalH.emlLineNumber);
    end

    linkText = [' #' num2str(lineInfo.lineNum)];
    lss = struct('objId', 0, 'objName', linkText, 'startIdx', lineInfo.startIdx, 'endIdx', lineInfo.endIdx);
    storeInData.isEml = true;
    storeInData.startIdx = lineInfo.startIdx;
    storeInData.endIdx = lineInfo.endIdx;
end
end



function PathObjectives = addPathObjective(goalH, obsInfo, ...
    goalIdToObjIdMap,...
    linkInfo, rangeData,...
    objectiveFactory) %#ok<INUSL>
% This function is called only with PathBasedTestGeneration feature is on
PathObjectives = obsInfo.PathObjectives;
goalIdToExtObjIdMap = obsInfo.goalIdToExtObjIdMap;
goalIdToPathObjIdMap = obsInfo.goalIdToPathObjIdMap;

if ~goalIdToPathObjIdMap.isKey(goalH.getGoalMapId)

    newObjective = objectiveFactory.createPathObjective(goalH, linkInfo);

    if isempty(PathObjectives)
        PathObjectives = newObjective;
    else
        PathObjectives(end+1) = newObjective;
    end

    % Add composed goals information to the new objective
    composedGoals = goalH.composedGoals;
    if ~isempty(composedGoals)
        % Assumption: composedGoals contains the
        % ExtensionGoal/SourceObjective as its first entry always
        currentGoal = composedGoals(1);
        currGoalIdx = currentGoal.getGoalMapId();
        if goalIdToExtObjIdMap.isKey(currGoalIdx)
            composedObjIndex = goalIdToExtObjIdMap(...
                currGoalIdx);
            PathObjectives(end).extensionObjectives = composedObjIndex;
            % For custom test objectives, there are no additional
            % extension objectives added.
            % Hence the pathObjective would
            % refer directly to the test objective.
        elseif strcmp(currentGoal.type,'AVT_GOAL_CUSTEST') &&...
                goalIdToObjIdMap.isKey(currGoalIdx)
            composedObjIndex = goalIdToObjIdMap(currGoalIdx);
            PathObjectives(end).objectives = composedObjIndex;
        end
    end
    goalIdToPathObjIdMap(goalH.getGoalMapId) = length(PathObjectives);         %#ok<NASGU>
end
end

function Objectives = addExtObjective(goalH, Objectives, ...
                        goalIdToExtObjIdMap, linkInfo, ...
                        rangeData, objectiveFactory, ...
                        objectiveToGoalMap, modelH)
if ~goalIdToExtObjIdMap.isKey(goalH.getGoalMapId)
    newObjective = objectiveFactory.createObjective(goalH, linkInfo, rangeData, modelH);
    newObjective.modelObjectIdx = [];
    Objectives = [Objectives newObjective];

    goalIdToExtObjIdMap(goalH.getGoalMapId) = length(Objectives);         %#ok<NASGU>
    objectiveToGoalMap(length(Objectives)) = goalH; %#ok<NASGU>
end
end



