//a utility class for doing byte conversion
//NOTE: network byte order is BigEndian

import java.util.StringTokenizer;


public class ByteConverter {
    
    public static final int IPADDR_LEN = 4; // length of IP addr in bytes
    

    public static byte[] zeroed(byte tgt[]) {
	char nullchar = '\0';
	for (int i = 0; i < tgt.length; i++)
	    tgt[i] = (byte)nullchar;
	return tgt;
    }

    public static byte[] rightpad(byte tgt[], int len) {
	byte retval[] = new byte[len];
	retval        = zeroed(retval);
	for (int i = 0; i < tgt.length; i++) {
	    retval[i] = tgt[i];
	}
	return retval;
    }


    
    //PURPOSE: converts an IP address into a BigEndian byte format
    //PRE: addr is a valid IP address in decimal dotted format
    //RETVAL:  the byte value of addr in Big Endian
    public static byte[] BE_makeByteAddr(String addr) {
	byte ipaddr[] = new byte[IPADDR_LEN];
	StringTokenizer tok = new StringTokenizer(addr,".",false);
	int segCount = 0;
	int offset = 0;
	zeroed(ipaddr);
	while (tok.hasMoreTokens()) {
	    String segment = tok.nextToken();
	    //byte bnum[] = segment.getBytes();
	    //diff way
	    int iseg = Integer.parseInt(segment);
	    segCount +=1;
	    offset = segCount-1;
	    //assume the integer is no larger than a byte size
	    ipaddr[offset] = (byte)((iseg << 24) >>> 24);
	}
	return ipaddr;
    }


    //PURPOSE: convert a BigEndian byte stream into an IP address
    //         in decimal dotted format
    //PRE: buf[] = byte stream, pos = position in byte stream to begin
    //RETVAL: the IP address in decimal dotted format
    public static String BE_makeIPAddr(byte buf[], int pos) {
	//ip addr 4 bytes
	Byte holder;
	String seg[] = new String[4];
	String retval;
	for (int i = 0; i < 4; i++) {
	    //holder = new Byte(buf[pos +i]);
	    //seg[i] = Integer.toString(holder.intValue());
	    holder = new Byte((byte)(buf[pos + i] & 0377));
	    int tmp = holder.intValue();
	    if (tmp < 0) { 
		//due to signess in java, a byte value is also signed.
		//hence this trash.
		tmp = 127 + (128 + tmp) + 1;
	    }
	    seg[i] = Integer.toString(tmp);
	    
	}
	retval = seg[0] + "." + seg[1] + "." +
	         seg[2] + "." + seg[3];

	return retval;

    }

    
    //PURPOSE: converts a decimal digit into a Big Endian byte string
    //PRE: src = decimal digit stored as an int
    //PRE: int is 32 bits
    //RETVAL: a 32-bit byte string in Big Endian format
    public static byte[] BE_convert2Bytes(int src) {
	//an int is equivalent to 32 bits, 4 bytes
	byte tgt[] = new byte[4];
	int mask = 0xff; /* 0377 in octal*/
	
	/* this works. which set is more elegant? */
	/*
	tgt[0] = (byte)(src >>> 24);
	tgt[1] = (byte)((src << 8) >>> 24);
	tgt[2] = (byte)((src << 16) >>> 24);
	tgt[3] = (byte)((src << 24) >>> 24);
	*/
	tgt[0] = (byte) ((src >> 24) & mask);
	tgt[1] = (byte) ((src >> 16) & mask);
	tgt[2] = (byte) ((src >> 8) & mask);
	tgt[3] = (byte) (src & mask);
	/* System.out.println("byteconvert" + tgt[0] + tgt[1] + tgt[2] + tgt[3]); */
	return tgt;
    }

    //PURPOSE: converts a decimal digit into a Big Endian byte string
    //PRE: src = decimal digit stored as a short
    //PRE: short is 16 bits
    //RETVAL: a 16-bit byte string in Big Endian format
    public static byte[] BE_convert2Bytes(short src) {
	//short 16 bits, 2 bytes
	byte tgt[] = new byte[2];
	/* this works
	tgt[0] = (byte)(src >>> 8);
	tgt[1] = (byte)((src << 8) >>> 8);
	*/
	tgt[0] = (byte)(src >>> 8);
	tgt[1] = (byte)(src & 0xff);

	return tgt;
    }

