/*
* JBoss, Home of Professional Open Source
* Copyright 2008-10 Red Hat and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
* @authors Andrew Dinn
*/
package org.jboss.byteman.rule;

import org.jboss.byteman.rule.compiler.CompileContext;
import org.jboss.byteman.rule.type.Type;
import org.jboss.byteman.rule.expression.ExpressionHelper;
import org.jboss.byteman.rule.expression.Expression;
import org.jboss.byteman.rule.grammar.ECATokenLexer;
import org.jboss.byteman.rule.grammar.ECAGrammarParser;
import org.jboss.byteman.rule.grammar.ParseNode;
import org.jboss.byteman.rule.exception.ParseException;
import org.jboss.byteman.rule.exception.TypeException;
import org.jboss.byteman.rule.exception.ExecuteException;
import org.jboss.byteman.rule.exception.CompileException;
import org.jboss.byteman.rule.helper.HelperAdapter;
import org.objectweb.asm.MethodVisitor;

import java.io.StringWriter;
import java.io.StringReader;

import java_cup.runtime.Symbol;

/**
 * class which represents a rule condition comprising a boolean expression
 */
public class Condition extends RuleElement
{
    public static Condition create(Rule rule, ParseNode conditionTree) throws TypeException
    {
        Condition condition = new Condition(rule, conditionTree);
        return condition;
    }

    public static Condition create(Rule rule, String text) throws ParseException, TypeException
    {
        if ("".equals(text)) {
            return new Condition(rule);
        }
        String fulltext = "BIND NOTHING IF \n" + text + "\n DO NOTHING";
        try {
            ECATokenLexer lexer = new ECATokenLexer(new StringReader(text));
            ECAGrammarParser parser = new ECAGrammarParser(lexer);
            Symbol condition_parse = parser.parse();
            ParseNode conditionTree = (ParseNode) condition_parse.value;
            Condition condition = new Condition(rule, conditionTree);
            return condition;
        } catch (Exception e) {
            throw new ParseException("org.jboss.byteman.rule.Condition : error parsing condition\n" + text, e);
        }
    }

    protected Condition(Rule rule, ParseNode conditionTree)
            throws TypeException
    {
        super(rule);
        int tag = conditionTree.getTag();
        condition = ExpressionHelper.createExpression(rule, rule.getBindings(), conditionTree, Type.BOOLEAN);
        // check bindings
        condition.bind();
    }
    
    protected Condition(Rule rule)
    {
        super(rule);
        this.condition = null;
    }

    public Type typeCheck(Type expected) throws TypeException {
        // expected must be Type.Z
        if (condition != null) {
            condition.typeCheck(Type.Z);
        }
        return Type.Z;
    }

    public void compile(MethodVisitor mv, CompileContext compileContext) throws CompileException {
        int currentStack = compileContext.getStackCount();
        // get the condition to compile itself -- it adds 1 to stack height
        condition.compile(mv, compileContext);
        // unbox if necessary
        if (condition.getType() == Type.BOOLEAN) {
            compileContext.compileUnbox(Type.BOOLEAN, Type.Z);
        }

        // check stack heights
        if (compileContext.getStackCount() != currentStack + 1) {
            throw new CompileException("Condition.compile : invalid stack height " + compileContext.getStackCount() + " expecting " + currentStack);
        }
    }

    public Object interpret(HelperAdapter helper)
            throws ExecuteException
    {
        Boolean result = (Boolean)condition.interpret(helper);

        return result;
    }

    public void writeTo(StringWriter stringWriter)
    {
        if (condition == null) {
            stringWriter.write("IF   TRUE");
        } else {
            stringWriter.write("IF   ");
            condition.writeTo(stringWriter);
        }
        stringWriter.write("\n");
    }

    private Expression condition;
}
