/*
 * This file is part of JICI, licensed under the MIT License (MIT).
 *
 * Copyright (c) 2015-2015 Aleksi Sapon <http://sapon.ca/jici/>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package ca.sapon.jici.parser.expression;

import ca.sapon.jici.evaluator.Environment;
import ca.sapon.jici.evaluator.value.ObjectValue;
import ca.sapon.jici.evaluator.value.Value;
import ca.sapon.jici.evaluator.value.ValueKind;
import ca.sapon.jici.evaluator.value.type.ObjectUnionValueType;
import ca.sapon.jici.evaluator.value.type.PrimitiveValueType;
import ca.sapon.jici.evaluator.value.type.ValueType;
import ca.sapon.jici.lexer.literal.number.IntLiteral;

public class Conditional implements Expression {
    private final Expression test;
    private final Expression left;
    private final Expression right;
    private ValueType valueType = null;
    private Value value = null;

    public Conditional(Expression test, Expression left, Expression right) {
        this.test = test;
        this.left = left;
        this.right = right;
    }

    @Override
    public ValueType geValueType(Environment environment) {
        if (valueType == null) {
            final ValueType testType = test.geValueType(environment).unbox();
            final ValueType leftType = left.geValueType(environment).unbox();
            final ValueType rightType = right.geValueType(environment).unbox();
            if (!testType.isBoolean()) {
                throw new IllegalArgumentException("Not a boolean: " + testType.getName());
            }
            if (leftType.is(rightType.getTypeClass())) {
                // both same type to that type
                valueType = leftType;
            } else if (leftType.isObject() || rightType.isObject()) {
                // for objects return a union
                valueType = new ObjectUnionValueType(leftType.box(),  rightType.box());
            } else if (left instanceof IntLiteral && rightType.canNarrowTo(((IntLiteral) left).asInt())) {
                // left constant numeric that narrows to right, use right
                valueType = rightType;
            } else if (right instanceof IntLiteral && leftType.canNarrowTo(((IntLiteral) right).asInt())) {
                // right constant numeric that narrows to left, use left
                valueType = leftType;
            } else if (leftType.is(byte.class) && rightType.is(short.class) || leftType.is(short.class) && rightType.is(byte.class)) {
                // one byte and other short to short
                valueType = PrimitiveValueType.of(short.class);
            } else {
                // else use binary widening
                valueType = leftType.binaryWiden(rightType.getTypeClass());
            }
        }
        return valueType;
    }

    @Override
    public Value getValue(Environment environment) {
        if (value == null) {
            final Value tesValue = test.getValue(environment);
            final Value leftValue = left.getValue(environment);
            final Value rightValue = right.getValue(environment);
            final ValueKind widenKind = valueType.getKind();
            value = doConditional(tesValue, leftValue, rightValue, widenKind);
        }
        return value;
    }

    private Value doConditional(Value testValue, Value leftValue, Value rightValue, ValueKind widenKind) {
        final Value resultValue = testValue.asBoolean() ? leftValue : rightValue;
        if (resultValue.getKind() == widenKind) {
            // prevent useless conversion
            return resultValue;
        }
        if (resultValue instanceof ObjectValue) {
            // prevent useless reboxing
            return resultValue;
        }
        return widenKind.convert(resultValue);
    }

    @Override
    public String toString() {
        return "Conditional(" + test + " ? " + left + " : " + right + ")";
    }
}

@@ -1870,40 +1870,8 @@
 ull;
-%0A    private Value value = null;
 %0A%0A  
@@ -2221,38 +2221,32 @@
 ();%0A            
-final 
 ValueType leftTy
@@ -2275,32 +2275,24 @@
 environment)
-.unbox()
 ;%0A          
@@ -2285,38 +2285,32 @@
 t);%0A            
-final 
 ValueType rightT
@@ -2341,32 +2341,24 @@
 environment)
-.unbox()
 ;%0A          
@@ -2606,32 +2606,39 @@
                 
+return 
 valueType = left
@@ -2648,37 +2648,128 @@
 e;%0A            %7D
- else
+%0A            leftType = leftType.unbox();%0A            rightType = rightType.unbox();%0A           
  if (leftType.is
@@ -2858,32 +2858,39 @@
                 
+return 
 valueType = new 
@@ -2925,17 +2925,16 @@
 e.box(),
- 
  rightTy
@@ -2949,37 +2949,44 @@
 );%0A            %7D
- else
+%0A           
  if (left instan
@@ -3140,32 +3140,39 @@
                 
+return 
 valueType = righ
@@ -3187,29 +3187,36 @@
            %7D
- else
+%0A           
  if (right i
@@ -3374,32 +3374,39 @@
                 
+return 
 valueType = left
@@ -3424,21 +3424,28 @@
        %7D
- else
+%0A           
  if (lef
@@ -3614,32 +3614,39 @@
                 
+return 
 valueType = Prim
@@ -3694,20 +3694,9 @@
    %7D
- else %7B%0A    
+%0A
     
@@ -3739,27 +3739,30 @@
             
-   
+return
  valueType =
@@ -3806,38 +3806,24 @@
 peClass());%0A
-            %7D%0A
         %7D%0A  
@@ -3924,41 +3924,8 @@
 ) %7B%0A
-        if (value == null) %7B%0A    
     
@@ -3943,16 +3943,17 @@
 alue tes
+t
 Value = 
@@ -3972,36 +3972,32 @@
 e(environment);%0A
-    
         final Va
@@ -4040,36 +4040,32 @@
 nment);%0A        
-  
-  
 final Value righ
@@ -4102,36 +4102,32 @@
 nment);%0A        
-    
 final ValueKind 
@@ -4163,233 +4163,8 @@
 ();%0A
-            value = doConditional(tesValue, leftValue, rightValue, widenKind);%0A        %7D%0A        return value;%0A    %7D%0A%0A    private Value doConditional(Value testValue, Value leftValue, Value rightValue, ValueKind widenKind) %7B%0A
     

Fix conditional type checking