/** represents a bus of wires, not the big red type
 * Daniel Hulme, July 2004
 */
public class Bus {
  private boolean wires[];
  private int width;
  
  /** Constructs a new bus of the given width.
   * @param width the width of the bus
   */
  public Bus(int width) {
    wires = new boolean[width];
    this.width = width;
  }

  /** As expected, does a deep copy of this Bus.
   * @return a deep copy of this bus
   */
  public Object clone() {
    Bus evilTwin;
    evilTwin = new Bus(width);
    for(int i=0; i < width; i++) {
      evilTwin.setBit(i,this.getBit(i));
    }
    return evilTwin;
  }

  /** Gives you a String binary representation of the bits on this Bus.
   * @return the binary representation of the bits on this bus
   */
  public String toString() {
    StringBuffer buf = new StringBuffer(width);
    for (int i=0; i<width; i++) {
      buf.append((getBit(i)?"1":"0"));
    }
    return buf.reverse().toString();
  }

  /** Gives you a String hexadecimal representation of the bits on this Bus.
   * @return the hexadecimal representation of the bits of this bus
   */
  public String toHexString() {
    int len = (int)Math.ceil(width/4);
    StringBuffer buf = new StringBuffer(len);
    int i;
    for (i=0; i<len-1; i++) {
      byte b = extract(i*4,4).getByte();
      buf.append(b<=9 ? b+'0' : b-10+'a');
    }
    // now do the top n bits (n<=4)
    byte b = subscript(width-1,i*4).getByte();
    buf.append(b<=9 ? b+'0' : b-10+'a');
    return buf.reverse().toString();
  }
  
  /** Returns a smaller bus of width w containing w bits of this bus starting from bit l.
   * @param l The index of the low bit in this bus that bit 0 in the returned bit will be set to.
   * @param w The width of the new bus.
   * @return a bus containing bits this[w+l:l]
   *
   * For efficiency and ease-of-use (read: not having a very long throws
   * clause) this function delegates all checking to the Java array bounds
   * checker. Don't call it with stupid values or your program will get
   * OOBExn'd to Hell and back.
   */
  public Bus extract(int l, int w) {
    Bus baby;
    baby = new Bus(w);
    for (int i=0; i < w; i++) {
      baby.setBit(i,this.getBit(i+l));
    }
    return baby;
  }

  /** Convenience function to return a bus equivalent to this[h:l] in Verilog.
   * @param h The index of the highest bit to return.
   * @param l The index of the lowest bit to return.
   * @return a bus containing bits this[h:l]
   */
  public Bus subscript(int h, int l) {
    return extract(l, 1+h-l);
  }
    
  /** Returns a huge bus consisting of two buses concatenated together.
   * @param high The bus to go in the high bits of the new bus.
   * @param low The bus to go in the low bits of the new bus.
   * @return a bus containing {high,low}
   */
  public static Bus concatenate(Bus high, Bus low) {
    Bus huge;
    huge = new Bus(low.width+high.width);
    for (int i=0; i<low.width; i++) {
      huge.setBit(i, low.getBit(i));
    }
    for (int i=0; i < high.width; i++) {
      huge.setBit(i+low.width, high.getBit(i));
    }
    return huge;
  }

  /**
   * Sets the given bit to the given value.
   * @param n which bit to alter
   * @param value the value to set it to
   */
  public void setBit(int n, boolean value) {
    wires[n] = value;
  }

  /** Retrieves the value of the given bit.
   * @param n the index of the bit to return
   * @return bit n of the bus, where 0 is the LSB
   */
  public boolean getBit(int n) {
    return wires[n];
  }

  /** Returns the bottom 16 bits of this bus as a char.
   * If the bus is less than 16b wide, the top (16 - width) bits will be zero.
   * @return the bottom 16 bits packed into a char
   */
  public char getChar() {
    char mander = 0;
    int w = width<=16 ? width : 16;

    for (int i=0; i<w; i++) {
      mander |= (getBit(i)?1:0)<<i;
    }
    return mander;
  }

