function docker(varargin)
%COMPILER.PACKAGE.DOCKER  Create a docker image for files generated by the MATLAB Compiler product
%
%    COMPILER.PACKAGE.DOCKER(RESULTS) creates a docker image for files
%        generated by the MATLAB Compiler using the compiler.build.Results
%        object RESULTS. The RESULTS object is created by a
%        compiler.build.standaloneApplication function.
%
%    COMPILER.PACKAGE.DOCKER(RESULTS,NAME,VALUE) creates a docker image
%        for files generated by the MATLAB Compiler using the
%        compiler.build.Results object RESULTS. The image can be
%        customized using optional name-value pairs.
%
%    COMPILER.PACKAGE.DOCKER(RESULTS,'Options',OPTS) creates a docker
%        image for files generated by the MATLAB Compiler using the
%        compiler.build.Results object RESULTS. Image options are specified
%        by a DockerOptions object OPTS. If you use a DockerOptions object,
%        you cannot specify any other options using name-value pairs.
%
%    COMPILER.PACKAGE.DOCKER(FILES,FILEPATH,'ImageName',IMAGENAME) creates
%        a docker image for files generated by the MATLAB Compiler. The
%        docker image name is specified by IMAGENAME.
%
%    COMPILER.PACKAGE.DOCKER(FILES,FILEPATH,'ImageName',IMAGENAME,NAME,VALUE)
%        creates a docker image for files generated by the MATLAB Compiler.
%        The docker image name is specified by IMAGENAME. The image can be
%        customized using optional name-value pairs.
%
%    COMPILER.PACKAGE.DOCKER(FILES,FILEPATH,'Options',OPTS) creates a
%        docker image for files generated by the MATLAB Compiler with
%        options specified by a DockerOptions object OPTS. If you use a
%        DockerOptions object, you cannot specify any other options using
%        name-value pairs.
%
%    INPUT ARGUMENTS:
%
%    - RESULTS (A scalar compiler.build.Results object)
%        Build results, created by a compiler.build.standaloneApplication
%        function.
%
%    - FILES (A row char vector, a cellstr vector, or a string vector)
%        List of files and folders for installation. These files are
%        typically generated by the MATLAB Compiler product and can also
%        include any additional files and folders required by the installed
%        application to run. Files generated by the MATLAB Compiler product
%        in a particular release can be packaged using the
%        COMPILER.PACKAGE.DOCKER function of the same release.
%
%    - FILEPATH (A row char vector or a string scalar)
%        Path to the requiredMCRProducts.txt file generated by the MATLAB
%        Compiler product.
%
%    - IMAGENAME (A row char vector or a string scalar)
%        Name of the docker image. It must comply with docker naming rules.
%
%    - OPTS (A scalar compiler.package.DockerOptions object)
%        Docker options, specified as a DockerOptions object.
%
%    NAME-VALUE PAIR ARGUMENTS:
%
%    - AdditionalInstructions (A row char vector, a string vector, or a cellstr vector)
%        Lines of text to be added to the Dockerfile that generates the
%        application image.
%
%    - AdditionalPackages (A row char vector, a string vector, or a cellstr vector)
%        List of Ubuntu 22.04 packages to install into the application
%        image.
%
%    - ContainerUser (A row char vector or a string scalar)
%        Name of the Linux user the Docker container will run as.
%
%    - DockerContext (A row char vector or a string scalar)
%        Path to folder where the docker image is built.
%
%    - EntryPoint (A row char vector or a string scalar)
%        Name of command to be executed at image start-up.
%
%    - ExecuteDockerBuild ('on'/'off', true/false, or 1/0)
%        Determines if the function will build the docker image. This is
%        set to 'on' by default. If set to 'off' the function will populate
%        the DockerContext folder without calling 'docker build'.
%
%    - ImageName (A row char vector or a string scalar)
%        Name of the docker image.
%
%    - RuntimeImage (A row char vector or a string scalar)
%        Name of a MATLAB Runtime docker image.
%
%    - VerbosityLevel ('none', 'concise' or 'verbose')
%
%        'none' - Option for no screen output.
%
%        'concise' - Option for enabling update information but no Docker
%        screen output.
%
%        'verbose' (default) - Option for all screen output, including
%        Docker output that occurs from "docker pull" and "docker build".
%
%    EXAMPLES:
%
%    % Create a docker image from an existing file named 'myapp.m'.
%    % Build a standalone application using the compiler.build.standaloneApplication command.
%    buildResults = compiler.build.standaloneApplication('myapp.m');
%    % Create a docker image for the standalone application using the compiler.package.docker function.
%    COMPILER.PACKAGE.DOCKER(buildResults)
%
%    % Customize the docker image using name-value pairs.
%    COMPILER.PACKAGE.DOCKER(buildResults, ...
%        'ImageName','launchapp','DockerContext','./mydockerapp')
%
%    % Customize a docker image using a DockerOptions object.
%    opts = compiler.package.DockerOptions(buildResults);
%    opts.ImageName = 'launchapp';
%    opts.DockerContext = './mydockerapp';
%    COMPILER.PACKAGE.DOCKER(buildResults,'Options',opts)
%
%    % Create a docker image from the outputs of the mcc command.
%    % Build a standalone application using the mcc command.
%    mcc -o runmyapp -m myapp.m
%    % Create a docker image
%    COMPILER.PACKAGE.DOCKER('runmyapp','requiredMCRProducts.txt', ...
%        'ImageName','launchapp','EntryPoint','runmyapp')
%
%    See also compiler.package.DockerOptions, compiler.build.standaloneApplication, mcc

