function [errorCode, out] = evaluateRequest(argsIn, taskID)
%evaluateRequest Evaluate the Function of a parallel.Future.

%   Copyright 2013-2020 The MathWorks, Inc.

import com.mathworks.toolbox.distcomp.pmode.taskqueue.FevalEvaluationStatus

stackFilesToIgnore = {mfilename};
iJavaLog(6, 'Entering evaluateRequest for task %d.', taskID);
logOnExit = onCleanup(@() iJavaLog(6, 'Leaving evaluateRequest for task %d.', taskID));

% Handle 'mpi'-ness state.
pctPreRemoteEvaluation('mpi_mi');

errorCode = FevalEvaluationStatus.EVALUATION_OK;

% Unpack the inputs
try
    [userFcn, numArgsOut, inputArgsCell] = iInterpretArgsIn(argsIn);
    iJavaLog(3, 'evaluateRequest for task %d got function: %s.', taskID, func2str(userFcn));
catch E
    errorCode = FevalEvaluationStatus.EVALUATION_ERROR;
    err = MException(message('MATLAB:parallel:future:CouldNotInterpretFunction'));
    err = addCause(err, E);
    out = distcompserialize(err);
    iJavaLog(4, 'Leaving evaluateRequest with EVALUATION_ERROR (from unpacking).');
    return;
end

% Evaluate the function
try
    outArgs = cell(1, numArgsOut);

    % Use builtin('feval', ...) to avoid dispatching to an FEVAL method
    % of one of inputArgsCell.
    if numArgsOut == 0
        builtin('feval', userFcn, inputArgsCell{:});
    else
        [outArgs{1:numArgsOut}] = builtin('feval', userFcn, inputArgsCell{:});
    end
catch E
    [isMissingSourceError, missingSources] = parallel.internal.pool.missingSourcesFromException(E);
    if isMissingSourceError
        errorCode = FevalEvaluationStatus.EVALUATION_RESULTED_IN_UNDEFINED_FUNCTION;
        iJavaLog(4, 'Leaving evaluateRequest for task %d with EVALUATION_RESULTED_IN_UNDEFINED_FUNCTION.', taskID);
        iJavaLog(5, 'Missing sources from error: %s', strjoin(missingSources, ','));
    else
        errorCode = FevalEvaluationStatus.EVALUATION_ERROR;
        iJavaLog(4, 'Leaving evaluateRequest for task %d with EVALUATION_ERROR (from evaluation).', taskID);
    end
    err = ParallelException.hBuildFromRemoteException(E, stackFilesToIgnore);
    out = distcompserialize(err);
    return;
end

% Pack up the outputs
try
    out = parallel.internal.pool.serialize(outArgs);
catch E
    errorCode = FevalEvaluationStatus.EVALUATION_ERROR;
    err = MException(message('MATLAB:parallel:future:ErrorSerializingResult'));
    err = addCause(err, E);
    out = distcompserialize(err);
    iJavaLog(4, 'Leaving evaluateRequest for task %d with EVALUATION_ERROR (from serialization).', taskID);
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [fcn, numArgsOut, inputArgsCell] = iInterpretArgsIn(argsIn)
% The java layer always duplicates our buffers before calling 'evaluateRequest',
% so we must free them here.
freeBuffersAfterDeserialization = true;
[allVals, notFound] = ...
    parallel.internal.pool.deserializeFunction(argsIn, freeBuffersAfterDeserialization);

[fcn, numArgsOut, inputArgsCell] = deal(allVals{:});
iJavaLog(4, 'Not found flag: %d for function: %s', notFound, func2str(fcn));
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% iJavaLog - log to the pmode logger. Note we do not use dctSchedulerMessage
% here as that prints output to the command window.
function iJavaLog( level, fmt, varargin )
try
    loglvl = com.mathworks.toolbox.parallel.pctutil.logging.DistcompLevel.getLevelFromValue( level );
    LOGGER = com.mathworks.toolbox.distcomp.pmode.taskqueue.Log.LOGGER;
    LOGGER.log( loglvl, sprintf( fmt, varargin{:} ) );
catch E %#ok<NASGU>
        % ignore any logging problems
end
end
