package org.drools.planner.core.constructionheuristic.placer.value;

import org.drools.planner.core.constructionheuristic.placer.AbstractPlacer;
import org.drools.planner.core.constructionheuristic.scope.ConstructionHeuristicMoveScope;
import org.drools.planner.core.constructionheuristic.scope.ConstructionHeuristicSolverPhaseScope;
import org.drools.planner.core.constructionheuristic.scope.ConstructionHeuristicStepScope;
import org.drools.planner.core.domain.variable.PlanningVariableDescriptor;
import org.drools.planner.core.heuristic.selector.move.generic.ChangeMove;
import org.drools.planner.core.heuristic.selector.value.ValueSelector;
import org.drools.planner.core.localsearch.scope.LocalSearchSolverPhaseScope;
import org.drools.planner.core.move.Move;
import org.drools.planner.core.score.Score;
import org.drools.planner.core.score.director.ScoreDirector;
import org.drools.planner.core.termination.Termination;

public class ValuePlacer extends AbstractPlacer {

    protected final Termination termination;
    protected final ValueSelector valueSelector;
    protected final PlanningVariableDescriptor variableDescriptor;
    protected final int selectedCountLimit;

    protected boolean assertMoveScoreIsUncorrupted = false;
    protected boolean assertUndoMoveIsUncorrupted = false;

    public ValuePlacer(Termination termination, ValueSelector valueSelector, int selectedCountLimit) {
        this.termination = termination;
        this.valueSelector = valueSelector;
        variableDescriptor = valueSelector.getVariableDescriptor();
        this.selectedCountLimit = selectedCountLimit;
        solverPhaseLifecycleSupport.addEventListener(valueSelector);
        // TODO don't use Integer.MAX_VALUE as a magical value
        if (valueSelector.isNeverEnding() && selectedCountLimit == Integer.MAX_VALUE) {
            throw new IllegalStateException("The placer (" + this
                    + ") with selectedCountLimit (" + selectedCountLimit + ") has valueSelector (" + valueSelector
                    + ") with neverEnding (" + valueSelector.isNeverEnding() + ").");
        }
    }

    public void setAssertMoveScoreIsUncorrupted(boolean assertMoveScoreIsUncorrupted) {
        this.assertMoveScoreIsUncorrupted = assertMoveScoreIsUncorrupted;
    }

    public void setAssertUndoMoveIsUncorrupted(boolean assertUndoMoveIsUncorrupted) {
        this.assertUndoMoveIsUncorrupted = assertUndoMoveIsUncorrupted;
    }

    public void doPlacement(ConstructionHeuristicStepScope stepScope) {
        // TODO extract to PlacerForager
        Score maxScore = stepScope.getPhaseScope().getScoreDefinition().getPerfectMinimumScore();
        ConstructionHeuristicMoveScope maxMoveScope = null;

        Object entity = stepScope.getEntity();
        int selectedCount = 0;
        int moveIndex = 0;
        for (Object value : valueSelector) {
            ConstructionHeuristicMoveScope moveScope = new ConstructionHeuristicMoveScope(stepScope);
            moveScope.setMoveIndex(moveIndex);
            ChangeMove move = new ChangeMove(entity, variableDescriptor, value);
            moveScope.setMove(move);
            if (!move.isMoveDoable(stepScope.getScoreDirector())) {
                logger.trace("        Ignoring not doable move ({}).", move);
            } else {
                doMove(moveScope);
                // TODO extract to PlacerForager
                if (moveScope.getScore().compareTo(maxScore) > 0) {
                    maxScore = moveScope.getScore();
                    // TODO for non explicit Best Fit *, default to random picking from a maxMoveScopeList
                    maxMoveScope = moveScope;
                }
                if (selectedCount >= selectedCountLimit) {
                    break;
                }
            }
            moveIndex++;
            if (termination.isPhaseTerminated(stepScope.getPhaseScope())) {
                break;
            }
        }
        if (maxMoveScope != null) {
            Move step = maxMoveScope.getMove();
            stepScope.setStep(step);
            if (logger.isDebugEnabled()) {
                stepScope.setStepString(step.toString());
            }
            stepScope.setUndoStep(maxMoveScope.getUndoMove());
            stepScope.setScore(maxMoveScope.getScore());
        }
    }

    private void doMove(ConstructionHeuristicMoveScope moveScope) {
        ScoreDirector scoreDirector = moveScope.getScoreDirector();
        Move move = moveScope.getMove();
        Move undoMove = move.createUndoMove(scoreDirector);
        moveScope.setUndoMove(undoMove);
        move.doMove(scoreDirector);
        processMove(moveScope);
        undoMove.doMove(scoreDirector);
        if (assertUndoMoveIsUncorrupted) {
            ConstructionHeuristicSolverPhaseScope phaseScope = moveScope.getStepScope().getPhaseScope();
            phaseScope.assertUndoMoveIsUncorrupted(move, undoMove);
        }
        logger.trace("        Move index ({}), score ({}) for move ({}).",
                new Object[]{moveScope.getMoveIndex(), moveScope.getScore(), moveScope.getMove()});
    }

    private void processMove(ConstructionHeuristicMoveScope moveScope) {
        Score score = moveScope.getStepScope().getPhaseScope().calculateScore();
        if (assertMoveScoreIsUncorrupted) {
            moveScope.getStepScope().getPhaseScope().assertWorkingScore(score);
        }
        moveScope.setScore(score);
        // TODO work with forager
        // forager.addMove(moveScope);
    }

}

@@ -2768,39 +2768,8 @@
 ();%0A
-        int selectedCount = 0;%0A
     
@@ -3666,29 +3666,25 @@
     if (
-selectedCount
+moveIndex
  %3E= sele

fix selectedCountLimit