package traci.lang.interpreter.node;

import org.antlr.runtime.Token;

import traci.lang.interpreter.Context;
import traci.lang.interpreter.TraciValue;
import traci.lang.interpreter.exceptions.FunctionReturnException;
import traci.lang.interpreter.exceptions.InterpreterIllegalOperatorArgument;
import traci.lang.interpreter.exceptions.InterpreterRuntimeException;
import traci.lang.parser.TraciToken;
import traci.math.Vector;
import traci.model.material.Color;

public class BinaryOpNode implements TraciNode
{
    private final Op op;
    private final TraciNode aNode;
    private final TraciNode bNode;
    private final TraciToken token;

    public BinaryOpNode(final Op op, final TraciNode aNode, final TraciNode bNode, final Token token)
    {
        this.op = op;
        this.aNode = aNode;
        this.bNode = bNode;
        this.token = (TraciToken) token;
    }

    @Override
    public TraciValue eval(final Context context) throws FunctionReturnException, InterpreterRuntimeException
    {
        final TraciValue a = aNode.eval(context);
        final TraciValue b = bNode.eval(context);

        final TraciValue.Type aType = a.getType();
        final TraciValue.Type bType = b.getType();

        final Object res;

        switch (aType)
        {
        case NUMBER:
            switch (bType)
            {
            case NUMBER: res = calc(a.getNumber(), b.getNumber()); break;
            case VECTOR: res = calc(a.getNumber(), b.getVector()); break;
            case COLOR:  res = calc(a.getNumber(), b.getColor()); break;
            default:     res = null; break;
            }
            break;

        case VECTOR:
            switch (bType)
            {
            case NUMBER: res = calc(a.getVector(), b.getNumber()); break;
            case VECTOR: res = calc(a.getVector(), b.getVector()); break;
            default:     res = null; break;
            }
            break;

        case BOOLEAN:
            switch (bType)
            {
            case BOOLEAN: res = calc(a.getBoolean(), b.getBoolean()); break;
            default :     res = null; break;
            }
            break;

        case COLOR:
            switch (bType)
            {
            case NUMBER: res = calc(a.getColor(), b.getNumber()); break;
            case COLOR:  res = calc(a.getColor(), b.getColor()); break;
            default:     res = null; break;
            }
            break;

        default:
            res = null;
            break;
        }

        if (res == null)
        {
            throw new InterpreterIllegalOperatorArgument(token.location, context.callStack,
                    aType, token.getText(), bType);
        }

        return new TraciValue(res);
    }

    private Object calc(final Double a, final Double b)
    {
        switch (op)
        {
        case BINARY_ADD:  return Double.valueOf(a + b);
        case BINARY_SUB:  return Double.valueOf(a - b);
        case BINARY_MUL:  return Double.valueOf(a * b);
        case BINARY_DIV:  return Double.valueOf(a / b);
        case COMPARE_LT:  return Boolean.valueOf(a < b);
        case COMPARE_LTE: return Boolean.valueOf(a <= b);
        case COMPARE_GT:  return Boolean.valueOf(a > b);
        case COMPARE_GTE: return Boolean.valueOf(a >= b);
        case COMPARE_EQ:  return Boolean.valueOf(a == b);
        case COMPARE_NEQ: return Boolean.valueOf(a != b);
        default: return null;
        }
    }

    private Object calc(final Double a, final Vector b)
    {
        switch (op)
        {
        case BINARY_MUL: return b.mul(a);
        default: return null;
        }
    }

    private Object calc(final Vector a, final Double b)
    {
        switch (op)
        {
        case BINARY_MUL: return a.mul(b);
        default: return null;
        }
    }

    private Object calc(final Double a, final Color b)
    {
        switch (op)
        {
        case BINARY_MUL: return b.mul(a);
        default: return null;
        }
    }

    private Object calc(final Color a, final Double b)
    {
        switch (op)
        {
        case BINARY_MUL: return a.mul(b);
        default: return null;
        }
    }

    private Object calc(final Vector a, final Vector b)
    {
        switch (op)
        {
        case BINARY_ADD: return a.add(b);
        case BINARY_SUB: return a.sub(b);
        default: return null;
        }
    }

    private Object calc(final Color a, final Color b)
    {
        switch (op)
        {
        case BINARY_ADD: return a.add(b);
        case BINARY_SUB: return a.sub(b);
        default: return null;
        }
    }

    private Object calc(final Boolean a, final Boolean b)
    {
        switch (op)
        {
        case COMPARE_EQ:  return Boolean.valueOf(a == b);
        case COMPARE_NEQ: return Boolean.valueOf(a != b);
        default: return null;
        }
    }
}

@@ -2776,32 +2776,56 @@
 Double b)%0A    %7B%0A
+        a.compareTo(b);%0A
         switch (
@@ -2820,32 +2820,32 @@
     switch (op)%0A
-
         %7B%0A      
@@ -3116,12 +3116,25 @@
 Of(a
+.compareTo(b)
  %3C 
-b
+0
 );%0A 
@@ -3186,13 +3186,26 @@
 Of(a
+.compareTo(b)
  %3C= 
-b
+0
 );%0A 
@@ -3257,12 +3257,25 @@
 Of(a
+.compareTo(b)
  %3E 
-b
+0
 );%0A 
@@ -3327,13 +3327,26 @@
 Of(a
+.compareTo(b)
  %3E= 
-b
+0
 );%0A 
@@ -3386,37 +3386,50 @@
 oolean.valueOf(a
+.compareTo(b)
  == 
-b
+0
 );%0A        case 
@@ -3457,37 +3457,50 @@
 oolean.valueOf(a
+.compareTo(b)
  != 
-b
+0
 );%0A        defau
@@ -4847,13 +4847,26 @@
 Of(a
+.compareTo(b)
  == 
-b
+0
 );%0A 
@@ -4918,13 +4918,26 @@
 Of(a
+.compareTo(b)
  != 
-b
+0
 );%0A 

Correct number comparing.