function [tblValues,tblTooltips, tblColumnNames, tblColumnTooltips] = exportTaskSummary(tps, columns, fullData, execProf, reportFcn)
    % extract tables with task summary from executionProfile objects

%   Copyright 2022 The MathWorks, Inc.

    % process TicksPerSecond
    tps = double(tps);
    if isempty(tps) || tps == 0
        scaleFactor = 1;
        timeUnit = 'ticks';
    else
        scaleFactor = tps / 1000000000;
        timeUnit = 'ns';
    end
    % process data
    data = extractData(execProf, reportFcn, fullData, columns);
    % create tables
    [tblValues, tblTooltips] = createDataTables(data, columns, scaleFactor);
    [tblColumnNames, tblColumnTooltips] = createHeaderTables(columns, timeUnit);
end

function data = preAllocateData(numTasks)
    % initialize data structures
    data.dataModel = cell(1,numTasks);
    data.tooltipModel = cell(1,numTasks);
    data.dataModelCmd = cell(1,numTasks);
    data.tooltipModelCmd = cell(1,numTasks);
    data.dataMaxTurnaround = zeros(1,numTasks);
    data.tooltipMaxTurnaround = cell(1,numTasks);
    data.dataAverageTurnaround = zeros(1,numTasks);
    data.tooltipAverageTurnaround = cell(1,numTasks);
    data.dataMaxExecution = zeros(1,numTasks);
    data.tooltipMaxExecution = cell(1,numTasks);
    data.dataAverageExecution = zeros(1,numTasks);
    data.tooltipAverageExecution = cell(1,numTasks);
    data.dataMaxSelf = zeros(1,numTasks);
    data.tooltipMaxSelf = cell(1,numTasks);
    data.dataAverageSelf = zeros(1,numTasks);
    data.tooltipAverageSelf = cell(1,numTasks);
    data.dataCalls = zeros(1,numTasks);
    data.tooltipCalls = cell(1,numTasks);
    data.dataSDICmd = cell(1,numTasks);
    data.tooltipSDICmd = cell(1,numTasks);
    data.dataHistCmd = cell(1,numTasks);
    data.tooltipHistCmd = cell(1,numTasks);
    data.dataMembCmd = cell(1,numTasks);
    data.tooltipMembCmd = cell(1,numTasks);
end

function numTasks = getNumberOfTasks(execProf)
    numTasks = 0;
    for idxModule = 1:execProf.getModuleNumber()
        sections = execProf.getModule(idxModule).Sections;
        for idxSection = 1:length(sections)
            taskSection = sections(idxSection);
            if ~taskSection.getTraceInfo().isTask()
                continue;
            end
            numTasks = numTasks + 1;
        end
    end
end

