/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 
 * $Id: OPcondtnl.java,v 1.1 2000/02/11 22:08:18 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 trueList operations of "true" branch.
   * @param falseList operations of "false" branch.
   */
  public OPcondtnl(TypesStack typesStk,OPlist trueList, 
                   OPlist falseList) throws IllegalStateException {
    int type2ID=typesStk.peekID();
    Class type2=typesStk.pop();
    int type1ID=typesStk.peekID();
    Class type1=typesStk.pop();
    if (typesStk.peekID()!=0)
      throw new IllegalStateException("First argument of conditional must "+
                                      "be of boolean type.");
    typesStk.pop();

    // determine the result type according to JLS 15.24
    if (type2!=type1) {
      if ((type1ID<8) && (type2ID<8)) {
        int sID=Math.min(type1ID,type2ID);
        int lID=Math.max(type1ID,type2ID);
        if (((type1ID==1) && (type2ID==3)) || 
            ((type1ID==3) && (type2ID==1))) 
          resID=3;
        else
          resID=OPbinary.promotions[type1ID][type2ID];
      } else if ((type1ID==8) && (type2ID==8)) {
        resID=8;
        if ((type1==null) && (type2==null))
          resType=null;
        else if ((type1==null) && (type2!=null))
          resType=type2;
        else if ((type1!=null) && (type2==null)) 
          resType=type1;
        else if (type1.isAssignableFrom(type2))
          resType=type1;
        else if (type2.isAssignableFrom(type1))
          resType=type2;
        else throw new IllegalStateException("Branches of conditional "+
                                             "have reference types which "+
                                             "are not comptible.");
      } else
        throw new IllegalStateException("Branches of conditional should "+
                                        "be either both of primitive types"+
                                        " or both be references.");
    } else {
      resID=type1ID;
      resType=type1;
    };
    
    // convert types
    if (type1!=resType) {
      typesStk.pushID(type1ID,type1);
      trueList.addLast(new OPunary(typesStk,resID,resType,false));
      typesStk.pop();
    };

    if (type2!=resType) {
      typesStk.pushID(type2ID,type2);
      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();
  };
};
