classdef ExecutionTimeSection < handle & matlab.mixin.Heterogeneous
%EXECUTIONTIMESECTION execution profiling results for a section of code
    
%   Copyright 2010-2015 The MathWorks, Inc.
    
    properties (Hidden=true, Access=protected)
        TraceInfo;
        SectionNumber;
        TimerData;
    end
    
    properties (GetAccess=public, SetAccess=private)
        Name;
        Number;
        ExecutionTimeInTicks;
        SelfTimeInTicks;
        TurnaroundTimeInTicks;
        TotalExecutionTimeInTicks;
        TotalSelfTimeInTicks;
        TotalTurnaroundTimeInTicks;
        MaximumExecutionTimeInTicks;
        MaximumExecutionTimeCallNum;
        MaximumSelfTimeInTicks;
        MaximumSelfTimeCallNum;
        MaximumTurnaroundTimeInTicks;
        MaximumTurnaroundTimeCallNum;
        NumCalls;
        ExecutionTimeInSeconds;
        Time;
    end        
    
    properties (Access=private)
        IdUint64;
    end        
    
    methods
        
        function name = get.Time(this)
            name = this.TimerData.getSimulationTimeForNode(this.SectionNumber);
        end
        
        function timeVals =  get.ExecutionTimeInSeconds(this)
            lTimerTicksPerSecond = this.TimerData.getTimerTicksPerSecond;
            if ~isempty(lTimerTicksPerSecond)
                timeVals = double(this.getTicks)/double(lTimerTicksPerSecond);
            else
                DAStudio.error('CoderProfile:ExecutionTime:NoTimerTicksPerSecond');
            end
        end            
        
        function name = get.Name(this)
            name = this.getName;
        end
        
        function numCalls = get.NumCalls(this)
            numCalls = this.TimerData.getNumCallsForSection(this.SectionNumber);            
        end
        
        function number = get.Number(this)
            number = this.getSectionNumber;            
        end
        
        function ticks =  get.ExecutionTimeInTicks(this)
            ticks = this.getTicks;
        end            
        
        function selfTicks = get.SelfTimeInTicks(this)
            [~, selfTicks] = this.getTicks;
        end
        
        function turnaroundTicks = get.TurnaroundTimeInTicks(this)
            [~, ~, turnaroundTicks] = this.getTicks;
        end
    
        function ticksTotal =  get.TotalExecutionTimeInTicks(this)
            ticksTotal = this.TimerData...
                .getTotalExecutionTicksForSection(this.SectionNumber);          
        end            
        
        function selfTicksTotal = get.TotalSelfTimeInTicks(this)
            selfTicksTotal = this.TimerData...
                .getTotalSelfTicksForSection(this.SectionNumber);                  
        end
        
        function turnaroundTicksTotal = get.TotalTurnaroundTimeInTicks(this)
            turnaroundTicksTotal = this.TimerData...
                .getTotalTurnaroundTicksForSection(this.SectionNumber);          
        end
    
        function ticksMax =  get.MaximumExecutionTimeInTicks(this)
            ticksMax = this.getMaxTicks;
        end            
        
        function selfTicksMax = get.MaximumSelfTimeInTicks(this)
            selfTicksMax = this.getMaxSelfTicks;
        end
        
        function turnaroundTicksMax = get.MaximumTurnaroundTimeInTicks(this)
            turnaroundTicksMax = this.getMaxTurnaroundTicks;
        end
    
        function callNum =  get.MaximumExecutionTimeCallNum(this)
            [~, callNum] = this.getMaxTicks;
        end            
        
        function callNum = get.MaximumSelfTimeCallNum(this)
            [~, callNum] = this.getMaxSelfTicks;
        end
        
        function callNum = get.MaximumTurnaroundTimeCallNum(this)
            [~, callNum] = this.getMaxTurnaroundTicks;
        end
    
    end
    
    
    methods (Access=private)  
        
        function [lExecutionTimes, lSectionSelfTimes,...
                  lTurnaroundTimes] = ...
                getSectionTimerValues(this)
            
            % Throw an error if only summary data is available
            this.TimerData.errorIfSummaryDataOnly;
                
            lTimerStartValues = this.TimerData.getTimerStartValuesForNode(this.SectionNumber);
            lTimerEndValues =  this.TimerData.getTimerEndValuesForNode(this.SectionNumber);
            lSectionSelfTimes = this.TimerData.getSelfTimeValuesForNode(this.SectionNumber);
            lExecutionTimes = this.TimerData.getExecutionTimeValuesForNode(this.SectionNumber);
            lTurnaroundTimes = lTimerEndValues - lTimerStartValues;
            lExecutionTimes=lExecutionTimes(:)';
            lTurnaroundTimes=lTurnaroundTimes(:)';
            lSectionSelfTimes=lSectionSelfTimes(:)';
                        
        end
        
    end

    methods (Access=public, Hidden=true)
        
        function [numInconsistencies, firstInconsistency] =  getAnalysisInconsistencies(this)
            [numInconsistencies, firstInconsistency] = ...
                this.TimerData.getAnalysisInconsistencies(this.SectionNumber);
        end       
        
        function [idSection, idParent] = getIdUint64(this)
            
            % Get the 64-bit identifier for the main execution profile object
            idParent = this.TimerData.getIdUint64;
            
            % If data streaming was enabled for the profile that data will already
            % be in SDI. In this case task-level unique identifiers will have been 
            % constructed in:
            % matlab/toolbox/coder/profile/src_internal/profiling/ProfilingData.cpp
            
            % If streaming is not enabled these IDs will be initialised to zero
            % and we need to generate ids here. As streaming is only supported
            % at task-level we will also need to generate ids for the function-level
            % profile sections

            % Get the ProfilingData.cpp IDs
            uniqueIds = this.TimerData.UniqueIds;
            idSection = uniqueIds(this.SectionNumber + 1);
            
            % If section id is 0 or this is not a task we need to generate the
            % unique id.
            if idSection == 0 || ~this.TraceInfo.isTask;
                % We need an identifier that is unique for this section number
                offset = uint64(this.SectionNumber);
            
                % Since integer arithmetic in MATLAB does not wrap, we avoid the (very remote)
                % possibility of saturation by adding or subtracting the offset
                if idParent < uint64(2^63)
                    idSection = idParent + offset;
                else
                    idSection = idParent - offset;
                end
            end
        end
        
        function sigName = getSignalNameForSDI(this)
            
            if ~isempty(this.TraceInfo)
                sigName = this.TraceInfo.getCodeSectionName;
            else
                sigName = this.getName;
            end
            sigName = sprintf('%d:%s', this.Number, sigName);        
        end
        
        function number = getNodeNumber(this)
            
        % Don't filter out the hidden (i.e. preemption) sections

            number = this.SectionNumber;
            
        end        
                
        function isPreemption = isPreemptionSection(~)
            isPreemption = false;
        end
        
        
        function [lTimerStartValues, lTimerEndValues, lCoreNumStart, lCoreNumEnd] = ...
            getData(this)
            % This method is undocumented and is likely to change from one release to the
            % next
            
            if strcmp(this.TimerData.getCodeProfilingSaveOptions, 'SummaryOnly')
                
                this.reportSummaryDataOnly;
                
            else
                lTimerStartValues = this.TimerData.getTimerStartValuesForNode(this.SectionNumber);
                lTimerEndValues =  this.TimerData.getTimerStartValuesForNode(this.SectionNumber);
                lCoreNumStart = this.TimerData.getCoreNumStartForNode(this.SectionNumber);
                lCoreNumEnd = this.TimerData.getCoreNumEndForNode(this.SectionNumber);
            end
                        
        end
        
        
        function  lTraceInfo = getTraceInfo(this)
            lTraceInfo = this.TraceInfo;
        end
        
        
        function [callDepth, taskAncestor] = getCallDepth(this)
            
            if this.SectionNumber==0
                callDepth=0;
                taskAncestor = [];
            else
                [parentCallDepth, parentTaskAncestor]=this.getParent.getCallDepth;
                callDepth = parentCallDepth + 1;
            
                if this.getParent.Number==0
                    taskAncestor = this.Number;
                else
                    taskAncestor = parentTaskAncestor;
                end
            end

            
        end
        
        
        function parent = getParent(this)
            
            parentNumber = this.TimerData.getParentNumber(this.SectionNumber);
            parent = this.TimerData.getSectionProfile(parentNumber);
            
        end
        
        function children = profGetChildren(this)
            
            childSectionNumbers = this.TimerData.getChildNumbers ...
                (this.SectionNumber);
            
            numChildren = length(childSectionNumbers);

            % Default initialization
            children = this(ones(1, numChildren));
            
            for i = 1:numChildren
                children(i) = this.TimerData.getSectionProfile... 
                    (childSectionNumbers(i));
            end
                
        end
        
        function children = getChildren(this)
        % This undocumented, hidden method will be removed: do not use it. Instead use
        % the replacement profGetChildren that returns an array instead of a
        % cell array.
            DAStudio.warning('CoderProfile:ExecutionTime:ExecTimeSectionGetChildrenWarn');
            
            childSectionNumbers = this.TimerData.getChildNumbers ...
                (this.SectionNumber);
            
            numChildren = length(childSectionNumbers);
            children =  cell(numChildren, 1);
            
            for i = 1:numChildren
                children{i} = this.TimerData.getSectionProfile...
                    (childSectionNumbers(i));
            end
                
        end
        
    end
    
    methods (Abstract, Access = public, Hidden=true)
        getName(this);
    end
    
    
    methods (Hidden=true, Access=public)
        
        function number = getSectionNumber(this)

        % We filter out the hidden (i.e. preemption) sections
            number = this.TimerData.getVisibleSectionNumber(this.SectionNumber);
            
        end
        
    end        
    
    
    methods (Hidden=true, Access=public)
            
        function numCalls = getNumCalls(this)
            numCalls = this.NumCalls;
        end
        
        
        function [timeVals, sourcePropName] = getExecTimeDataForSDI(this)
        % Return time in seconds if the timer is calibrated, otherwise return time in
        % clock ticks
            if isempty(this.TimerData.getTimerTicksPerSecond)
                sourcePropName = 'ExecutionTimeInTicks';
            else
                sourcePropName = 'ExecutionTimeInSeconds';
            end
            timeVals = eval(['this.' sourcePropName]);
        end

        function data = getTimes(this)
            data = [];
            
            lSectionExecutionTimes = this.ExecutionTimeInTicks;

            lTimerTicksPerSecond = this.TimerData.getTimerTicksPerSecond;
            if ~isempty(lTimerTicksPerSecond)
                data = lSectionExecutionTimes(:)';
                data = double(double(data)/double(lTimerTicksPerSecond));
            end
        end
        
        function lTimerTicksPerSecond = getTimerTicksPerSecond(this)
            lTimerTicksPerSecond = this.TimerData.getTimerTicksPerSecond;
        end

        function [ticks, selfTicks, turnaroundTicks] = getTicks(this)
            [ticks, selfTicks, turnaroundTicks] = ...
                getSectionTimerValues(this);
        end
        
        function ticksTotal = getTotalTicks(this)
            ticksTotal = this.TotalExecutionTimeInTicks;
        end
        
        function selfTicksTotal = getTotalSelfTicks(this)
            selfTicksTotal = this.TotalSelfTimeInTicks;
        end
        
        function turnaroundTicksTotal = getTotalTurnaroundTicks(this)
            turnaroundTicksTotal = this.TotalTurnaroundTimeInTicks;
        end

        function [ticksMax, callNum] = getMaxTicks(this)
            [ticksMax, callNum] = this.TimerData...
                .getMaxExecutionTicksForSection(this.SectionNumber);
        end
                
        function [selfTicksMax, callNum] = getMaxSelfTicks(this)
            [selfTicksMax, callNum] = this.TimerData...
                .getMaxSelfTicksForSection(this.SectionNumber);
        end
                
        function [turnaroundTicksMax, callNum] = getMaxTurnaroundTicks(this)
            [turnaroundTicksMax, callNum] = this.TimerData...
                .getMaxTurnaroundTicksForSection(this.SectionNumber);
        end

        
        function this = ExecutionTimeSection...
                (lTimerData,...
                 lSectionNumber,...
                 lTraceInfo)

            this.SectionNumber = lSectionNumber;
            this.TimerData  = lTimerData;
            this.TraceInfo = lTraceInfo;
        end
    end
end