    //PURPPSE: convert a decimal digit into a Big Endian byte string
    //ARG: src = decimal digit stored as a short
    //PRE: long is 64 bits
    //RETVAL: a 64-bit byte string in Big Endian format
    //NOTE: in ANSI C, long and int are both 4 bytes.
    //For this purpose, we may sometimes only need to use the lower
    //4 bytes only from src.
    public static byte[] BE_convert2Bytes(long src, boolean isfour) {
	byte val[];
	int mask = 0xff; /* 0377 in octal*/
	if (!isfour) {
	    val    = new byte[8];
	    val[0] = (byte)((src >> 56) & 0xff);
	    val[1] = (byte)((src >> 48) & 0xff);
	    val[2] = (byte)((src >> 40) & 0xff);
	    val[3] = (byte)((src >> 32) & 0xff);
	    val[4] = (byte)((src >> 24) & 0xff);
	    val[5] = (byte)((src >> 16) & 0xff);
	    val[6] = (byte)((src >> 8)  & 0xff);
	    val[7] = (byte)(src & 0xff);
	}
	else {
	    val    = new byte[4]; /* only take the lower 4 bytes */
	    val[0] = (byte)((src >> 24) & (0xff));
	    val[1] = (byte)((src >> 16) & (0xff));
	    val[2] = (byte)((src >> 8)  & (0xff));
	    val[3] = (byte)(src & 0xff);
	}
	return val;
    }

    public static byte[] BE_convert2Bytes(Long src, boolean isfour) {
	byte val[];
	if (!isfour) {
	    val    = new byte[8];
	    val[0] = (byte)((src.longValue() >> 56) & 0xff);
	    val[1] = (byte)((src.longValue() >> 48) & 0xff);
	    val[2] = (byte)((src.longValue() >> 40) & 0xff);
	    val[3] = (byte)((src.longValue() >> 32) & 0xff);
	    val[4] = (byte)((src.longValue() >> 24) & 0xff);
	    val[5] = (byte)((src.longValue() >> 16) & 0xff);
	    val[6] = (byte)((src.longValue() >> 8)  & 0xff);
	    val[7] = (byte)(src.longValue() & 0xff);
	}
	else {
	    val    = new byte[4]; /* only take the lower 4 bytes */
	    val[0] = (byte)((src.longValue() >> 24) & (0xff));
	    val[1] = (byte)((src.longValue() >> 16) & (0xff));
	    val[2] = (byte)((src.longValue() >> 8)  & (0xff));
	    val[3] = (byte)(src.longValue() & 0xff);
	}
	return val;
    }

    //PURPOSE: converts a Big Endian byte string into a short decimal
    //PRE: buf is the byte string and pos indicates where to start reading
    //RETVAL: a short decimal
    public static short BE_convertBytes2Short(byte[] buf, int pos) {
	short retval = (short)((buf[pos] << 8 ) |
			       (buf[pos+1] & 0xff));
	
	//this works too.
	/* short lsb = (short)((buf[pos+1] << 8) >>> 8); */
	/*
	short lsb    = (short)(buf[pos+1] & 0xff);
	if (lsb < 0) {
	    //due to signess of byte val in Java
	    lsb = (short)(127 + (128 + lsb) + 1);
	}
	retval += lsb;
	*/
	
	return retval;
    }

    //PURPOSE: converts a Big Endian byte string into an int decimal
    //PRE: buffer is the byte string and startPos is where the byte string starts
    //PRE: an int is assumed to be 32-bits;
    //note that in Java, an int is actually 64-bits, 32 bits of positive nums,
    // and 32-bits of negative nums
    //RETVAL: an int
    public static int BE_convertBytes2Int(byte[] buffer, int startPos) {
	/* old way
           int retval =  (int)(buffer[startPos]<<24)+
                         (int)((buffer[startPos+1]<<24)>>>8)+
                         (int)((buffer[startPos+2]<<24)>>>16)+
                         (int)((buffer[startPos+3]<<24)>>>24) ;
	*/
	/*
	System.err.println("startPos " + startPos);
	System.err.println(buffer[startPos]);
	System.err.println(buffer[startPos + 1]);
	System.err.println(buffer[startPos + 2]);
	System.err.println(buffer[startPos + 3]);
	*/
	int retval = 
	    ((buffer[startPos] << 24) & 0xff000000) |
	    ((buffer[startPos + 1] << 16) & 0xff0000) |
	    ((buffer[startPos + 2] << 8) & 0xff00) |
	    (buffer[startPos + 3] & 0xff);
	return retval;
    }

