package IR2;

import java.util.*;

public class DelocalizedBinaryOpInstruction extends DelocalizedInstruction {

  public static final int ADD = 1;
  public static final int SUB = 5;
  public static final int MUL = 2;
  public static final int EQ  = 6;
  public static final int NE  = 11;
  public static final int GE  = 7;
  public static final int GT  = 8;
  public static final int LE  = 9;
  public static final int LT  = 10;
  public static final int SHL = 3;
  public static final int SHR = 4;

  private int op_num;
  private DelocalizedLValue result;
  private DelocalizedRValue da1, da2;

  public DelocalizedBinaryOpInstruction(int op_num, DelocalizedLValue result, 
                                        DelocalizedRValue da1, 
                                        DelocalizedRValue da2) {
    this.op_num = op_num;
    this.result = result;
    this.da1 = da1;
    this.da2 = da2;
  }
  public Enumeration lsources() {
    if ((da1 instanceof DelocalizedLValue) && (da2 instanceof DelocalizedLValue)) {
      return new ShortEnumeration(da1, da2);
    } else if ((da1 instanceof DelocalizedLValue) && !(da2 instanceof DelocalizedLValue)) {
      return new ShortEnumeration(da1);
    } else if (!(da1 instanceof DelocalizedLValue) && (da2 instanceof DelocalizedLValue)) {
      return new ShortEnumeration(da2);
    } else {
      return new ShortEnumeration();
    }
  }

  public DelocalizedLValue destination() {
    return result;
  }

  public void generate_code(Codegen c){
    DelocalizedLValue presult = result.lproxy();
    DelocalizedRValue pa1 = da1.rproxy(), pa2 = da2.rproxy();

    // MIPS specific.  Watch me not care.
    DelocalizedRegister rresult = null, ra1 = null, ra2 = null;
    DelocalizedImmed ia2 = null;

    if (presult instanceof DelocalizedRegister) {
      rresult = (DelocalizedRegister) presult;
    } else {
      rresult = c.r(2);
      // do store_from_reg at end
    }

    if ((op_num == ADD || op_num == MUL || op_num == EQ || op_num == NE) &&
        pa1 instanceof DelocalizedImmed &&
        ((DelocalizedImmed) pa1).get_value() < 32768 &&
        ((DelocalizedImmed) pa1).get_value() >= -32768) {
      ia2 = (DelocalizedImmed) pa1;
    } else if (pa1 instanceof DelocalizedRegister) {
      ra1 = (DelocalizedRegister) pa1;
    } else {
      ra1 = c.r(0);
      pa1.load_into_reg(c, c.r(0));
    }

    if (ia2 == null) {
      if (pa2 instanceof DelocalizedImmed &&
          ((DelocalizedImmed) pa2).get_value() < 32768 &&
          ((DelocalizedImmed) pa2).get_value() >= -32768) {
        ia2 = (DelocalizedImmed) pa2;
      } else if (pa2 instanceof DelocalizedRegister) {
        ra2 = (DelocalizedRegister) pa2;
      } else {
        ra2 = c.r(1);
        pa2.load_into_reg(c, c.r(1));
      }
    } else {
      if (pa2 instanceof DelocalizedRegister) {
        ra1 = (DelocalizedRegister) pa2;
      } else {
        ra1 = c.r(0);
        pa2.load_into_reg(c, c.r(0));
      }
    }

    if (ia2 == null) {
      switch (op_num) {
      case ADD:
        c.generate_add(rresult, ra1, ra2);
        break;
      case SUB:
        c.generate_sub(rresult, ra1, ra2);
        break;
      case MUL:
        c.generate_mulo(rresult, ra1, ra2);
        break;
      case EQ:
        c.generate_seq(rresult, ra1, ra2);
        break;
      case NE:
        c.generate_sne(rresult, ra1, ra2);
        break;
      case GE:
        c.generate_sge(rresult, ra1, ra2);
        break;
      case GT:
        c.generate_sgt(rresult, ra1, ra2);
        break;
      case LE:
        c.generate_sle(rresult, ra1, ra2);
        break;
      case LT:
        c.generate_slt(rresult, ra1, ra2);
        break;
      case SHL:
        c.generate_shl(rresult, ra1, ra2);
        break;
      case SHR:
        c.generate_shr(rresult, ra1, ra2);
        break;
      default:
        System.out.println("DelocalizedBinaryOpInstruction: Unknown binop, Badness 10000!");
      }
    } else {
      switch (op_num) {
      case ADD:
        c.generate_add(rresult, ra1, ia2);
        break;
      case SUB:
        c.generate_sub(rresult, ra1, ia2);
        break;
      case MUL:
        c.generate_mulo(rresult, ra1, ia2);
        break;
      case EQ:
        c.generate_seq(rresult, ra1, ia2);
        break;
      case NE:
        c.generate_sne(rresult, ra1, ia2);
        break;
      case GE:
        c.generate_sge(rresult, ra1, ia2);
        break;
      case GT:
        c.generate_sgt(rresult, ra1, ia2);
        break;
      case LE:
        c.generate_sle(rresult, ra1, ia2);
        break;
      case LT:
        c.generate_slt(rresult, ra1, ia2);
        break;
      case SHL:
        c.generate_shl(rresult, ra1, ia2);
        break;
      case SHR:
        c.generate_shr(rresult, ra1, ia2);
        break;
      default:
        System.out.println("DelocalizedBinaryOpInstruction: Unknown binop, Badness 10000!");
      }
    }

    if (!(presult instanceof DelocalizedRegister)) {
      presult.store_from_reg(c, c.r(2));
    }
  }


  public String toString() {
    return "BinOp: " + result + "<-("+ op_num+") " + da1 + " " +da2;


  }

  // Substitution. For swapping in a symbolic register
  public void substitute_lvalue(DelocalizedLValue oldlval, 
                                DelocalizedLValue newlval) {
    if (result.lproxy() == oldlval) result = newlval;
  }

  public void substitute_rvalue(DelocalizedLValue oldlval, 
                                DelocalizedLValue newlval) {
    if (da1.rproxy() == oldlval) da1 = (DelocalizedRValue)newlval;
    if (da2.rproxy() == oldlval) da2 = (DelocalizedRValue)newlval;
  }

  /* Copy propagation */
  public boolean kills_copy(CopyInstruction copy) {
    return result.equals(copy.copy_source())
      || result.equals(copy.copy_destination());
  }
  public void substitute_copies(Hashtable map) {
    if (map.contains(da1))
      da1 = (DelocalizedRValue) map.get(da1);
    if (map.contains(da2))
      da2 = (DelocalizedRValue) map.get(da2);
  }
}
