/*
 * Copyright 2012 JBoss Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.drools.planner.core.heuristic.selector.move.generic.chained;

import java.util.Collection;

import org.apache.commons.lang.ObjectUtils;
import org.drools.planner.core.domain.variable.PlanningVariableDescriptor;
import org.drools.planner.core.heuristic.selector.move.generic.SwapMove;
import org.drools.planner.core.move.Move;
import org.drools.planner.core.score.director.ScoreDirector;

public class ChainedSwapMove extends SwapMove {

    public ChainedSwapMove(Collection<PlanningVariableDescriptor> variableDescriptors,
            Object leftEntity, Object rightEntity) {
        super(variableDescriptors, leftEntity, rightEntity);
    }

    // ************************************************************************
    // Worker methods
    // ************************************************************************

    @Override
    public Move createUndoMove(ScoreDirector scoreDirector) {
        return new ChainedSwapMove(variableDescriptors, rightEntity, leftEntity);
    }

    public void doMove(ScoreDirector scoreDirector) {
        for (PlanningVariableDescriptor variableDescriptor : variableDescriptors) {
            Object oldLeftValue = variableDescriptor.getValue(leftEntity);
            Object oldRightValue = variableDescriptor.getValue(rightEntity);
            if (!ObjectUtils.equals(oldLeftValue, oldRightValue)) {
                if (!variableDescriptor.isChained()) {
                    scoreDirector.beforeVariableChanged(leftEntity, variableDescriptor.getVariableName());
                    variableDescriptor.setValue(leftEntity, oldRightValue);
                    scoreDirector.afterVariableChanged(leftEntity, variableDescriptor.getVariableName());

                    scoreDirector.beforeVariableChanged(rightEntity, variableDescriptor.getVariableName());
                    variableDescriptor.setValue(rightEntity, oldLeftValue);
                    scoreDirector.afterVariableChanged(rightEntity, variableDescriptor.getVariableName());
                } else {
                    if (oldRightValue != leftEntity) {
                        doChainedMove(scoreDirector, variableDescriptor, leftEntity, oldRightValue);
                    }
                    if (oldLeftValue != rightEntity) {
                        doChainedMove(scoreDirector, variableDescriptor, rightEntity, oldLeftValue);
                    }
                }
            }
        }
    }

    // TODO DRY with ChainedChangeMove
    public void doChainedMove(ScoreDirector scoreDirector, PlanningVariableDescriptor variableDescriptor,
            Object planningEntity, Object toPlanningValue ) {
        Object oldPlanningValue = variableDescriptor.getValue(planningEntity);
        Object oldTrailingEntity = scoreDirector.getTrailingEntity(variableDescriptor, planningEntity);
        // If chaining == true then toPlanningValue == null guarantees an uninitialized entity
        Object newTrailingEntity = toPlanningValue == null ? null
                : scoreDirector.getTrailingEntity(variableDescriptor, toPlanningValue);

        // Close the old chain
        if (oldTrailingEntity != null) {
            scoreDirector.beforeVariableChanged(oldTrailingEntity, variableDescriptor.getVariableName());
            variableDescriptor.setValue(oldTrailingEntity, oldPlanningValue);
            scoreDirector.afterVariableChanged(oldTrailingEntity, variableDescriptor.getVariableName());
        }

        // Change the entity
        scoreDirector.beforeVariableChanged(planningEntity, variableDescriptor.getVariableName());
        variableDescriptor.setValue(planningEntity, toPlanningValue);
        scoreDirector.afterVariableChanged(planningEntity, variableDescriptor.getVariableName());

        // Reroute the new chain
        if (newTrailingEntity != null) {
            scoreDirector.beforeVariableChanged(newTrailingEntity, variableDescriptor.getVariableName());
            variableDescriptor.setValue(newTrailingEntity, planningEntity);
            scoreDirector.afterVariableChanged(newTrailingEntity, variableDescriptor.getVariableName());
        }
    }

}

@@ -3181,25 +3181,17 @@
  Object 
-planningE
+e
 ntity, O
@@ -3211,17 +3211,16 @@
 ingValue
- 
 ) %7B%0A    
@@ -3273,33 +3273,25 @@
 or.getValue(
-planningE
+e
 ntity);%0A    
@@ -3373,25 +3373,17 @@
 riptor, 
-planningE
+e
 ntity);%0A
@@ -4073,33 +4073,25 @@
 ableChanged(
-planningE
+e
 ntity, varia
@@ -4160,25 +4160,17 @@
 etValue(
-planningE
+e
 ntity, t
@@ -4229,25 +4229,17 @@
 Changed(
-planningE
+e
 ntity, v
@@ -4520,17 +4520,9 @@
 ty, 
-planningE
+e
 ntit

simplify field names (remove "planning" prefix)