%ContextGuard utility to handle optimization in coverage
% Put SlCov.ContextGuard(modelName)in code where cvload happens for the same
% model.It will help to optimize updateModelHandle.The optimization will turn of with delete.


%   Copyright 2020 The MathWorks, Inc.

classdef ContextGuard < handle
    properties (Hidden, Access = private)
        modelNames = []
    end
    methods
        %====================
        function  this = ContextGuard(modelNames, guardType)
            if nargin < 2
               %guarantee that model will not change 
               guardType  =  SlCov.ContextGuardType.ModelUnchanged;
            end
            if ~iscell(modelNames)
                modelNames = {modelNames};
            end
            for idx = 1: numel(modelNames) 
                addGuard(this, modelNames{idx}, guardType);
            end
        end
        %====================        
        function  addGuard(this, modelName, guardType)
            fidx = []; 
            if ~isempty(this.modelNames)
                fidx = find(this.modelNames == string(modelName)); 
            end
            if ~isempty(fidx)
                SlCov.ContextGuard.guardMap('remove', this.modelNames(fidx));
            else
                this.modelNames = [this.modelNames {modelName}];
            end
            SlCov.ContextGuard.guardMap('init', modelName, guardType);
        end
       %====================
        function delete(this)
            for idx = 1 : numel(this.modelNames)
                SlCov.ContextGuard.guardMap('remove', this.modelNames{idx});
            end
        end
     end
    methods (Static, Hidden)
        %====================
        function res = getAllData(guardType)
            if nargin < 1
               guardType  =  [];
            end
            res = SlCov.ContextGuard.guardMap('getAll', [], guardType);
        end
        
        %methods for SlCov.ContextGuardType.ModelUnchanged 
        %====================
        function res = isUpdatedRoot(modelName, rootPath)
            res = false;
            guardData = SlCov.ContextGuard.guardMap('get', modelName, SlCov.ContextGuardType.ModelUnchanged);
            if ~isempty(guardData) && isfield(guardData, 'data') && ~isempty(guardData.data)
                res = any(guardData.data == string(rootPath));
            end
        end
       %====================
        function res = isModelUnchanged(modelName)
            guardData = SlCov.ContextGuard.guardMap('get', modelName, SlCov.ContextGuardType.ModelUnchanged);
            res = ~isempty(guardData) && isfield(guardData, 'data') && ~isempty(guardData.data);
        end

        %====================
        function addUpdatedRoot(modelName, rootPath)
            SlCov.ContextGuard.guardMap('set', modelName, SlCov.ContextGuardType.ModelUnchanged, rootPath);
        end
    end
    
    methods (Static, Hidden,  Access = private)
        %====================
        function res = guardMap(cmd, modelName, guardType,  data)
            persistent guardData;
            res = [];
            if strcmpi(cmd, 'set')
              % if the model is not in the map it wasn't called as a context
              % guard
              if isempty(guardData)
                  return;
              else
                  dataIdx = find({guardData.modelName} == string(modelName) & ...
                                 [guardData.type] == guardType);
                  if ~isempty(dataIdx)
                    guardData(dataIdx).data{end + 1} = data;
                  end
              end
            elseif strcmpi(cmd, 'get')
                if ~isempty(guardData)
                  res  = guardData({guardData.modelName} == string(modelName));
                end
            elseif strcmpi(cmd, 'init')
                addNew = true;
                if ~isempty(guardData)
                  dataIdx = find({guardData.modelName} == string(modelName));
                  if ~isempty(dataIdx)
                      dataIdx = find([guardData(dataIdx).type] == guardType);
                      if ~isempty(dataIdx)
                          guardData(dataIdx).data = {};
                          addNew = false;
                      end
                  end
                end
                if addNew 
                    guardData = [guardData struct('modelName', modelName, 'type', guardType, 'data', [])];
                end
            elseif strcmpi(cmd, 'remove')
              if ~isempty(guardData)
                   guardData({guardData.modelName} == string(modelName)) = [];
              end
            elseif strcmpi(cmd, 'getAll')
                if ~isempty(guardType)
                    res =  guardData([guardData.type] == guardType);
                else
                    res = guardData;
                end

            end
        end
    end
end
