/*
 * Copyright (c) 2002,2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 */
package com.ibm.wala.shrikeBT;

/**
 * This class represents comparisons between floats, longs and doubles.
 */
final public class ComparisonInstruction extends Instruction implements IComparisonInstruction {
  protected ComparisonInstruction(short opcode) {
    super(opcode);
  }

  private final static ComparisonInstruction preallocatedLCMP = new ComparisonInstruction(OP_lcmp);

  private final static ComparisonInstruction[] preallocatedFloatingCompares = preallocateFloatingCompares();

  private static ComparisonInstruction[] preallocateFloatingCompares() {
    ComparisonInstruction[] r = new ComparisonInstruction[OP_dcmpg - OP_fcmpl + 1];
    for (short i = OP_fcmpl; i <= OP_dcmpg; i++) {
      r[i - OP_fcmpl] = new ComparisonInstruction(i);
    }
    return r;
  }

  public static ComparisonInstruction make(String type, Operator operator) throws IllegalArgumentException {
    int t = Util.getTypeIndex(type);
    switch (t) {
    case TYPE_long_index:
      if (operator != Operator.CMP) {
        throw new IllegalArgumentException("Operator " + operator + " is not a valid comparison operator for longs");
      } else {
        return preallocatedLCMP;
      }
    case TYPE_float_index:
    case TYPE_double_index:
      if (operator == Operator.CMP) {
        throw new IllegalArgumentException("Operator " + operator + " is not a valid comparison operator for floating point values");
      } else {
        return preallocatedFloatingCompares[(operator.ordinal() - Operator.CMPL.ordinal()) + (t - TYPE_float_index) * 2];
      }
    default:
      throw new IllegalArgumentException("Type " + type + " cannot be compared");
    }
  }

  @Override
  public boolean equals(Object o) {
    if (o instanceof ComparisonInstruction) {
      ComparisonInstruction i = (ComparisonInstruction) o;
      return i.opcode == opcode;
    } else {
      return false;
    }
  }

  /**
   * @return OPR_cmp (for long), OPR_cmpl, or OPR_cmpg (for float and double)
   */
  @Override
  public Operator getOperator() {
    switch (opcode) {
    case OP_lcmp:
      return Operator.CMP;
    case OP_fcmpl:
    case OP_dcmpl:
      return Operator.CMPL;
    case OP_dcmpg:
    case OP_fcmpg:
      return Operator.CMPG;
    default:
      throw new Error("Unknown opcode");
    }
  }

  @Override
  public String getType() {
    switch (opcode) {
    case OP_lcmp:
      return TYPE_long;
    case OP_fcmpg:
    case OP_fcmpl:
      return TYPE_float;
    case OP_dcmpl:
    case OP_dcmpg:
      return TYPE_double;
    default:
      throw new Error("Unknown opcode");
    }
  }

  @Override
  public int hashCode() {
    return opcode + 1391901;
  }

  @Override
  public int getPoppedCount() {
    return 2;
  }

  @Override
  public String getPushedType(String[] types) {
    return Constants.TYPE_boolean;
  }

  @Override
  public byte getPushedWordSize() {
    return 1;
  }

  @Override
  public void visit(IInstruction.Visitor v) throws NullPointerException {
    v.visitComparison(this);
  }

  @Override
  public String toString() {
    return "Comparison(" + getType() + ',' + getOperator() + ')';
  }

  @Override
  public boolean isPEI() {
    return false;
  }
}
