function export(modelName, varargin)
% EXPORT(MODELNAME, 'PARAMETER1', VALUE1,...)
% export ASAP2(a2l) file for a Simulink model MODELNAME
%
%
%   modelName   Name of Simulink model.
%   varargin    Optional arguments (key,value)
%
% Examples:
% Export a2l file for Simulink model with name myModel.
%    coder.asap2.export('myModel');

% Export a2l file at location /share/calibration for Simulink model myModel.
%    coder.asap2.export('myModel', 'Folder', '/share/calibration');

% Export a2l file with address fields populated for Simulink model with
% name myModel and debug mapping information from file myApp.elf.
% NOTE: File myApp.elf should contain dwarf section.
%    coder.asap2.export('myModel', 'MapFile', 'myApp.elf');
%
% Export a2l file without comments for Simulink model myModel.
%    coder.asap2.export('myModel', 'Comments', false);
% Export a2l file with comments for Simulink model myModel(If Comments not specified
%    as argument,by default generated a2l file will have comments).
%    coder.asap2.export('myModel', 'Comments', true);
%
% Export a2l file with customization for Simulink model myModel(If CustomizationObject not specified
%    as argument,generated a2l file would have the default sections).
%    coder.asap2.export('myModel', 'CustomizationObject', obj);
%
% Export a2l file without XCP information even when the ExternalMode is
%    turned on(By default XCP Info is generated when ExternalMode is on )
%    coder.asap2.export('myModel', 'GenerateXCPInfo', true);
%
% Export a2l with a specified name myA2l
%    (By default the a2l file name would be the same as model name)
%    coder.asap2.export('myModel', 'Filename', 'myA2l');
%
% Export a2l with Indentation
%    coder.asap2.export('myModel', 'IndentFile', true);
%
% Export a2l with IncludeAllRecordLayouts
%    coder.asap2.export('myModel', 'IncludeAllRecordLayouts', true);
%   Copyright 2020-2021 The MathWorks, Inc.

% checkout simulink coder license
[checkoutSuccess, errmsg] = license('checkout', 'Real-Time_Workshop');
if ~checkoutSuccess
    DAStudio.error('RTW:asap2:RequiresSimulinkCoder', errmsg);
end

if nargin > 0
    modelName = convertStringsToChars(modelName);
else
    DAStudio.error('RTW:asap2:RequiresModelName');
end

% Create an instance of the inputParser class to handle varargin
argParser = inputParser;

% Add arguments to the schema
argParser.addParameter('MapFile', '', @(x)(ischar(x) || isStringScalar(x)));
argParser.addParameter('Folder', '', @(x)(ischar(x) || isStringScalar(x)));
argParser.addParameter('ModelClassInstanceName', '', @(x)(ischar(x) || isStringScalar(x)));
argParser.addParameter('GenerateXCPInfo',true, @(x)(isa(x,'logical')));
argParser.addParameter('Comments', true, @(x)(isa(x,'logical')));
argParser.addParameter('Version', '', @(x)any(validatestring(x, ...
    coder.asap2.getASAP2SupportedVersions())));
argParser.addParameter('CustomizationObject', '', @(x)(isobject(x)));
argParser.addParameter('Filename', '', @(x)(ischar(x) || isStringScalar(x)));
argParser.addParameter('IndentFile', false, @(x)(isa(x,'logical')));
argParser.addParameter('CustomizeGroupsBy', '', @(x)(coder.asap2.isValidGroupType(x)));
argParser.addParameter('IncludeAllRecordLayouts', false, @(x)(isa(x,'logical')));
argParser.parse(varargin{:});

% Check that the model is loaded in memory
systems = find_system('type', 'block_diagram', 'name', modelName);
if isempty(systems)
    DAStudio.error('Simulink:components:discmdlMdlNotFound', modelName);
end
% GRT/ERT Target with C++ is not supported
if Simulink.CodeMapping.isCppClassInterface(modelName) && ~Simulink.CodeMapping.isAutosarAdaptiveSTF(modelName)
    DAStudio.error('RTW:asap2:IncompatibleTargetLanguage');
end
% Is it an appropriate target?
isCompliant = isSupportedTarget(modelName);
if ~isCompliant
    DAStudio.error('RTW:asap2:a2lExportCompliant');
end

