/*
 * 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.shrike.shrikeBT;

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

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

  private static final 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;
  }
}