function data = extractData(execProf, reportFcn, fullData, columns)
    % extract data from executionProfile objects

    % preallocate data
    numTasks = getNumberOfTasks(execProf);
    if numTasks == 0
        data = [];
        return;
    end
    data = preAllocateData(numTasks);
    % process all tasks
    idx = 0;
    for idxMdoule = 1:execProf.getModuleNumber()
        module = execProf.getModule(idxMdoule);
        sections = module.Sections;
        topModel = module.SimulationComponent;
        for idxSection = 1:length(sections)
            taskSection = sections(idxSection);
            if ~taskSection.getTraceInfo().isTask()
                continue;
            end
            idx = idx + 1;
            numCalls = taskSection.NumCalls;
            for idxColumn = 1:length(columns)
                column = columns(idxColumn);
                % extract information for each expected column
                switch(column)
                    case coder.profile.ResultColumns.Model
                        [~, matlabCmd, modelNameTooltip] = coder.profile.profGetMatlabCmd(...
                            taskSection, topModel, reportFcn, '', true);
                        data.dataModel{idx} = taskSection.Name;
                        data.tooltipModel{idx} = modelNameTooltip;
                        data.dataModelCmd{idx} = matlabCmd;
                        data.tooltipModelCmd{idx} = '';
                    case coder.profile.ResultColumns.MaxTurnaround
                        data.dataMaxTurnaround(idx) = taskSection.MaximumTurnaroundTimeInTicks;
                        if taskSection.MaximumTurnaroundTimeCallNum > 0
                            data.tooltipMaxTurnaround{idx} = ...
                                sprintf('Longest turnaround execution at call number %d', ...
                                taskSection.MaximumTurnaroundTimeCallNum);
                        else
                            data.tooltipMaxTurnaround{idx} = '';
                        end
                    case coder.profile.ResultColumns.AverageTurnaround
                        data.dataAverageTurnaround(idx) = ...
                            taskSection.TotalTurnaroundTimeInTicks / numCalls;
                        data.tooltipAverageTurnaround{idx} = '';
                    case {coder.profile.ResultColumns.MaxRealTimeExecution, ...
                            coder.profile.ResultColumns.MaxNonRealTimeExecution}
                        data.dataMaxExecution(idx) = taskSection.MaximumExecutionTimeInTicks;
                        if taskSection.MaximumExecutionTimeCallNum > 0
                            data.tooltipMaxExecution{idx} = ...
                                sprintf('Longest execution at call number %d', ...
                                taskSection.MaximumExecutionTimeCallNum);
                        else
                            data.tooltipMaxExecution{idx} = '';
                        end
                    case {coder.profile.ResultColumns.AverageRealTimeExecution, ...
                            coder.profile.ResultColumns.AverageNonRealTimeExecution}
                        data.dataAverageExecution(idx) = ...
                            taskSection.TotalExecutionTimeInTicks / numCalls;
                        data.tooltipAverageExecution{idx} = '';
                    case coder.profile.ResultColumns.MaxSelf
                        data.dataMaxSelf(idx) = taskSection.MaximumSelfTimeInTicks;
                        if taskSection.MaximumSelfTimeCallNum > 0
                            data.tooltipMaxSelf{idx} = ...
                                sprintf('Longest self execution at call number %d', ...
                                taskSection.MaximumSelfTimeCallNum);
                        else
                            data.tooltipMaxSelf{idx} = '';
                        end
                    case coder.profile.ResultColumns.AverageSelf
                        data.dataAverageSelf(idx) = ...
                            double(taskSection.TotalSelfTimeInTicks) / numCalls;
                        data.tooltipAverageSelf{idx} = '';
                    case coder.profile.ResultColumns.Calls
                        data.dataCalls(idx) = numCalls;
                        data.tooltipCalls{idx} = '';
                    case coder.profile.ResultColumns.Links
                        if fullData
                            [~, sdiMatlabCmd, sdiTooltpCmd] = ...
                                coder.profile.ExecTimeHelper.getDataForSdiLink(...
                                    taskSection,  topModel, reportFcn, false, true, true);
                            [~, hstMatlabCmd, hstTooltipCmd] = ...
                                coder.profile.ExecTimeHelper.getDataForHistogramLink(...
                                    taskSection, topModel, false, reportFcn, false, true);
                        else
                            sdiMatlabCmd = '';
                            sdiTooltpCmd = '';
                            hstMatlabCmd = '';
                            hstTooltipCmd = '';
                        end
                        data.dataSDICmd{idx} = sdiMatlabCmd;
                        data.tooltipSDICmd{idx} = sdiTooltpCmd;
                        data.dataHistCmd{idx} = hstMatlabCmd;
                        data.tooltipHistCmd{idx} = hstTooltipCmd;
                        [~, memMatlabCmd, memTooltipCmd] = ...
                            coder.profile.ExecTimeHelper.getDataForApiLink(...
                                taskSection, topModel, reportFcn, false, false);
                        data.dataMembCmd{idx} = memMatlabCmd;
                        data.tooltipMembCmd{idx} = memTooltipCmd;
                end
            end
        end
    end
end

function [tblValues, tblTooltips] = createDataTables(data, columns, scaleFactor)
    % create result table

    % init outputs
    tblValues = table();
    tblTooltips = table();
    % check error condition
    if isempty(data)
        return;
    end
    % create table with task results
    for idxColumn = 1:length(columns)
        column = columns(idxColumn);
        switch(column)
            case coder.profile.ResultColumns.Model
                % section name column
                tblValues.Name = data.dataModel';
                tblTooltips.Name = data.tooltipModel';
                tblValues.NameCmd = data.dataModelCmd';
                tblTooltips.NameCmd = data.tooltipModelCmd';
            case coder.profile.ResultColumns.MaxTurnaround
                % maximum touraround column
                tblValues.MaxTurnaround = (data.dataMaxTurnaround/scaleFactor)';
                tblTooltips.MaxTurnaround = data.tooltipMaxTurnaround';
            case coder.profile.ResultColumns.AverageTurnaround
                % average touraround column
                tblValues.AverageTurnaround = (data.dataAverageTurnaround/scaleFactor)';
                tblTooltips.AverageTurnaround = data.tooltipAverageTurnaround';
            case {coder.profile.ResultColumns.MaxRealTimeExecution, ...
                    coder.profile.ResultColumns.MaxNonRealTimeExecution}
                % maximum execution time column
                tblValues.MaxExecution = (data.dataMaxExecution/scaleFactor)';
                tblTooltips.MaxExecution = data.tooltipMaxExecution';
            case {coder.profile.ResultColumns.AverageRealTimeExecution, ...
                    coder.profile.ResultColumns.AverageNonRealTimeExecution}
                % average execution time column
                tblValues.AverageExecution = (data.dataAverageExecution/scaleFactor)';
                tblTooltips.AverageExecution = data.tooltipAverageExecution';
            case coder.profile.ResultColumns.MaxSelf
                % max self time column
                tblValues.MaxSelf = (data.dataMaxSelf/scaleFactor)';
                tblTooltips.MaxSelf = data.tooltipMaxSelf';
            case coder.profile.ResultColumns.AverageSelf
                % average self time column
                tblValues.AverageSelf = (data.dataAverageSelf/scaleFactor)';
                tblTooltips.AverageSelf = data.tooltipAverageSelf';
            case coder.profile.ResultColumns.Calls
                % numm call column
                tblValues.Calls = data.dataCalls';
                tblTooltips.Calls = data.tooltipCalls';
            case coder.profile.ResultColumns.Links
                % sdi command column
                tblValues.SDICmd = data.dataSDICmd';
                tblTooltips.SDICmd = data.tooltipSDICmd';
                % histogram command column
                tblValues.HistCmd = data.dataHistCmd';
                tblTooltips.HistCmd = data.tooltipHistCmd';
                % membrane command column
                tblValues.MembCmd = data.dataMembCmd';
                tblTooltips.MembCmd = data.tooltipMembCmd';
        end
    end