try
    % Check for build directory existence
    buildDir = RTW.getBuildDir(modelName);
    if exist(buildDir.BuildDirectory, 'dir') ~= 7
        DAStudio.error('RTW:asap2:ModelIsNotBuilt', modelName);
    end
    % Check for code descriptor
    if exist(fullfile(buildDir.BuildDirectory,'codedescriptor.dmr'),'file') ~= 2
        DAStudio.error('RTW:asap2:missingCodeDescriptor', buildDir.BuildDirectory, 'ASAP2');
    end
    % Check for MapFile existence
    if ~isempty(argParser.Results.MapFile)
        if ~exist(argParser.Results.MapFile,'file')
            DAStudio.error('RTW:asap2:UnableFindFile', argParser.Results.MapFile);
        end
    end
    % Check for Folder existence
    if ~isempty(argParser.Results.Folder)
        folderPath = convertStringsToChars(argParser.Results.Folder);
        % When backward slash is provided instead of forward
        % slash in Linux , the directory creation fails.
        % Hence replacing the backward slash with forward slash.
        if ~ispc
            folderPath = replace(folderPath, '\', '/');
        end
        if exist(folderPath, 'dir') ~= 7
            mkdir(folderPath);
        end
        exportLocation = folderPath;
    else
        exportLocation = buildDir.BuildDirectory;
    end
    % Check for FileName
    noFileSeps = isempty(regexp(argParser.Results.Filename,'[\\/]','once'));
    if ~(noFileSeps)
        DAStudio.error('SimulinkCoderApp:ui:uiInvalidFileName');
    end
    if ~isempty(argParser.Results.Filename)
        a2lFilePath = fullfile(exportLocation, ...
            [convertStringsToChars(argParser.Results.Filename) '.a2l']);
    else
        a2lFilePath = fullfile(exportLocation,[modelName '.a2l']);
    end
    includeComments = argParser.Results.Comments;
    includeXCPInfo = argParser.Results.GenerateXCPInfo;
    usingUserCustomizationObj = argParser.Results.CustomizationObject;
    customizeGroups = argParser.Results.CustomizeGroupsBy;
    includeAllRecordLayouts = argParser.Results.IncludeAllRecordLayouts;
    % Get the model version from model info
    modelVersion = get_param(modelName, 'ModelVersion');
    modelClassInstanceName = convertStringsToChars(argParser.Results.ModelClassInstanceName);
    activeConfigSet = getActiveConfigSet(modelName);
    % Create an object for coder.internal.asap2.Generator with specified
    % ASAP2 version.
    obj = coder.internal.asap2.Generator(modelName, convertStringsToChars(argParser.Results.Version));
    info = obj.generate(a2lFilePath, modelClassInstanceName, customizeGroups, getSTF(modelName), modelVersion,includeComments,usingUserCustomizationObj, includeAllRecordLayouts);
    
    % Add xcp slave config and a2ml section to asap2 file if xcp support is
    % enabled for model.
    if Simulink.CodeMapping.isAutosarAdaptiveSTF(modelName)
        % Do the address replacement using provided map file
        if ~isempty(argParser.Results.MapFile)
            rtw.asap2SetAddress(a2lFilePath, argParser.Results.MapFile);
        end
        coder.internal.asap2.AddXCPInfoToAdaptiveASAP2(modelName, a2lFilePath, argParser);
    elseif Simulink.CodeMapping.isSLRealTimeCompliant(modelName)
        % For SLRT following function call does ECU address replacement,
        % Segment Size replacement and add IF_DATA section
        coder.internal.xcp.a2l.slrealtime.addXCPInfoToASAP2(modelName,...
            a2lFilePath, convertStringsToChars(argParser.Results.MapFile));
    else
        if ~isempty(argParser.Results.MapFile)
            % Do the address replacement using provided map file
            rtw.asap2SetAddress(a2lFilePath, argParser.Results.MapFile);
        end
        if coder.internal.xcp.isXCPTarget(activeConfigSet)&& includeXCPInfo
            %if xcp support is enabled for model add the A2ML section
            coder.internal.asap2.AddXCPInfoToERTGRT(modelName, a2lFilePath);
        end
    end
    if argParser.Results.IndentFile
       coder.internal.asap2.addIndentationToFile(a2lFilePath,exportLocation);
    end
    if ~isempty(info)
        fprintf("%s",message('RTW:asap2:NoElementInASAP2',strjoin(info,', ')));
    end
catch Me
    rethrow(Me);
end
end

function isValidTarget = isSupportedTarget(modelName)
% this function checks whether the STF is set to
% appropriate targets.
[~, mappingType] = Simulink.CodeMapping.getCurrentMapping(modelName);
isValidTarget = strcmp(mappingType, 'CoderDictionary') || ...
    strcmp(mappingType, 'SimulinkCoderCTarget') || strcmp(mappingType, 'AutosarTargetCPP');
end

function res = getSTF(model)
% This function finds STF. It checks the config spec recursively.
cs = getActiveConfigSet(model);
while cs.isa('Simulink.ConfigSetRef')
    if ~strcmpi(cs.SourceResolved, 'on')
        res = false;
        return;
    end
    cs = cs.getRefConfigSet();
end
res = get_param(model, 'SystemTargetFile');
end
% LocalWords:   MODELNAME pdb ASAP2 discmdl xcp SLRT slrealtime STF CTarget codedescriptor dmr
