classdef BuildConfiguration < handle
    % BUILDCONFIGURATION Class representation of a build configuration.
    
    % Copyright 2011-2020 The MathWorks, Inc.
    
    properties
        Name = '';
        Description = '';
        Options
        File = [];
    end
    
    methods
        % -----------------------------------------------------------------
        % Constructor
        % -----------------------------------------------------------------
        function h = BuildConfiguration(name, options)
            h.Name = name;
            h.Options = coder.make.util.UnorderedList('coder.make.BuildItem');
            if nargin==2 
                if iscell(options) && mod(numel(options),2)==0
                    for i=1:2:numel(options)
                        h.addOption(options{i}, coder.make.BuildItem(options{i+1}));
                    end
                else
                    error(message('CoderFoundation:builder:BuildConfigurationConstruction_InvalidOptions'));
                end
            end
        end
        
        %------------------------------------------------------------------
        % Display
        %------------------------------------------------------------------
        function disp(h)
            h.info();
        end
        
        % -----------------------------------------------------------------
        % Check if an option exists in the list
        % -----------------------------------------------------------------
        function resp = isOption(h, option)
            resp = h.Options.isKey(option);
        end
        
        % -----------------------------------------------------------------
        % Add a new option
        % -----------------------------------------------------------------
        function addOption(h, option, value)
            h.Options.add(option, value);
        end
        
        % -----------------------------------------------------------------
        % Get the value of a specific option
        % -----------------------------------------------------------------
        function resp = getOption(h, option, returnType)
            resp = h.Options.getValue(option);
            if nargin == 3
                if strcmpi(returnType, 'object')
                    return;
                elseif strcmpi(returnType, 'macro')
                    resp = resp.getMacro();
                elseif strcmpi(returnType, 'value')
                    resp = resp.getValue();
                elseif strcmpi(returnType, 'token')
                    resp = resp.getToken();
                end
            end
        end
        
        % -----------------------------------------------------------------
        % Set the value of a specific option
        % -----------------------------------------------------------------
        function setOption(h, option, value)
            obj = h.getOption(option);
            if isa(value, 'coder.make.BuildItem')
                h.Options.setValue(option, value);
            else
                obj.setValue(value);
            end
        end
        
        % -----------------------------------------------------------------
        % Get the keys of all the options
        % -----------------------------------------------------------------
        function out = keys(h)
            out = h.Options.keys();
        end
        
        % -----------------------------------------------------------------
        % Get the values of all the options
        % -----------------------------------------------------------------
        function out = values(h)
            out = h.Options.values();
        end
        
        % -----------------------------------------------------------------
        % Save this build configuration to a MAT file
        % -----------------------------------------------------------------
        function save(h, filename)
            narginchk(1,2);
            if nargin==1
                if isempty(h.File)
                    warning(message('coder_compile:toolchain:CannotSaveToEmptyBuildConfigFileProperty', ...
                        h.Name));
                    return; % cannot save this build configuration
                else
                    filename = h.File;
                end
            elseif nargin==2
                if isempty(filename)
                    error(message('coder_compile:toolchain:CannotSaveToEmptyBuildConfigFile', ...
                        h.Name));
                else
                    h.File = filename;
                end
            end
            try
                bc = h;
                save(filename, 'bc');
            catch exc
                error(message('coder_compile:toolchain:ErrorSavingBuildConfigMATFile', ...
                    filename, exc.message));
            end
        end
        
        % -----------------------------------------------------------------
        % Get a cell array of options and their values
        % -----------------------------------------------------------------
        function resp = getOptionsAndValues(h, extra)
            narginchk(1,2);
            filterResp = (nargin==2 && strcmpi(extra, 'filter'));
            % keys = h.getBuildConfigOrderedOptions();
            keys = h.Options.keys();
            resp = [];
            for i=1:numel(keys)
                if filterResp && ~isempty(strfind(keys{i}, 'MEX'))
                    continue;
                else
                    resp{end+1} = keys{i}; %#ok<AGROW>
                    optionItem = getOptionsString(h.Options.getValue(keys{i}));
                    resp{end+1} = optionItem.getValue(); %#ok<AGROW>
                end
            end
            function optsValue = getOptionsString(str)
                optsValue = str;
                if iscell(optsValue)
                    temp = '';
                    for idx=1:numel(optsValue)
                        temp = [temp optsValue{idx} ' ']; %#ok<AGROW>
                    end
                    optsValue = temp;
                end
            end
        end
        
        % -----------------------------------------------------------------
        % SET Methods
        % -----------------------------------------------------------------
        
        function set.Name(h, value)
            value = convertStringsToChars(value);
            if ~isempty(value) && ~ischar(value)
                error(message('coder_compile:toolchain:PropertyMustBeString', 'Name', class(h)));
            end
            h.Name = value;
        end
        
        function set.Description(h, value)
            value = convertStringsToChars(value);
            if ~isempty(value) && ~ischar(value)
                error(message('coder_compile:toolchain:PropertyMustBeString', 'Description', class(h)));
            end
            h.Description = value;
        end
        
        function set.File(h, value)
            value = convertStringsToChars(value);
            if ~isempty(value) && ~ischar(value)
                error(message('coder_compile:toolchain:PropertyMustBeString', 'File', class(h)));
            end
            if ischar(value)
                % Check if this is a valid MAT file
                [~, ~, fext] = fileparts(value);
                if ~strcmpi(fext, '.mat')
                    error(message('coder_compile:toolchain:InvalidBuildConfigFileFormat', 'File'));
                end
            end
            h.File = value;
        end
        
        function set.Options(h, value)
            if ~isempty(value) && ~isa(value, 'coder.make.util.UnorderedList')
                error(message('coder_compile:toolchain:InvalidPropertyValue', 'Options', class(h), 'coder.make.util.UnorderedList'));
            end
            h.Options = value;
        end
        
        % -----------------------------------------------------------------
        % Get information about this build tool.
        %
        % INFO(H)   Display the build tool info.
        % X=INFO(H) Return build tool info in a buffer.
        % -----------------------------------------------------------------
        function varargout = info(h)
            nargoutchk(0,1);
            out_all = '';
            for i=1:numel(h)
                out = [h(i).getInfo(), newline];
                out_all = [out_all, out]; %#ok<AGROW>
                if nargout == 0
                    disp(out);
                end
            end
            if nargout == 1
                varargout{1} = out_all;
            end
        end
        
    end % public
    
    methods (Hidden)
        
        % -----------------------------------------------------------------
        % Clone contents of another object (copy values only, not handles)
        % -----------------------------------------------------------------
        function clone(h, src)
            h.copyBuildConfiguration(src);
        end
        
        % -----------------------------------------------------------------
        % Check if the contents are equal
        % -----------------------------------------------------------------
        function out = isEqualTo(h, src)
            out = false;
            if ~isequal(h, src)                
                return
            end
            % Double check the handle properties
            if ~h.Options.isEqualTo(src.Options)
                return;
            end
            out = true;
        end
        
        % -----------------------------------------------------------------
        % Internal-only access to private/public properties
        % -----------------------------------------------------------------
        function out = getProp(h, prop)
            out = h.(prop);
        end
        
        % -----------------------------------------------------------------
        % Display the specifications
        % -----------------------------------------------------------------
        function out = getInfo(h)
            comment = '#';
            hP = coder.make.internal.Printer(comment);
            
            % --------------------------------------
            % Start writing
            % --------------------------------------
            
            % Heading
            hP.printBorder();
            hP.printComment('Build Configuration : %s', h.Name)
            hP.printComment('Description         : %s', h.Description)
            hP.printBorder();
            % General
            printGeneralInfo();            
            % Options
            printOptions();            
            
            out = hP.Buffer;
            
            % --------------------------------------
            % Helpers
            % --------------------------------------
            
            function printGeneralInfo()
                if ~isempty(h.File)
                    hP.printBlankLine();
                    hP.println('File : ''%s''', strrep(h.File, '\', '\\'));
                end
            end
            
            % --------------------------------------
            % Utilities
            % --------------------------------------
            
            function printOptions()
                hP.printBlankLine();
                optionList = h.Options.values();
                for idx=1:numel(optionList)
                    options = optionList{idx};
                    macro = options.getMacro();
                    value = options.getValue();
                    if isempty(macro)
                        return
                    end
                    if ischar(value)
                        optsvalue = {value};
                    else
                        optsvalue = value;
                    end
                    % Compute the extra space we're adding to align the display
                    numExtraSpaces = (20-numel(macro));
                    if numExtraSpaces>0
                        spaceBuffer = repmat(' ', 1, numExtraSpaces);
                    else
                        spaceBuffer = '';
                    end
                    %
                    optsvalue(cellfun('isempty', optsvalue)) = []; % remove empty cell elements
                    numOptions = numel(optsvalue);
                    if numOptions==0
                        hP.println('%s%s =', macro, spaceBuffer);
                    else
                        hP.printMultiLine(macro, optsvalue, spaceBuffer);
                    end
                end
            end
            
        end % getInfo

    end % hidden
    
    methods (Access = private)
        
        % -----------------------------------------------------------------
        % Copy or clone a specified build configuration
        % -----------------------------------------------------------------
        function copyBuildConfiguration(h, src)
            if ~strcmp(class(h), class(src))
                error(message('coder_compile:toolchain:CannotCloneClassMismatch', ...
                    class(src), class(h)));
            end
            h.Name         = src.Name;
            h.Description  = src.Description;
            h.File         = src.File;                     
            coder.make.internal.copyListProperty('Options', src, h);
        end
        
    end % private
end

% LocalWords:  toolchain bc
