/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*   This file is part of the Java Expressions Library (JEL).
*   For more information about JEL visit : http://fti.dn.ua/JEL/
*
*   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2006, 2007, 2009 Konstantin L. Metlov
*
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program 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 General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package gnu.jel;

import gnu.jel.debug.Debug;
import java.util.Stack;

/**
 * A tree node, representing conditional.
 */
public class OPcondtnl extends OP {
  /**
   * Creates conditional operator.
   * <P>On entry the paramOPs should contain <boolean> <result of the
   * 1st branch> <result of the 2nn branch>.
   * @param paramOPs stack holding the operands
   */
  public OPcondtnl(Stack<OP> paramOPs) 
    throws CompilationException {
    
    chi=new OP[3];
    for (int i=2;i>=0;i--)
      chi[i]=paramOPs.pop();

    int type2ID=chi[2].resID;
    Class type2=chi[2].resType;
    int type1ID=chi[1].resID;
    Class type1=chi[1].resType;
    int argID=chi[0].resID;

    if (unwrapType[argID]!=0) // first argument must be boolean
      throw new CompilationException(23,null);
    if (argID!=0) { // unwrap
      paramOPs.push(chi[0]);
      chi[0]=new OPunary(paramOPs,0,null,false);
    };

    // determine the result type according to JLS 15.24
    resID=-1;

    if ((type1ID>=8) && (type2ID>=8)) {
      // references
      if (isWidening(type1ID, type1, type2ID, type2)) {
        resID=type2ID;
        resType=type2;
      } else if (isWidening(type2ID, type2, type1ID, type1)) {
        resID=type1ID;
        resType=type1;
      };
      // otherwise both must unwrap to primitives, which is checked next
    };

    if (resID<0) {
      // if reference conversion did not work
      int type1IDunwrp;
      int type2IDunwrp;

      if ((resID=type1IDunwrp=unwrapType[type1ID])!=
          (type2IDunwrp=unwrapType[type2ID])) {
        if (((type1IDunwrp==1) && (type2IDunwrp==3)) || 
            ((type1IDunwrp==3) && (type2IDunwrp==1)))
          resID=3;
        else {
          if ((type1IDunwrp>=8) || (type2IDunwrp>=8) || 
              ((resID=OPbinary.promotions[type1IDunwrp][type2IDunwrp])<0)) {
            Object[] paramsExc={type1,type2};
            throw new CompilationException(24,paramsExc);
          };
        };
      };
      resType=specialTypes[resID]; // here it's always the primitive
    };
    
    // convert types
    if ((type1ID!=resID) || ((resID==8) && (type1!=null) && 
                             (type1!=resType))) {
      paramOPs.push(chi[1]);
      chi[1]=new OPunary(paramOPs,resID,resType,false);
    };
    
    if ((type2ID!=resID) || ((resID==8) && (type2!=null) && 
                             (type2!=resType))) {
      paramOPs.push(chi[2]);
      chi[2]=new OPunary(paramOPs,resID,resType,false);
    };
  };

  public void compile(ClassFile cf) {
    chi[0].compile(cf);
    if (chi[1]!=null) {
      // in the case condition was impossible to evaluate at compile-time
      cf.code(0xE4); // start "true" branch
      chi[1].compile(cf);
      cf.code(0xE5); // finish "true" branch / start "false" branch
      chi[2].compile(cf);
      cf.code(0xE6); // finish "false" branch
    };
  };

  public Object eval() throws Exception {
    boolean cond;
    try {
      cond=((Boolean)chi[0].eval()).booleanValue();
    } catch (Exception e) {
      try {
        chi[1]=new OPload(chi[1],chi[1].eval());
      } catch (Exception exc) {
      };
      try {
        chi[2]=new OPload(chi[2],chi[2].eval());
      } catch (Exception exc) {
      };
      throw e;
    };
    
    OP rop=cond?chi[1]:chi[2];
        
    try {
      return rop.eval();
    } catch (Exception e) {
      // if can't eval, but know the condition
      chi[0]=rop;
      chi[1]=null;
      chi[2]=null;
      throw e;
    }
  };

};
