package janala.instrument;

/** An object to keep track of (classId, methodId, instructionId) tuples during
 instrumentation. */
public class GlobalStateForInstrumentation {
  public static GlobalStateForInstrumentation instance = new GlobalStateForInstrumentation();
  private int iid = 0;
  private int mid = 0;
  private int cid = 0;

  // JQF's Fast Coverage implementation uses a plain int, no bit packing, no truncation errors
  private int fastCoverageIID = 0;
  public int incAndGetFastCoverageId(){
    fastCoverageIID++;
    return fastCoverageIID;
  }


  // When one gets the id, she gets the result of merging all three ids.
  // NOTE: Beaware of truncation errors.
  private final static int CBITS = 10;  // CID occupies the upper 10 bits
  private final static int MBITS = 10;  // MID occupies the next 10 bits
  private final static int IBITS = 32 - CBITS - MBITS;  // MID occupies the remaining 12 bits

  public int incAndGetId() {
    iid++;
    validate(iid, IBITS);
    return getId();
  }

  public int getId() {
    return (cid << (32 - CBITS)) + (mid << (32 - CBITS - MBITS)) + iid;
  }

  public int getMid() {
    return mid;
  }

  public void incMid() {
    this.mid++;
    validate(mid, MBITS);
    this.iid = 0;
  }

  public int getCid() {
    return cid;
  }

  public void setCid(int cid) {
    cid = Math.abs(cid) % (1 << CBITS);
    this.iid = 0;
    this.mid = 0;
    this.cid = cid;
  }

  private void validate(int id, int bits) {
    if (id < 0 || id >= (1 << bits)) {
      throw new IllegalArgumentException("Invalid instruction ID range");
    }
  }
}
