function nbytes = fprintf(fileID,formatSpec,varargin)
%MATLAB Code Generation Library Function

%   Limitations:
%   1. b and t modifiers on %u, %o, and %x formats are not supported.
%   2. The $ flag for reusing input arguments is not supported.
%   3. There is no automatic casting. Input arguments must generally match
%   their format types for predictable results.
%   4. Escaped chars are limited to the (decimal) range 0-127.
%   5. With extrinsic functions disabled in MATLAB or Simulink or inside a
%   parfor, fprintf(1,...) and fprintf(2,...) (when 1 and 2 are constants)
%   are changed to printf(...). Otherwise, output would not be directed to
%   the MATLAB command window.
%   6. Null characters are handled differently in MATLAB Coder than in MATLAB.
%   To output a null character using MATLAB Coder use:
%
%      fprintf(fid, '%c', char(0));
%   7. Integer arguments must be representable in the target hardware.

%   Copyright 2012-2022 The MathWorks, Inc.
%#codegen
narginchk(1,Inf);

if coder.internal.isAmbiguousTypes()
    nbytes = coder.ignoreConst(0);
    return
end

% Inline always for shrink types
coder.inline('always');
if coder.internal.isCharOrScalarString(fileID)
    coder.internal.prefer_const(fileID);
    % No fileID provided
    if nargin == 1
        nbytes = fprintf(1, fileID);
        return
    else
        nbytes = fprintf(1, fileID, formatSpec, varargin{:});
        return
    end
else
    % Error-check the fileID and format specifier
    narginchk(2,Inf);

    coder.internal.prefer_const(fileID, formatSpec);

    fidCheck(fileID, mfilename);
    coder.internal.errorIf(fileID == 0,'MATLAB:notimplemented_mx');

    % Try to check fileID at compile-time. If not const, then we'll check at runtime
    % below.
    coder.internal.errorIf(coder.internal.isConstTrue(fileID < 0), ...
                           'MATLAB:FileIO:InvalidFid');
    if coder.internal.isConstTrue(fileID < 0)
        nbytes = 0;
        return;
    end

    % 'FPRINTF' as a literal is significantly compiletime faster than 'coder.const(upper(mfilename))'
    [cfmt, nconvs, validatedHoleFilling] = coder.internal.print_processing('FPRINTF', formatSpec, varargin{:});


    if isFileIOExtrinsic()
        % Validate the types against the format specifier
        nbytes = 0;
        nbytes = feval('fprintf',...
                       fileID,...
                       coder.internal.toCharIfString(formatSpec),...
                       varargin{1:nconvs});
    else
        % Validate types and null-terminate for %s
        nbytes = double(cfprintf(fileID,cfmt,validatedHoleFilling{:}));
    end
end

%--------------------------------------------------------------------------

function nbytesint = cfprintf(fileID,cfmt,varargin)
% FPRINTF returning an int32
coder.inline('always');
coder.internal.prefer_const(fileID,cfmt);
headerFile = coder.internal.getCHeaderName('stdio');
coder.cinclude(headerFile);
varDataType = coder.internal.getCLibName('FILE');
NULL = coder.opaquePtr(varDataType,coder.internal.null,'HeaderFile',headerFile);
INMATLAB = coder.const(coder.internal.runs_in_matlab());
nbytesint = int32(0);
if INMATLAB && (fileID == 1 || fileID == 2)
    % Convert fprintf(1,...) and fprintf(2,...) to printf(...);
    if coder.internal.isInParallelRegion()
        ctx = coder.opaque('emlrtConstCTX', 'NULL');
        ctx = coder.ceval('emlrtGetRootTLSGlobal');
        nbytesint = coder.ceval('emlrtMexPrintf', ctx, cfmt, varargin{:});
    else
        nbytesint = coder.ceval('mexPrintf', cfmt, varargin{:});
    end
else
    isConstStdStream = coder.internal.isConst(fileID) && (fileID == 1 || fileID == 2);
    if isConstStdStream
        % Special case the standard streams so we avoid unnecessary error checks and
        % emit clean code with the stream name inlined like:
        %
        %   fflush(stdout)
        varDataType = coder.internal.getCLibName('FILE');
        libDataType = [varDataType ' *'];        
        libMethodFlush = coder.internal.getCLibName('fflush');
        if fileID == 1
            libMethodPrintf = coder.internal.getCLibName('printf');
            nbytesint = coder.ceval(libMethodPrintf,cfmt, varargin{:});
            % use dummyOutput to model "int fflush(..." signature (g2259568)
            dummyOutput = coder.internal.indexInt(0);            
            dummyOutput = coder.ceval(libMethodFlush,coder.opaque(libDataType,'stdout','HeaderFile',headerFile)); %#ok<NASGU>
        else
            libFprintf = coder.internal.getCLibName('fprintf');
            nbytesint = coder.ceval(libFprintf,coder.opaque(libDataType,'stderr','HeaderFile',headerFile), ...
                                    cfmt, varargin{:});
            % use dummyOutput to model "int fflush(..." signature (g2259568)
            dummyOutput = coder.internal.indexInt(0); 
            dummyOutput = coder.ceval(libMethodFlush,coder.opaque(libDataType,'stderr','HeaderFile',headerFile)); %#ok<NASGU>
        end
    else
        [filestar,autoflush] = coder.internal.fileManager('filestar',fileID);
        if filestar == NULL
            if coder.internal.hasRuntimeErrors()
                coder.internal.error('MATLAB:FileIO:InvalidFid');
            else
                nbytesint = int32(0);
            end
        else
            libMethodName = coder.internal.getCLibName('fprintf');
            nbytesint = coder.ceval(libMethodName,filestar, cfmt, varargin{:});
            if autoflush
                    % use dummyOutput to model "int fflush(..." signature (g2259568)
                    dummyOutput = coder.internal.indexInt(0); 
                    libMethodName = coder.internal.getCLibName('fflush');
                    dummyOutput = coder.ceval(libMethodName,filestar); %#ok<NASGU>
            end
        end
    end
end