    //PRE: the assumption here is that input is a 4-byte unsigned value
    //return val is a Java long var which is 8-byte
    public static long BE_convertBytes2Long(byte[] buf, 
					    int startPos, 
					    boolean isfour) {
	long retval;
	long a,b,c,d,e,f,g,h;
	if (!isfour) {
	    a = buf[startPos];
	    b = buf[startPos + 1];
	    c = buf[startPos + 2];
	    d = buf[startPos + 3];
	    e = buf[startPos + 4];
	    f = buf[startPos + 5];
	    g = buf[startPos + 6];
	    h = buf[startPos + 7];
	    
	    if (a < 0)
		a = (127 + (128 + a) + 1L);
	    a = (a << 56) & 0xff00000000000000L;

	    if (b < 0)
		b = (127 + (128 + b) + 1L);
	    b = (b << 48) & 0x00ff000000000000L;

	    if (c < 0)
		c = (127 + (128 + c) + 1L);
	    c = (c << 40) & 0x0000ff0000000000L;

	    if (d < 0)
		d = (127 + (128 + d) + 1L);
	    d = (d << 32) & 0x000000ff00000000L;

	    if (e < 0)
		e = (127 + (128 + e) + 1L);
	    e = (e << 24) & 0x00000000ff000000L;

	    if (f < 0)
		f = (127 + (128 + f) + 1L);
	    f = (f << 16) & 0x0000000000ff0000L;

	    if (g < 0)
		g = (127 + (128 + g) + 1L);
	    g = (g << 8)  & 0x000000000000ff00L;

	    if (h < 0)
		h = (127 + (128 + h) + 1L);
	    h = h & 0xffL;

	    retval = a + b + c + d + e + f + g + h;

	}
	else {
	    a = buf[startPos];
	    if (a < 0)
		a = (127 + (128 + a) + 1L);
	    a = (a << 24) & 0xff000000;

	    b = buf[startPos + 1];
	    if (b < 0)
		b = (127 + (128 + b) + 1L);
	    b = (b << 16) & 0xff0000;

	    c = buf[startPos + 2];
	    if (c < 0)
		c = (127 + (128 + c) + 1L);
	    c = (c << 8) & 0xff00;

	    d = buf[startPos + 3];
	    if (d < 0)
		d = (127 + (128 + d) + 1L);
	    d = d & 0xff;

	    retval = a + b + c + d;

	    //the following code does not work because of the
	    //signess of primitive types in Java screws
	    //things up
	    /*
	    retval = 
		((buf[startPos] << 24) & 0xff000000) |
		((buf[startPos + 1] << 16) & 0xff0000) |
		((buf[startPos + 2] << 8) & 0xff00) |
		(buf[startPos + 3] & 0xff);
	    */
	}
	return retval;
    }

    public static byte[] LE_makeByteAddr(String addr) {
	byte ipaddr[] = new byte[IPADDR_LEN];
	StringTokenizer tok = new StringTokenizer(addr,".",false);
	int segCount = 0;
	int offset = 0;
	zeroed(ipaddr);
	while (tok.hasMoreTokens()) {
	    String segment = tok.nextToken();
	    //byte bnum[] = segment.getBytes();

	    //diff way
	    int iseg = Integer.parseInt(segment);
	    segCount +=1;
	    offset = 4 - segCount;
	    //assume the integer is no larger than a byte size
	    //ipaddr[offset] = (byte)((iseg << 24) >>> 24);
	    ipaddr[offset] = (byte)(iseg & 0xff);
	}
	return ipaddr;
    }

