function state = pause(varargin)
%MATLAB Code Generation Library Function

%Limitations:

% 1. In generated code, pause delays are saturated to uint32 range prior to calling C run-time code
%
% 2. Cannot be used inside of PARFOR in MEX
%
% 3. When generating code on Windows for MATLAB Host compatible hardware, the Windows API Sleep is
% used. In all other cases the POSIX API nanosleep is used. When compiling generated code that uses
% the POSIX API, the preprocessor macro _POSIX_C_SOURCE must be defined to an integer greater than
% or equal to 199309L.
%
% 4. In code generation, when pause is called without a semicolon and without assigning the output,
% the return value will still be printed. So:
%
%      pause(...)
%
% will print out ans = <pause state>. In MATLAB that value would not have been printed. This
% includes calling pause inside of anonymous functions.
%
% 5. In standalone code, calling pause with no inputs to wait for keyboard input:
%
%   pause();
%
% uses getchar to read input. This has 2 implications. First, when running the generated code inside
% of a terminal, you may need to press enter/return to continue from the pause as the terminal often
% buffers input until a return is entered. Second, when running the generated code inside of MATLAB
% with the system command, with SIL, or with PIL, pause may not behave correctly as STDIN may not be
% redirected from the MATLAB command window to the generated code.

%   Copyright 2018-2020 The MathWorks, Inc.

%#codegen
narginchk(0,1);
if coder.internal.runs_in_matlab()
    checkNotInPARFOR();
    if coder.internal.canUseExtrinsic()
        if nargout > 0
            % Returns either 'on' or 'off'
            coder.varsize('state',[1,3],[0,1]);
            state = 'on';
            state = feval('pause',varargin{:});
        else
            feval('pause',varargin{:});
        end
        return
    end
end
if nargout > 0
    state = cpause(varargin{:});
else
    cpause(varargin{:});
end

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

function state = cpause(varargin)
persistent pauseState;
if isempty(pauseState)
    pauseState = ON;
end
if nargout > 0
    state = stateToName(pauseState);
    doPause = false;
else
    doPause = pauseState == ON;
end
if nargin == 0
    % pause
    if ~doPause
        return
    end
    indefinitePause();
else
    n = varargin{1};
    haveCmd = coder.internal.isCharOrScalarString(n);
    coder.internal.assert(haveCmd || ...
                          coder.internal.isBuiltInNumeric(n), ...
                          'MATLAB:badopt');
    if haveCmd
        % pause('on'), pause('off'), or pause('query')
        cmd = n;
        matched = false;
        switch cmd
          case 'on'
            pauseState = ON;
            matched = true;
          case 'off'
            pauseState = OFF;
            matched = true;
          case 'query'
            state = stateToName(pauseState);
            return
        end
        coder.internal.assert(matched, ...
                              'MATLAB:badopt');
    else
        if ~doPause
            return
        end
        durationPause(n);
    end
end

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

function n = ON
n = uint8(0);

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

function n = OFF
n = uint8(1);

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

function name = stateToName(state)
switch state
  case ON
    name = 'on';
  otherwise
    name = 'off';
end

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

function indefinitePause
if coder.internal.runs_in_matlab()
    coder.internal.errorIf(coder.internal.canUseExtrinsic(), ...
                           'Coder:builtins:Explicit', ...
                           'Internal error: Should have used extrinsic pause');
    coder.ceval('-barrier','-jit','emlrtPauseForInput');
else
    coder.cinclude('<stdio.h>');
    coder.ceval('-barrier','-jit','getchar');
end

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

function durationPause(n)
% pause(n)
MAXDELAY = intmax('uint32');
if isempty(n)
    delayInt = 0;
    delayNano = 0;
else
    delay = real(n(1));
    if delay < 0 || isnan(delay)
        delay = zeros('like',delay);
    end
    delayInt = floor(delay);
    if delayInt > MAXDELAY
        delayInt = cast(MAXDELAY,'like',delayInt);
    end
    delayFrac = delay-delayInt;
    if delayFrac > 0
        delayNano = floor(delayFrac*coder.internal.time.impl.timescale);
    else
        delayNano = zeros('like',delay);
    end
end
if coder.internal.runs_in_matlab()
    coder.internal.errorIf(coder.internal.canUseExtrinsic(), ...
                           'Coder:builtins:Explicit', ...
                           'Internal error: Should have used extrinsic pause');
    sleepFcn = @cDurationPauseEMLRT;
elseif coder.internal.time.impl.isWin64Codegen()
    sleepFcn = @cDurationPauseWin64;
else
    % Default to POSIX
    sleepFcn = @cDurationPausePOSIX;
end
sleepFcn(delayInt,delayNano);

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

function cDurationPauseEMLRT(delayInt,delayNano,~)
timespec = coder.internal.time.impl.makeEMLRTTimespec(delayInt,delayNano);
status = coder.internal.indexInt(0);
status = coder.ceval('-barrier','-jit','emlrtSleep',coder.rref(timespec)); %#ok

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

function cDurationPauseWin64(delayInt,delayNano,~)
cDurationPauseWin64Impl(delayInt,delayNano,'Sleep');

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

function cDurationPauseWin64Impl(delayInt,delayNano,cfname)
coder.cinclude('<windows.h>');
milli = 1e3;
delayMilli = floor(delayNano/(coder.internal.time.impl.timescale/milli));
delay = milli*delayInt + delayMilli;
% Saturate to fit
delayUint32 = uint32(delay);
coder.ceval('-barrier','-jit',cfname,delayUint32);

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

function cDurationPausePOSIX(delayInt,delayNano,~)
timespec = coder.internal.time.impl.makeTimespec(delayInt,delayNano);
status = coder.internal.indexInt(0);
status = coder.ceval('-barrier','nanosleep',coder.rref(timespec),coder.opaque('struct timespec*','NULL')); %#ok

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

function checkNotInPARFOR()
coder.internal.errorIf(coder.internal.isInParallelRegion(), ...
                       'Coder:toolbox:PausePARFOR');

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