function [XBest, ChooseRandom, this] = chooseNextPointInternal(this)
%

%   Copyright 2016-2023 The MathWorks, Inc.

[ChooseRandom, ReasonKey] = shouldChooseRandomPoint(this);
if ChooseRandom
    if this.PrivOptions.Verbose >= 2
        bayesoptim.printInfo('ChoosingRandomPoint');
        bayesoptim.printInfo(ReasonKey);
    end
    XBest = randomXFeasiblePoints(this, 1);
elseif doingGridsearch(this)
    [XBest, this] = gridXFeasiblePoint(this);
elseif isequal(this.PrivOptions.AcquisitionFunctionName, 'asha')
    [XBest, this] = choosePointASHA(this);
else
    % Choose the next point by maximizing the
    % constraint-weighted acquisition function.
    switch this.PrivOptions.AcquisitionFunctionName
        case 'probabilityofimprovement'
            AFcn = @(X)bayesoptim.probabilityOfImprovement(X, this.ObjectiveFcnModel, ...
                this.IncumbentF, this.ObjectiveFcnModel.Sigma);
        case {'expectedimprovement', 'expectedimprovementplus'}
            AFcn = @(X)bayesoptim.expectedImprovement(X, this.ObjectiveFcnModel, ...
                this.IncumbentF);
        case 'lowerconfidencebound'
            Kappa = 2;
            AFcn = @(X)bayesoptim.lowerConfidenceBound(X, this.ObjectiveFcnModel, Kappa);
        case {'expectedimprovementpersecond', 'expectedimprovementpersecondplus'}
            AFcn = @(X)bayesoptim.expectedImprovementPerSecond(X, this.ObjectiveFcnModel, ...
                this.ObjectiveEvaluationTimeModel, this.IncumbentF);
        otherwise
            bayesoptim.err('AFUnknown', this.PrivOptions.AcquisitionFunctionName);
    end
    VarSpec = this.PrivOptions.VarSpec;
    XBest = fminbndGlobal(this, @constraintWeightedNegAF, VarSpec.LBTrans, ...
        VarSpec.UBTrans, this.PrivOptions.NumRestartCandidates, ...
        this.PrivOptions.NumRestarts, this.PrivOptions.VerboseRestarts, ...
        this.PrivOptions.MaxIterPerRestart, this.PrivOptions.RelTol);
end
if ~isempty(XBest)
    XBest = legalizePoints(this, XBest);
end

    function NegAF = constraintWeightedNegAF(X)
        % Return the constraint-weighted negative AF value for all
        % feasible points in X (to be minimized). NegAF(i) = Inf if
        % not feasible
        NegAF = inf(size(X,1),1);
        [Xcanon, InputFeasible] = legalizePoints(this, X);
        if any(InputFeasible)
            Xfeasible = Xcanon(InputFeasible,:);
            if isnan(this.IncumbentF)
                % We have constraints, but no incumbent, meaning that
                % no feasible points were found. Maximize only the
                % constraint probability.
                NegAF(InputFeasible) = -ProbAllConstraintsSatisfied(this, Xfeasible);
            else
                % Constraints and an incumbent. Maximize constraint-weighted AF.
                NegAF(InputFeasible) = -AFcn(Xfeasible) .*  ProbAllConstraintsSatisfied(this, Xfeasible);
            end
        end
    end
end