end

function [tblColumnNames, tblColumnTooltips] = createHeaderTables(columns, timeUnit)
    % create table headers

    % init outputs
    tblColumnNames = table();
    tblColumnTooltips = table();
    % create table with task results
    for idxColumn = 1:length(columns)
        column = columns(idxColumn);
        switch(column)
            case coder.profile.ResultColumns.Model
                % section name column
                tblColumnNames.Name = DAStudio.message('CoderProfile:ExecutionTime:ColumnHeadingSection');
                tblColumnTooltips.Name = {''};
                tblColumnNames.NameCmd = {''};
                tblColumnTooltips.NameCmd = {''};
            case coder.profile.ResultColumns.MaxTurnaround
                % maximum touraround column
                tblColumnNames.MaxTurnaround = DAStudio.message('CoderProfile:ExecutionTime:MaxTurnaroundTime', timeUnit);
                tblColumnTooltips.MaxTurnaround = DAStudio.message('CoderProfile:ExecutionTime:MaxTurnaroundTimeTooltip', timeUnit);
            case coder.profile.ResultColumns.AverageTurnaround
                % average touraround column
                tblColumnNames.AverageTurnaround = DAStudio.message('CoderProfile:ExecutionTime:AvTurnaroundTime', timeUnit);
                tblColumnTooltips.AverageTurnaround = DAStudio.message('CoderProfile:ExecutionTime:AvTurnaroundTimeTooltip', timeUnit);
            case coder.profile.ResultColumns.MaxRealTimeExecution
                % maximum execution time column
                tblColumnNames.MaxExecution = DAStudio.message('CoderProfile:ExecutionTime:MaxExecutionTime', timeUnit);
                tblColumnTooltips.MaxExecution = DAStudio.message('CoderProfile:ExecutionTime:MaxRealTimeExecutionTimeTooltip', timeUnit);
            case coder.profile.ResultColumns.MaxNonRealTimeExecution
                % maximum execution time column
                tblColumnNames.MaxExecution = DAStudio.message('CoderProfile:ExecutionTime:MaxExecutionTime', timeUnit);
                tblColumnTooltips.MaxExecution = DAStudio.message('CoderProfile:ExecutionTime:MaxNonRealTimeExecutionTimeTooltip', timeUnit);
            case coder.profile.ResultColumns.AverageRealTimeExecution
                % average execution time column
                tblColumnNames.AverageExecution = DAStudio.message('CoderProfile:ExecutionTime:AvExecutionTime', timeUnit);
                tblColumnTooltips.AverageExecution = DAStudio.message('CoderProfile:ExecutionTime:AvRealTimeExecutionTimeTooltip', timeUnit);
            case coder.profile.ResultColumns.AverageNonRealTimeExecution
                % average execution time column
                tblColumnNames.AverageExecution = DAStudio.message('CoderProfile:ExecutionTime:AvExecutionTime', timeUnit);
                tblColumnTooltips.AverageExecution = DAStudio.message('CoderProfile:ExecutionTime:AvNonRealTimeExecutionTimeTooltip', timeUnit);
            case coder.profile.ResultColumns.MaxSelf
                % max self time column
                tblColumnNames.MaxSelf = DAStudio.message('CoderProfile:ExecutionTime:MaxSelfTime', timeUnit);
                tblColumnTooltips.MaxSelf = DAStudio.message('CoderProfile:ExecutionTime:MaxSelfTimeTooltip', timeUnit);
            case coder.profile.ResultColumns.AverageSelf
                % average self time column
                tblColumnNames.AverageSelf = DAStudio.message('CoderProfile:ExecutionTime:AvSelfTime', timeUnit);
                tblColumnTooltips.AverageSelf = DAStudio.message('CoderProfile:ExecutionTime:AvSelfTimeTooltip', timeUnit);
            case coder.profile.ResultColumns.Calls
                % num call column
                tblColumnNames.Calls = DAStudio.message('CoderProfile:ExecutionTime:ColumnHeadingCalls');
                tblColumnTooltips.Calls = {''};
            case coder.profile.ResultColumns.Links
                % sdi command column
                tblColumnNames.SDICmd = {''};
                tblColumnTooltips.SDICmd = {''};
                % histogram command column
                tblColumnNames.HistCmd = {''};
                tblColumnTooltips.HistCmd = {''};
                % membrane command column
                tblColumnNames.MembCmd = {''};
                tblColumnTooltips.MembCmd = {''};
        end
    end
end
