/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 
 * $Id: OPcondtnl.java,v 1.3 2000/09/09 19:51:48 metlov Exp $
 *
 * This file is part of the Java Expressions Library (JEL).
 *   For more information about JEL visit :
 *    http://galaxy.fzu.cz/JEL/
 *
 * (c) 1998 -- 2000 by Konstantin Metlov(metlov@fzu.cz);
 *
 * JEL is Distributed under the terms of GNU General Public License.
 *    This code comes with ABSOLUTELY NO WARRANTY.
 *  For license details see COPYING file in this directory.
 */

package gnu.jel;

import gnu.jel.debug.Debug;

public class OPcondtnl extends OPfunction {
  OPlist trueList;
  OPlist falseList;
  
  /**
   * Creates conditional operator.
   * typeStk should contain <boolean> <result of the 1st branch> 
   * <result of the 2nn branch>. All these are popped and replaced by the 
   * result type of conditional.
   * @param typesStk stack holding current types.
   * @param list list for type conversion operations to be appended
   * @param trueList operations of "true" branch.
   * @param falseList operations of "false" branch.
   */
  public OPcondtnl(TypesStack typesStk,OPlist list, OPlist trueList, 
                   OPlist falseList) throws CompilationException {
    int type2ID=typesStk.peekID();
    Class type2=typesStk.pop();
    int type1ID=typesStk.peekID();
    Class type1=typesStk.pop();
    int argID=typesStk.peekID();
    if (TypesStack.unwrapType[argID]!=0) // first argument must be boolean
      throw new CompilationException(-1,23,null);
    if (argID!=0) // unwrap
      list.addLast(new OPunary(typesStk,0,null,false));
    typesStk.pop();

    // determine the result type according to JLS 15.24
    if (type2==type1) {
      resID=type1ID;
      resType=type1;
    } else {
      int type1IDunwrp=TypesStack.baseType[TypesStack.unwrapType[type1ID]];
      int type2IDunwrp=TypesStack.baseType[TypesStack.unwrapType[type2ID]];
      boolean found=false;
      if ((TypesStack.baseType[type1ID]==8) && 
          (TypesStack.baseType[type2ID]==8)){
        // try references match
        resID=8;
        found=true;
        if ((type1==null) && (type2==null))
          resType=null;
        else if (((type1==null) && (type2!=null)) || 
                 type2.isAssignableFrom(type1) ){
          resType=type2;
          resID=type2ID;
        } else if (((type1!=null) && (type2==null)) ||
                   type1.isAssignableFrom(type2)){
          resID=type1ID;
          resType=type1;
        } else { // Branches are incompatible references
          if ((type1IDunwrp==type1ID) && (type2IDunwrp==type2ID)) {
            // if can't unwrap it is an error
            Object[] paramsExc={type1,type2};
            throw new CompilationException(-1,24,paramsExc);
          } else found=false; // try to unwrap
        };
      };
      if (!found) { // try to unwrap
        if (((type1IDunwrp==1) && (type2IDunwrp==3)) || 
            ((type1IDunwrp==3) && (type2IDunwrp==1))) 
          resID=3;
        else {
          resID=OPbinary.promotions[type1IDunwrp][type2IDunwrp];
          if (resID<0) {
            Object[] paramsExc={type1,type2};
            throw new CompilationException(-1,24,paramsExc);
          };
        };
      };
    };
    
    // convert types
    if ((type1ID!=resID) || ((resID==8) && (type1!=null) && 
                             (type1!=resType))) {
      typesStk.pushID(type1ID,type1);
      trueList.addLast(new OPunary(typesStk,resID,resType,false));
      typesStk.pop();
    };
    
    if ((type2ID!=resID) || ((resID==8) && (type2!=null) && 
                             (type2!=resType))) {
      typesStk.pushID(type2ID,type1);
      falseList.addLast(new OPunary(typesStk,resID,resType,false));
      typesStk.pop();
    };

    typesStk.pushID(resID,resType);

    this.trueList=trueList;
    this.falseList=falseList;
  };
  
  /**
   * Returns number of parameters for this function.
   */
  public int getNParams() {
    return 1;
  };

  protected void compile(ClassFile cf) {
    cf.branch_true();
    trueList.compile(cf);
    cf.branch_false();
    falseList.compile(cf);
    cf.branch_end();
  };

  protected void eval(OPlist list) {
    trueList.performCF();
    falseList.performCF();
    if (! (prev instanceof OPload)) return; // operand is not ready
    
    OPload opl=(OPload) prev;
    OPlist replList;
    if (((Boolean)opl.what).booleanValue())
      replList=trueList;
    else
      replList=falseList;
    
    OP curr=replList.getLast();
    while (curr!=null) {
      OP prv=curr.prev;
      list.addAfter(this,curr);
      curr=prv;
    };
    
    list.remove(this.prev);
    list.remove(this);
  };

  public String toString() {
    StringBuffer res=new StringBuffer("?(");
    res.append(trueList.toString());
    res.append("):(");
    res.append(falseList.toString());
    res.append(")");
    return res.toString();
  };
};
