// The following implements the packet formats for BLAST.
// The following packets are needed:
// BLAST_req:
//		reqnum: int32
// BLAST_data:
// 		as in PD3e on page 412
//		ProtNum:	int32
//		MID:		int32
//		Length:		int32
//		NumFrags:	int16
//		Type:		int16
//		FragMask:	int32
//		Data:		byte[]
// In our implementation, the maximum size of data will be 1K.
//
// BLAST_SSR
//	Same as BLAST_data, but with zero bytes of data.

// The "raw" packet format, as sent and received via DatagramSocket,
// is byte[].  As appropriate, a read(byte[]) method or a
// byte[] write() method is provided for the conversion from/to byte[].
// Appropriate constructors might be used instead of read(), but handling
// errors is trickier then.

import java.io.*;

public class blastpkt {

    public static final short SERVERPORT = 2222;
    public static final int   REQSIZE = 4;
    public static final int   BHEADSIZE = 20;
    public static final int   MAXDATA = 1024;
    public static final int   MAXSIZE = BHEADSIZE + MAXDATA;

    public static final short TYPE_DATA = 0;
    public static final short TYPE_SSR  = 1;


    public static final int   DONE = 5000;   // milliseconds
    public static final int   RETRY = 1000;
    public static final int   LAST_FRAG = 2000;  // milliseconds

    // given a mask with a single bit set, fragpos returns its position 0..31.
    // eg if mask= ..001000, then fragpos(mask) returns 3.
    // it returns -1 if more than one bit is set, or if no bit is set.
    public static int fragpos (int mask) {
		int i=0;
		if (mask == 0) return -1;
		while ((mask & 1) == 0) {
			i++;
			mask = (mask >> 1);
		}
		if ((mask & ~1) != 0) {
			return -1;
		}
		return i;

	}

	// maskmaker(n) returns a mask consisting of n 1's in positions 0..n-1
	// eg maskmaker(4) returns ...00001111

	public static int maskmaker(int numfrags) {
		int mask=0;
		while (numfrags > 0) {
			mask = (mask << 1) | 1;
			numfrags--;
		}
		return mask;
	}


//**********************************************************************************

public class request {

    private int  _reqnum;

    //---------------------------------

    public request(int reqnum) {
        _reqnum = reqnum;
    }

    public void read(byte[] buf) {            // not complete but not needed
        //super(BUMPPROTO, REQop);
    }

    public byte[] write() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(size());
        DataOutputStream dos = new DataOutputStream(baos);
        try {
            //writeExternal(dos);
            dos.writeInt(_reqnum);
            return baos.toByteArray();
            } catch (IOException ioe) {
                System.err.println("BASE packet output conversion failed");
                return null;
            }
        //return null;
    }

    public int size() {
        return REQSIZE;
    }
}

//*******************

public class data {

	//public final int HEADERSIZE = 20;

    private int   _ProtNum;
    private int   _MID;
    private int   _Length;
    private short _NumFrags;
    private short _Type;
    private int   _FragMask;
    private byte[] _Data;

    //---------------------------------

    public data () {		// use read() later to fill in fields

    }

    public int ProtNum() {return _ProtNum;}
    public int MID()     {return _MID;}
    public int Length()  {return _Length;}
    public short NumFrags(){return _NumFrags;}
    public short Type()    {return _Type;}
    public int FragMask(){return _FragMask;}
    public byte[] Data() {return _Data;}

    public byte[] write() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(size());
        DataOutputStream dos = new DataOutputStream(baos);
        try {
            //writeExternal(dos);
            dos.writeInt(_ProtNum);
            dos.writeInt(_MID);
            dos.writeInt(_Length);
            dos.writeShort(_NumFrags);          // padding
            dos.writeShort(_Type);          // padding
            dos.writeInt(_FragMask);
            for (int i=0; i<_Data.length; i++)
            	dos.writeByte(_Data[i]);
            return baos.toByteArray();
        } catch (IOException ioe) {
                System.err.println("ACK packet output conversion failed");
                return null;
        }
    }

	// reads data from specified byte[] buf, size many bytes
    public void read (byte[] buf, int size) {
        ByteArrayInputStream bais = new ByteArrayInputStream(buf, 0, size);
        DataInputStream dis = new DataInputStream(bais);
        try {
            _ProtNum = dis.readInt();
            _MID =     dis.readInt();
            _Length=   dis.readInt();
            _NumFrags= dis.readShort();
            _Type =    dis.readShort();
            _FragMask= dis.readInt();
            _Data  = new byte[size-BHEADSIZE];
            dis.read(_Data);
        } catch (IOException ioe) {
            System.err.println("BLAST_data conversion failed");
            return;
        }
    }

    public void read(byte[] buf) {
		read(buf, buf.length);
	}

    public int size() {
        return BHEADSIZE + _Data.length;
    }

}

//*******************

public class ssr  {

	//public final int HEADERSIZE = 20;

    private int   _ProtNum;
    private int   _MID;
    private int   _Length;
    private short _NumFrags;
    private short _Type;
    private int   _FragMask;

    //---------------------------------

    public ssr (int protnum, int mid, int length, short numfrags, short type, int fragmask) {
		_ProtNum  = protnum;
		_MID      = mid;
		_Length   = length;
		_NumFrags = numfrags;
		_Type     = type;
		_FragMask = fragmask;
    }

    public int ProtNum() {return _ProtNum;}
    public int MID()     {return _MID;}
    public int Length()  {return _Length;}
    public short NumFrags(){return _NumFrags;}
    public short Type()    {return _Type;}
    public int FragMask(){return _FragMask;}

    public byte[] write() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(size());
        DataOutputStream dos = new DataOutputStream(baos);
        try {
            //writeExternal(dos);
            dos.writeInt(_ProtNum);
            dos.writeInt(_MID);
            dos.writeInt(_Length);
            dos.writeShort(_NumFrags);          // padding
            dos.writeShort(_Type);          // padding
            dos.writeInt(_FragMask);
            return baos.toByteArray();
            } catch (IOException ioe) {
                System.err.println("ACK packet output conversion failed");
                return null;
            }
     }

    public int size() {
        return BHEADSIZE;
    }

}



}