%   Copyright 2020-2023 The MathWorks, Inc.

packageStart = datetime;
packageID = matlab.lang.internal.uuid;
packageTarget = 'DockerImage';

% Verify Compiler license
if ~license('checkout','Compiler')
    error(message('Compiler:deploytool:license_checkout_error'))
end

if ~isunix || ismac
    error(message('Compiler:package:docker:onlyLinux'))
end

narginchk(1,2+2*numel(properties(compiler.package.DockerOptions.empty)))

expected = char(compiler.internal.build.BuildTypes.standaloneApplication);
if isa(varargin{1},'compiler.build.Results') && ...
        ~isequal(varargin{1}.BuildType,expected)
    error(message('Compiler:package:docker:mustBeStandalone'))
end
[items,prodnums,dckropt,buildID] = compiler.internal.package.shared.inputParser( ...
    @compiler.package.DockerOptions,varargin{:});

logger = compiler.internal.package.docker.log.Logger(dckropt.VerbosityLevel);

proxy = compiler.internal.package.docker.Proxy();
if dckropt.ExecuteDockerBuild
    proxy.assertDockerIsAvailable('compiler.package.docker');
end

% Preparation of base images
% TODO - This piece of code can be signle-sourced into createRuntimeImage to re-use.
% 
if ~isempty(dckropt.RuntimeImage)
    runtimeImg = compiler.internal.package.docker.GenericImage(dckropt.RuntimeImage, logger, dckropt.ExecuteDockerBuild);
else
    OSImg = compiler.internal.package.docker.GenericImage(compiler.internal.package.docker.Constants.BaseOSImage, logger, dckropt.ExecuteDockerBuild);
    
    depsImgContext = compiler.internal.validate.makePathAbsolute(...
                    fullfile(dckropt.DockerContext, compiler.internal.package.docker.Constants.RuntimeDepsContextSubDir));
    depsImg = compiler.internal.package.docker.MatlabRuntimeDepsImage(...
                    compiler.internal.package.docker.Constants.BaseImageRepository, depsImgContext, dckropt.ExecuteDockerBuild, logger, OSImg);
    depsImg.makeAvailable();
   
    installerZip = '';
    if strcmp(computer('arch'),'glnxa64')
        installerZip = compiler.internal.runtime.utils.getExistingMCRInstallerWithValidation;
    end

    installerContext = fullfile(compiler.internal.package.docker.Constants.WorkingDirectory, 'installer');
    installerImg = compiler.internal.package.docker.MatlabRuntimeInstallerImage(...
        compiler.internal.package.docker.Constants.InstallerImageRepository, ...
        installerContext, dckropt.ExecuteDockerBuild, logger, depsImg, 'InstallerFullPath', installerZip);

    runtimeImgContext = compiler.internal.validate.makePathAbsolute(...
                    fullfile(dckropt.DockerContext, compiler.internal.package.docker.Constants.RuntimeContextSubDir));
    runtimeImg = compiler.internal.package.docker.MatlabRuntimeImage(...
                    compiler.internal.package.docker.getRuntimeImageName(prodnums), ...
                    logger, runtimeImgContext, dckropt.ExecuteDockerBuild, prodnums, installerImg, depsImg);
end
runtimeImg.makeAvailable();

appImg = compiler.internal.package.docker.StandaloneApplicationImage(dckropt, logger, items, runtimeImg);
appImg.makeAvailable();

if dckropt.ExecuteDockerBuild
    packageSize = appImg.imageSize();
else
    packageSize = -1;
end

packageTime = seconds(datetime - packageStart);
compiler.internal.package.shared.processResults(packageID, buildID, packageTarget, string(prodnums), dckropt, packageTime, packageSize)

% Get info string
infostr = compiler.internal.package.docker.getHelpfulInfoString(dckropt,prodnums);

% Write getting started text file
gettingstartedfile = fullfile(dckropt.DockerContext,'GettingStarted.txt');
fid = fopen(gettingstartedfile,'w');
if fid > 0
    oc = onCleanup(@()fclose(fid));
    fprintf(fid,infostr);
    clear oc
end

% Print to screen if verbosity > none
if compiler.internal.package.docker.VerbosityLevel(dckropt.VerbosityLevel) > ...
        compiler.internal.package.docker.VerbosityLevel.none
    disp(infostr)
end