    public static String LE_makeIPAddr(byte buf[], int pos) {
	//ip addr 4 bytes
	Byte holder;
	String seg[] = new String[4];
	String retval;
	for (int i = 3; i >= 0; i--) {
	    //holder = new Byte(buf[pos +i]);
	    //seg[i] = Integer.toString(holder.intValue());
	    holder = new Byte((byte)(buf[pos + i] & 0377));
	    int tmp = holder.intValue();
	    if (tmp < 0) { 
		//due to signess in java, a byte value is also signed.
		//hence this trash.
		tmp = 127 + (128 + tmp) + 1;
	    }
	    seg[i] = Integer.toString(tmp);
	    
	}
	retval = seg[3] + "." + seg[2] + "." +
	         seg[1] + "." + seg[0];

	return retval;
    }

    //little endian
    public static byte[] LE_convert2Bytes(int src) {
	//an int is equivalent to 32 bits, 4 bytes
	byte tgt[] = new byte[4];
	int mask = 0377; /* 0377 in octal*/
	
	/* this works
	tgt[3] = (byte)(src >>> 24);
	tgt[2] = (byte)((src << 8) >>> 24);
	tgt[1] = (byte)((src << 16) >>> 24);
	tgt[0] = (byte)((src << 24) >>> 24);
	*/

	tgt[3] = (byte)(src >>> 24);
	tgt[2] = (byte)((src >> 16) & 0xff);
	tgt[1] = (byte)((src >> 8)  & 0xff);
	tgt[0] = (byte)(src & 0xff);

	return tgt;
    }
    public byte[] LE_convert2Bytes(short src) {
	//short 16 bits, 2 bytes
	byte tgt[] = new byte[2];
	tgt[1] = (byte)(src >>> 8);
	tgt[0] = (byte)(src & 0xff);
	return tgt;
    }
    


    public static int LE_convertBytes2Int(byte[] buffer, int startPos) {
	int retval = 
	    (buffer[startPos]            & 0xff)     |
	    ((buffer[startPos + 1] << 8) & 0xff00)   |
	    ((buffer[startPos + 2] << 16)& 0xff0000) |
	    ((buffer[startPos + 3] << 24)& 0xff000000);

	return retval;
    }

    //must handle sign in java
    public static short LE_convertBytes2Short(byte[] buf, int pos) {
	//System.err.println("lsb " + (short)buf[pos]);
	//System.err.println("msb " + (short)buf[pos+1]);
	/*
	short retval = (short)((short)(buf[pos+1] << 8) +
	               (short)buf[pos]);
	*/

	/* old way
	short retval = (short)(buf[pos+1] << 8 );
	short msb = (short)((buf[pos] << 8) >>> 8);
	if (msb < 0) {
	    //due to signess of byte val in Java
	    msb = (short)(127 + (128 + msb) + 1);
	}
	retval += msb;
	*/
	
	//new way
	short retval = (short)((buf[pos+1] << 8) |
			       (buf[pos] & 0xff));
	return retval;
    } 


    //helper fn
    //PURPOSE: copies a segment of bytes from src to dst
    //ARGS: src = source byte array
    //      dst = target byte array
    //      len = number of bytes to copy
    //   offset = where to copy to in target array
    public static byte[] slap(byte src[], byte dst[], int len, int offset) {
	int max = offset + len;
	int index = 0;
	for (int i = offset; i < max; i++) {
	    dst[i] = src[index];
	    index +=1;
	}
	return dst;
    }

    //helper fn
    //PURPOSE: concatenate two byte array
    public static byte[] concat(byte src[], byte dst[]) {
	
	if (dst == null)
	    return src;
	else {
	    byte buf[] = new byte[dst.length + src.length];
	    buf = slap(dst,
		       buf,
		       dst.length,
		       0);
	    buf = slap(src,
		       buf,
		       src.length,
		       dst.length);
	    return buf;
	}
    }

  public static byte[] bitwiseAnd(byte address[], byte mask[]) {
    byte tgt[] = new byte[4];
    
    tgt[0] = (byte) (((short) address[0]) & ((short) mask[0]));
    tgt[1] = (byte) (((short) address[1]) & ((short) mask[1]));
    tgt[2] = (byte) (((short) address[2]) & ((short) mask[2]));
    tgt[3] = (byte) (((short) address[3]) & ((short) mask[3]));
    
    return tgt;
  }
 
}
