classdef (Sealed) OverheadProbeComponentRegistry  < coder.profile.ProbeComponentRegistry
%   OverheadProbeComponentRegistry is the probe registry for the overhead
%   beanchmark.
%   It collects all the OVERHEAD_PROBE probes used to upload the estimated
%   overheads for the profiling API detected during the benchmark.
%   This class is not instanciated when the overhead is hand coded

%   Copyright 2018 The MathWorks, Inc.

    properties (Constant)
        TaskPostfix = 'Task';
        FunctionPostfix = 'Function';
        GenericPostfix = 'Generic';
        FreezePostfix = 'Freeze';
    end
    
    methods
        function this = OverheadProbeComponentRegistry(componentName,...
                                                   componentRef,...
                                                   targetWordSize,...
                                                   buildDir,...
                                                   maxIdLength,...
                                                   codeFolderRelative)
            if nargin < 6
                codeFolderRelative = '';
            end
            this = this@coder.profile.ProbeComponentRegistry(componentName,...
                                                             componentRef,...
                                                             targetWordSize,...
                                                             buildDir,...
                                                             maxIdLength,...
                                                             codeFolderRelative);
            this.registerProbeType(coder_profile_ProbeType.OVERHEAD_PROBE);
        end
        
        
        function id = requestFunctionOverheadProbe(this, lFullComponentPath)
            id = this.createOverheadProbe(lFullComponentPath, 'function');
        end
        
        function id = requestTaskOverheadProbe(this, lFullComponentPath)
            id = this.createOverheadProbe(lFullComponentPath, 'task');
        end

        function id = requestTimerOverheadProbe(this, lFullComponentPath)
            id = this.createOverheadProbe(lFullComponentPath, 'timestamp');
        end

        function id = requestFreezeOverheadProbe(this, lFullComponentPath)
            id = this.createOverheadProbe(lFullComponentPath, 'freezing');
        end

        function id = requestDummyOverheadProbe(this, lFullComponentPath)
            id = this.createOverheadProbe(lFullComponentPath, 'dummy');
        end

        function probeRealInstrFcns = instrumentationFnc(this, lProbeType, lSectionId)
            lProbeTypeName = coder_profile_ProbeType.toString(uint32(lProbeType));
            probeRealInstr =...
                this.getProbeInstrumentationFcn(...
                lProbeType,...
                lSectionId);
            
            probeRealInstrFcns =...
                arrayfun(@(x) strcat(x, this.FunctionPostfix),...
                probeRealInstr);
            probeRealInstrTasks =...
                arrayfun(@(x) strcat(x, this.TaskPostfix),...
                probeRealInstr);
            probeRealInstrFreeze =...
                arrayfun(@(x) strcat(x, this.FreezePostfix),...
                probeRealInstr);
            probeRealInstrGeneric =...
                arrayfun(@(x) strcat(x, this.GenericPostfix),...
                probeRealInstr);
            probeRealInstr = [probeRealInstrFcns(:)', ...
                              probeRealInstrTasks(:)', ...
                              probeRealInstrFreeze(:)', ...
                              probeRealInstrGeneric(:)'];
            
            probeInstrFncs =...
                arrayfun(@(x) strcat(x, '_estimate'),...
                probeRealInstr);
            
            % check to see if we already have this name for the probe type
            if this.ProbeCalls.isKey(lProbeTypeName)
                % safety check
                vals = this.ProbeCalls(lProbeTypeName);
                assert(isequal(vals{1}, probeInstrFncs),...
                    'The wrapper probe calls should be same across the same probe type');
                assert(isequal(vals{2}, probeRealInstr),...
                    'The real instrumentation probe calls should be same across the same probe type');
            else
                vals = cell(1,2);
                vals{1} = probeInstrFncs;
                vals{2} = probeRealInstr;
                this.ProbeCalls(lProbeTypeName) = vals;
            end
        end
    end
    
    methods (Access = private)
        function id = createOverheadProbe(this, lFullComponentPath, overheadType)
            if isempty(this.ComponentRef)
                this.ComponentRef = lFullComponentPath;
            else
                assert(strcmp(this.ComponentRef, lFullComponentPath),...
                    'The component path must remain consistent');
            end
            
            id = this.requestSectionId(...
                coder_profile_ProbeType.OVERHEAD_PROBE,...
                overheadType);
            
            % fill in local SectionIds
            probeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.OVERHEAD_PROBE);
            this.addInternalSectionId(probeTypeName, id);
            
            this.checkMaxIdLimit(probeTypeName);
            
            % add probe to ProbeCalls
            this.instrumentationFnc(coder_profile_ProbeType.OVERHEAD_PROBE, id);
        end
        
    end
end

