classdef BuildToolLangStandardMap
    % BUILDTOOLLANGSTANDARDMAP stores maps that 
    % map Target Language Standard (e.g., 'C99 (ISO)')
    % to an level index or coresponding compiler options. 
    % The level is used to check if the model's TargetLangStandard
    % is compatible with the BuildTool's TargetLangStandard before codegen. 
    % If model has higher level than the supported one, then throw an
    % error.
    % The compiler options will be used in the makefile to compile the code.
    
    %   Copyright 2016 The MathWorks, Inc.
    
    properties(Access=private)
        % Container map that maps the Supported Language Standard to a
        % level index. Notice the map for C and C++ will be different, so
        % essentially need two maps
        LevelMapC
        LevelMapCPP
    end
    
    properties(SetAccess=private, GetAccess=public)
        % Container map that maps the Supported Language Standard to the
        % coresponding compiler options. 
        OptionMap
    end
    

    methods (Access = {?coder.make.BuildToolStandard})
        % -----------------------------------------------------------------
        % Constructor
        % -----------------------------------------------------------------
        function h = BuildToolLangStandardMap
                       
            keySetC =   {'C89/C90 (ANSI)', 'C99 (ISO)', '*'};
            %set the value of '*' to be very high to ensure that when
            %we added new supported standards, '*' will still have
            %highest level
            %If new standard is added later, it should be added to both
            %LevelMapC/LevelMapCPP and OptionMap.
            valueSetC = [0, 1, 99];
            h.LevelMapC = containers.Map(keySetC,valueSetC);
            
            keySetCPP =   {'C++03 (ISO)', 'C++11 (ISO)', '*'};
            valueSetCPP = [0, 1, 99];
            h.LevelMapCPP = containers.Map(keySetCPP,valueSetCPP);
            
            % map that maps language standards to complier options. It is
            % used to expand macros 'C_STANDARD_OPTS' and
            % 'CPP_STANDARD_OPTS'.
            % '*' is used when one wants to set the compiler options the
            % same for all language standards.
            keySet =   {'C89/C90 (ANSI)', 'C99 (ISO)', 'GNU C99 extensions', 'C++03 (ISO)', 'C++11 (ISO)', '*'};
            optionSetDefault = {'', '', '', '', '', ''}; 
            h.OptionMap = containers.Map(keySet,optionSetDefault);
        end
            
    end
    
    methods (Static)
        
        %get the level of the standard, change to static 
        %there are two levels, langlv represents the language level (C or C++)
        %stdlv represents the standard level (e.g.,  C99 or C++03)
        function [langLevel, stdLevel] = getStandardLevel(languageStandard)
            %This map should be persistent, which will only be created once
            %because only properties LevelMapC and LevelMapCPP are used in
            %this function. LevelMapC and LevelMapCPP will not change.
            persistent buildToolMap;
            if (isempty(buildToolMap))
                buildToolMap = coder.make.BuildToolLangStandardMap;
            end
            if isKey(buildToolMap.LevelMapC,languageStandard)
               langLevel = 0;
               stdLevel = buildToolMap.LevelMapC(languageStandard);
            else
               langLevel = 1;
               stdLevel = buildToolMap.LevelMapCPP(languageStandard);
            end
        end
        
        %check if the targetStandard is supported by the supportedStandard
        %this is used before codegen to check if the Standard of model is 
        %supported by the ToolChain's (buildTool's) supported standard
        function isSupported = isSupportedBy(targetStandard, supportedStandard)
            if strcmp(supportedStandard,'*')
                isSupported = true;
            else
                [targetLangLevel, targetStdLevel] = ...
                    coder.make.BuildToolLangStandardMap.getStandardLevel(targetStandard);
                [supportedLangLevel, supportedStdLevel] = ...
                    coder.make.BuildToolLangStandardMap.getStandardLevel(supportedStandard);
                % they are in the same language map
                % Or target is from C map, supported is from CPP map
                % e.g., target = C11, supported = C++11
                if (targetLangLevel <= supportedLangLevel)
                    isSupported = (targetStdLevel <= supportedStdLevel);
                % target is from Cpp map, supported from C map
                % e.g., target = C++03, supported = C11
                % always return false (need to verify this)
                elseif (targetLangLevel > supportedLangLevel)
                    isSupported = false;
                end
            end
        end
        
    end
    
    
    methods           
        
        function options = getCompilerOptions(h,languageStandard)
            %get compiler options according to the standard
            if isKey(h.OptionMap, languageStandard)
                options = h.OptionMap(languageStandard);
            else
                options = '';
            end
        end
        
    end
    
end