  /** Returns the bottom 16 bits of this bus as a signed short.
   * If the bus is narrower than 16b, it will be sign-extended to the top (16 - width) bits.
   * For buses representing a -ve number, getShort() returns a different value to getChar().
   * @return the bottom 16 bits sign-extended as a short
   */
  public short getShort() {
    short fuse = 0;
    int w = width<=16 ? width : 16;

    for (int i=0; i<w; i++) {
      fuse |= (getBit(i)?1:0)<<i;
    }
    if (w<16 && getBit(width-1)) {
      short sign = -1;
      fuse |= sign << (width-1);
    }
    return fuse;  
  }

  /** Returns the bottom 8 bits of this bus as an unsigned byte.
   * If the bus is narrower than 8b, the top (8 - width) bits will be zero.
   * @return the bottom 8 bits as a byte
   */
  public byte getByte() {
    byte bag = 0;
    int w = width<=16 ? width : 8;
    for (int i=0; i<w; i++) {
      bag |= (getBit(i)?1:0)<<i;
    }
    return bag;
  }

  /** Returns the bottom 32 bits of this bus as an int.
   * If the bus is narrower than 32b, it will be sign-extended.
   * @return the bottom 32 bits sign-extended as an int
   */
  public int getInt() {
    int ern = 0;
    int w = width<=32 ? width : 32;

    for (int i=0; i<w; i++) {
      ern |= (getBit(i)?1:0)<<i;
    }
    if (w<32 && getBit(width-1)) {
      int sign = -1;
      ern |= sign << (width-1);
    }
    return ern;
  }

  /** Changes the bottom 8 bits of the bus to be equal to the given byte.
   * If the bus is narrower than 8b, the top (8 - width) bits of the argument are ignored.
   * If the bus is wider than 8b, the top (width - 8) bits of the bus become undefined.
   * @param val the bits to put on the bus
   */
  public void write(byte val) {
    int w = width<=8 ? width : 8;
    for (int i=0; i<w; i++) {
      setBit(i,(val%2==1));
      val>>=1;
    }
  }

  /** Changes the bottom 16 bits of the bus to be equal to the given char.
   * You can use this method safely with a short as it doesn't care about signedness.
   * If the bus is narrower than 16b, the top (16 - width) bits of the argument are ignored.
   * If the bus is wider than 16b, the top (width - 16) bits of the bus become undefined.
   * @param val the bits to put on the bus
   */
  public void write(char val) {
    int w = width<=16 ? width : 8;
    for (int i=0; i<w; i++) {
      setBit(i,(val%2==1));
      val >>=1;
    }
  }

  /** Changes the bottom 32 bits of the bus to be equal to the given int.
   * If the bus is narrower than 32b, the top (32 - width) bits of the argument are ignored.
   * If the bus is wider than 32b, the top (width - 32) bits of the bus become undefined.
   * @param val the bits to put on the bus
   */
  public void write(int val) {
    int w = width <= 32 ? width : 32;
    for (int i=0; i<2; i++) {
      setBit(i, (val%2==1));
      val >>=1;
    }
  }
  
  /** Performs an unary-reduction and on this bus.
   * @return Verilog &this
   */
  public boolean and() {
    boolean result = getBit(0);
    for (int i=1;i<width;i++)
      result &= getBit(i);
    return result;
  }
  
  /** Performs an unary-reduction or on this bus.
   * @return Verilog |this
   */
  public boolean or() {
    boolean result = getBit(0);
    for (int i=1;i<width;i++)
      result |= getBit(i);
    return result;
  }
  
  /** Performs an unary-reduction xor on this bus.
   * @return Verilog ^this
   */
  public boolean xor() {
    boolean result = getBit(0);
    for (int i=1;i<width;i++)
      result ^= getBit(i);
    return result;
  }
  
  /** Performs an unary not on this bus.
   */
  public void not() {
    for (int i=0;i<width;i++)
        setBit(i,!getBit(i));
  }
  
  /** Sets the bits on this bus to be equal to (this & b)
   */
  public void and(Bus b) {
    for (int i=0;i<width;i++)
      setBit(i,getBit(i)&b.getBit(i));
  }
  
  /** Sets the bits on this bus to be equal to (this | b)
   */
  public void or(Bus b) {
    for (int i=0; i<width; i++)
      setBit(i,getBit(i)|b.getBit(i));
  }
  
  /** Sets the bits on this bus to be equal to (this ^ b)
   */
  public void xor(Bus b) {
    for (int i=0; i<width; i++)
      setBit(i, getBit(i)^b.getBit(i));
  }
}
