package org.drools.rule.constraint;

import org.drools.base.mvel.MVELCompilationUnit;
import org.drools.common.InternalWorkingMemory;
import org.drools.reteoo.LeftTuple;
import org.drools.rule.*;
import org.mvel2.MVEL;
import org.mvel2.ParserConfiguration;
import org.mvel2.ParserContext;
import org.mvel2.ast.*;
import org.mvel2.compiler.CompiledExpression;
import org.mvel2.compiler.ExecutableAccessor;
import org.mvel2.compiler.ExecutableLiteral;
import org.mvel2.compiler.ExecutableStatement;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.util.ASTLinkedList;

import java.util.Map;

import static org.drools.rule.constraint.EvaluatorHelper.valuesAsMap;

public class MvelConditionEvaluator implements ConditionEvaluator, MapConditionEvaluator {

    protected final Declaration[] declarations;
    private final ParserConfiguration parserConfiguration;
    protected ExecutableStatement executableStatement;
    protected MVELCompilationUnit compilationUnit;

    private boolean evaluated = false;

    public MvelConditionEvaluator(ParserConfiguration configuration, String expression, Declaration[] declarations) {
        this.declarations = declarations;
        this.parserConfiguration = configuration;
        executableStatement = (ExecutableStatement)MVEL.compileExpression(expression, new ParserContext(parserConfiguration));
    }

    public MvelConditionEvaluator(MVELCompilationUnit compilationUnit, ParserConfiguration parserConfiguration, ExecutableStatement executableStatement, Declaration[] declarations) {
        this.declarations = declarations;
        this.compilationUnit = compilationUnit;
        this.parserConfiguration = parserConfiguration;
        this.executableStatement = executableStatement;
    }

    public boolean evaluate(Object object, Map<String, Object> vars) {
        return evaluate(executableStatement, object, vars);
    }

    public boolean evaluate(Object object, InternalWorkingMemory workingMemory, LeftTuple leftTuple) {
        return evaluate(executableStatement, object, workingMemory, leftTuple);
    }

    public boolean evaluate(ExecutableStatement statement, Object object, InternalWorkingMemory workingMemory, LeftTuple leftTuple) {
        if (compilationUnit == null) {
            Map<String, Object> vars = valuesAsMap(object, workingMemory, leftTuple, declarations);
            return evaluate(statement, object, vars);
        }

        VariableResolverFactory factory = compilationUnit.createFactory();
        compilationUnit.updateFactory( null, null, object,
                                       leftTuple, null, workingMemory,
                                       workingMemory.getGlobalResolver(),
                                       factory );

        org.drools.rule.Package pkg = workingMemory.getRuleBase().getPackage( "MAIN" );
        if ( pkg != null ) {
            MVELDialectRuntimeData data = (MVELDialectRuntimeData) pkg.getDialectRuntimeRegistry().getDialectData("mvel");
            factory.setNextFactory( data.getFunctionFactory() );
        }

        return (Boolean) MVEL.executeExpression( statement, object, factory );
    }

    private boolean evaluate(ExecutableStatement statement, Object object, Map<String, Object> vars) {
        return vars == null ? (Boolean)MVEL.executeExpression(statement, object) : (Boolean)MVEL.executeExpression(statement, object, vars);
    }

    ConditionAnalyzer.Condition getAnalyzedCondition() {
        return new ConditionAnalyzer(executableStatement, declarations).analyzeCondition();
    }

    ConditionAnalyzer.Condition getAnalyzedCondition(Object object, InternalWorkingMemory workingMemory, LeftTuple leftTuple) {
        ensureCompleteEvaluation(object, workingMemory, leftTuple);
        return new ConditionAnalyzer(executableStatement, declarations).analyzeCondition();
    }

    private void ensureCompleteEvaluation(Object object, InternalWorkingMemory workingMemory, LeftTuple leftTuple) {
        if (!evaluated) {
            ASTNode rootNode = getRootNode();
            if (rootNode != null) {
                ensureCompleteEvaluation(rootNode, object, workingMemory, leftTuple);
            }
            evaluated = true;
        }
    }

    private void ensureCompleteEvaluation(ASTNode node, Object object, InternalWorkingMemory workingMemory, LeftTuple leftTuple) {
        node = unwrap(node);
        if (node == null || !(node instanceof And || node instanceof Or)) {
            return;
        }
        ensureBranchEvaluation(object, workingMemory, leftTuple, ((BooleanNode)node).getLeft());
        ensureBranchEvaluation(object, workingMemory, leftTuple, ((BooleanNode)node).getRight());
    }

    private ASTNode unwrap(ASTNode node) {
        while (node instanceof Negation || node instanceof LineLabel || node instanceof Substatement) {
            node = unwrapNegation(node);
            node = unwrapSubstatement(node);
        }
        return node;
    }

    private void ensureBranchEvaluation(Object object, InternalWorkingMemory workingMemory, LeftTuple leftTuple, ASTNode node) {
        if (!isEvaluated(node)) {
            ASTNode next = node.nextASTNode;
            node.nextASTNode = null;
            evaluate(asCompiledExpression(node), object, workingMemory, leftTuple);
            node.nextASTNode = next;
        }
        ensureCompleteEvaluation(node, object, workingMemory, leftTuple);
    }

    private ASTNode unwrapNegation(ASTNode node) {
        if (node instanceof Negation) {
            ExecutableStatement statement = ((Negation)node).getStatement();
            return statement instanceof ExecutableAccessor ? ((ExecutableAccessor)statement).getNode() : null;
        }
        return node;
    }

    private ASTNode unwrapSubstatement(ASTNode node) {
        if (node instanceof LineLabel) {
            return node.nextASTNode;
        }
        return node instanceof Substatement ? ((ExecutableAccessor)((Substatement)node).getStatement()).getNode() : node;
    }

    private boolean isEvaluated(ASTNode node) {
        node = unwrapSubstatement(node);
        if (node instanceof Contains) {
            return ((Contains)node).getFirstStatement().getAccessor() != null;
        }
        return node instanceof BinaryOperation ? ((BooleanNode) node).getLeft().getAccessor() != null : node.getAccessor() != null;
    }

    private CompiledExpression asCompiledExpression(ASTNode node) {
        return new CompiledExpression(new ASTLinkedList(node), null, Object.class, parserConfiguration, false);
    }

    private ASTNode getRootNode() {
        if (executableStatement instanceof ExecutableLiteral) {
            return null;
        }
        return executableStatement instanceof CompiledExpression ? ((CompiledExpression) executableStatement).getFirstNode() : ((ExecutableAccessor) executableStatement).getNode();
    }
}

@@ -2758,324 +2758,8 @@
 );%0A%0A
-        org.drools.rule.Package pkg = workingMemory.getRuleBase().getPackage( %22MAIN%22 );%0A        if ( pkg != null ) %7B%0A            MVELDialectRuntimeData data = (MVELDialectRuntimeData) pkg.getDialectRuntimeRegistry().getDialectData(%22mvel%22);%0A            factory.setNextFactory( data.getFunctionFactory() );%0A        %7D%0A%0A
     

remove useless code in MvelConditionEvaluator