public static byte longToUbyte(long longvalue) {
	if (longvalue > MAX_UBYTE) {
		throw new ByteOverflowException(getExceptionText(MAX_UBYTE, longvalue));
	} else if (longvalue < 0) {
		throw new SignedNumberException(getExceptionText(MAX_UBYTE, longvalue));
	}
	return (byte) (longvalue & 0xFF);
}
 public static short longToUshort(long longvalue) {
	if (longvalue > MAX_USHORT) {
		throw new ByteOverflowException(getExceptionText(MAX_USHORT, longvalue));
	} else if (longvalue < 0) {
		throw new SignedNumberException(getExceptionText(MAX_USHORT, longvalue));
	}
	return (short) (longvalue & 0xFFFF);
}
 public static String toBinaryString(int value) {
	return toBinaryString(value, 32);
}
 public enum UtpSocketState {
	
		/**
		 * Indicates that a syn packet has been send.
		 */
		SYN_SENT,
		
		/**
		 * Indicates that a connection has been established.
		 */
		CONNECTED,
		
		/**
		 * Indicates that no connection is established.
		 */
		CLOSED,
		
		/**
		 * Indicates that a SYN has been recieved but could not be acked.
		 */
		SYN_ACKING_FAILED, 
		
		/**
		 * Indicates that the fin Packet has been send.
		 */
		FIN_SEND,
		
		/**
		 * Indicates that a fin was recieved. 
		 */
		GOT_FIN,
		
}
 public class UtpSocketChannelImpl extends UtpSocketChannel implements
		UtpPacketRecievable {

	private volatile BlockingQueue<UtpTimestampedPacketDTO> queue = new LinkedBlockingQueue<UtpTimestampedPacketDTO>();

	private UtpRecieveRunnable reciever;
	private UtpWritingRunnable writer;
	private UtpReadingRunnable reader;
	private Object sendLock = new Object();

	private UtpServerSocketChannelImpl server;
	private ScheduledExecutorService retryConnectionTimeScheduler;
	private int connectionAttempts = 0;

	private int eofPacket;


	private static final Logger log = LoggerFactory
			.getLogger(UtpSocketChannelImpl.class);
	
	/*
	 * Handles packet.
	 */
	@Override
	public void recievePacket(DatagramPacket udpPacket) {
		
		
		if (isSynAckPacket(udpPacket)) {
			handleSynAckPacket(udpPacket);
		} else if (isResetPacket(udpPacket)) {
			handleResetPacket(udpPacket);
		} else if (isSynPkt(udpPacket)) {
			handleIncommingConnectionRequest(udpPacket);
		} else if (isDataPacket(udpPacket)) {
			handlePacket(udpPacket);
		} else if (isStatePacket(udpPacket)) {
			handlePacket(udpPacket);
		} else if (isFinPacket(udpPacket)) {
			handleFinPacket(udpPacket);
		} else {
			sendResetPacket(udpPacket.getSocketAddress());
		}
			
	}

	private void handleFinPacket(DatagramPacket udpPacket) {
		stateLock.lock();
		try {
			setState(UtpSocketState.GOT_FIN);
			UtpPacket finPacket = extractUtpPacket(udpPacket);
			long freeBuffer = 0;
			if (reader != null && reader.isRunning()) {
				freeBuffer = reader.getLeftSpaceInBuffer();
			} else {
				freeBuffer = UtpAlgConfiguration.MAX_PACKET_SIZE;
			}
			this.eofPacket = finPacket.getSequenceNumber() & 0xFFFF;
			ackPacket(finPacket, timeStamper.utpDifference(finPacket.getTimestamp()), freeBuffer);
		} catch (IOException exp) {
			exp.printStackTrace();
			// TODO: what to do if exception?
		} finally {
			stateLock.unlock();
		}
	}

	private void handleResetPacket(DatagramPacket udpPacket) {
		this.close();
	}

	private boolean isSynAckPacket(DatagramPacket udpPacket) {
		return isStatePacket(udpPacket) && getState() == UtpSocketState.SYN_SENT;
	}

	private void handleSynAckPacket(DatagramPacket udpPacket) {
		UtpPacket pkt = extractUtpPacket(udpPacket);
		if ((pkt.getConnectionId() & 0xFFFF) == getConnectionIdRecieving()) {
			stateLock.lock();
			setAckNrFromPacketSqNr(pkt);
			setState(CONNECTED);
			printState("[SynAck recieved] ");
			disableConnectionTimeOutCounter();
			connectFuture.finished(null);
			stateLock.unlock();			
		} else {
			sendResetPacket(udpPacket.getSocketAddress());
		}
	}
	
	private boolean isSameAddressAndId(long id, SocketAddress addr) {
		return (id & 0xFFFFFFFF) == getConnectionIdRecieving() 
				&& addr.equals(getRemoteAdress());
	}
	
	private void disableConnectionTimeOutCounter() {
		if (retryConnectionTimeScheduler != null) {
			retryConnectionTimeScheduler.shutdown();
			retryConnectionTimeScheduler = null;
		}
		connectionAttempts = 0;
	}

	public int getConnectionAttempts() {
		return connectionAttempts;
	}

	public void incrementConnectionAttempts() {
		connectionAttempts++;
	}

	private void handlePacket(DatagramPacket udpPacket) {
		UtpPacket utpPacket = extractUtpPacket(udpPacket);
		queue.offer(new UtpTimestampedPacketDTO(udpPacket, utpPacket,
				timeStamper.timeStamp(), timeStamper.utpTimeStamp()));

	}
	/**
	 * Sends an ack. 
	 * @param utpPacket the packet that should be acked
	 * @param timestampDifference timestamp difference for tha ack packet
	 * @param windowSize the remaining buffer size. 
	 * @throws IOException
	 */
	public void ackPacket(UtpPacket utpPacket, int timestampDifference,
			long windowSize) throws IOException {
		UtpPacket ackPacket = createAckPacket(utpPacket, timestampDifference,
				windowSize);
		sendPacket(ackPacket);
	}
	
	/**
	 * setting up a random sequence number. 
	 */
	public void setupRandomSeqNumber() {
		Random rnd = new Random();
		int max = (int) (MAX_USHORT - 1);
		int rndInt = rnd.nextInt(max);
		setSequenceNumber(rndInt);

	}

	private void handleIncommingConnectionRequest(DatagramPacket udpPacket) {
		UtpPacket utpPacket = extractUtpPacket(udpPacket);

		if (acceptSyn(udpPacket)) {
			int timeStamp = timeStamper.utpTimeStamp();
			setRemoteAddress(udpPacket.getSocketAddress());
			setConnectionIdsFromPacket(utpPacket);
			setupRandomSeqNumber();
			setAckNrFromPacketSqNr(utpPacket);
			printState("[Syn recieved] ");
			int timestampDifference = timeStamper.utpDifference(timeStamp,
					utpPacket.getTimestamp());
			UtpPacket ackPacket = createAckPacket(utpPacket,
					timestampDifference,
					UtpAlgConfiguration.MAX_PACKET_SIZE * 1000);
			try {
				log.debug("sending syn ack");
				sendPacket(ackPacket);
				setState(CONNECTED);
			} catch (IOException exp) {
				// TODO: In future?
				setRemoteAddress(null);
				setConnectionIdsending((short) 0);
				setConnectionIdRecieving((short) 0);
				setAckNumber(0);
				setState(SYN_ACKING_FAILED);
				exp.printStackTrace();
			}
		} else {
			sendResetPacket(udpPacket.getSocketAddress());
		}
	}

	private void sendResetPacket(SocketAddress addr) {
		log.debug("Sending RST packet");

	}
	
	private boolean acceptSyn(DatagramPacket udpPacket) {
		UtpPacket pkt = extractUtpPacket(udpPacket);
		return getState() == CLOSED 
				|| (getState() == CONNECTED && isSameAddressAndId(
						pkt.getConnectionId(), udpPacket.getSocketAddress()));
	}

	@Override
	protected void setAckNrFromPacketSqNr(UtpPacket utpPacket) {
		short ackNumberS = utpPacket.getSequenceNumber();
		setAckNumber(ackNumberS & 0xFFFF);
	}

	private void setConnectionIdsFromPacket(UtpPacket utpPacket) {
		short connId = utpPacket.getConnectionId();
		int connIdSender = (connId & 0xFFFF);
		int connIdRec = (connId & 0xFFFF) + 1;
		setConnectionIdsending(connIdSender);
		setConnectionIdRecieving(connIdRec);

	}

	@Override
	protected void connectImpl(UtpConnectFutureImpl future) {
		reciever = new UtpRecieveRunnable(getDgSocket(), this);
		reciever.start();

	}

	@Override
	protected void abortImpl() {
		if (reciever != null) {
			reciever.graceFullInterrupt();
		} else if (server != null) {
			server.unregister(this);
		}
	}

	@Override
	public UtpWriteFuture write(ByteBuffer src) {
		UtpWriteFutureImpl future = null;
		try {
			future = new UtpWriteFutureImpl();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		writer = new UtpWritingRunnable(this, src, timeStamper, future);
		writer.start();
		return future;
	}

	public BlockingQueue<UtpTimestampedPacketDTO> getDataGramQueue() {
		return queue;
	}
	
	/**
	 * Returns a Data packet with specified header fields already set. 
	 * @return
	 */
	public UtpPacket getNextDataPacket() {
		return createDataPacket();
	}

	/**
	 * Returns predefined fin packet
	 * @return fin packet.
	 */
	public UtpPacket getFinPacket() {
		UtpPacket fin = createDataPacket();
		fin.setTypeVersion(FIN);
		return fin;
	}

	@Override
	public UtpReadFutureImpl read(ByteBuffer dst) {
		UtpReadFutureImpl readFuture = null;
		try {
			readFuture = new UtpReadFutureImpl();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		reader = new UtpReadingRunnable(this, dst, timeStamper, readFuture);
		reader.start();
		return readFuture;
	}
	
	/**
	 * Creates an Selective Ack packet
	 * @param headerExtension the header extension where the SACK data is stored
	 * @param timestampDifference timestamp difference for the ack pcket.
	 * @param advertisedWindow remaining buffer size.
	 * @throws IOException
	 */
	public void selectiveAckPacket(SelectiveAckHeaderExtension headerExtension,
			int timestampDifference, long advertisedWindow) throws IOException {
		UtpPacket sack = createSelectiveAckPacket(headerExtension,
				timestampDifference, advertisedWindow);
		sendPacket(sack);

	}

	private UtpPacket createSelectiveAckPacket(SelectiveAckHeaderExtension headerExtension,
			int timestampDifference, long advertisedWindow) {
		UtpPacket ackPacket = new UtpPacket();
		ackPacket.setAckNumber(longToUshort(getAckNumber()));
		ackPacket.setTimestampDifference(timestampDifference);
		ackPacket.setTimestamp(timeStamper.utpTimeStamp());
		ackPacket.setConnectionId(longToUshort(getConnectionIdsending()));
		ackPacket.setWindowSize(longToUint(advertisedWindow));

		ackPacket.setFirstExtension(SELECTIVE_ACK);
		UtpHeaderExtension[] extensions = { headerExtension };
		ackPacket.setExtensions(extensions);
		ackPacket.setTypeVersion(STATE);

		return ackPacket;
	}

	@Override
	public UtpSocketState getState() {
		return state;
	}

	@Override
	public void setState(UtpSocketState state) {
		this.state = state;
	}

	@Override
	public UtpCloseFuture close() {
		abortImpl();
		if (isReading()) {
			reader.graceFullInterrupt();
		}
		if (isWriting()) {
			writer.graceFullInterrupt();
		}
		return null;
	}

	@Override
	public boolean isReading() {
		return (reader != null ? reader.isRunning() : false);
	}

	@Override
	public boolean isWriting() {
		return (writer != null ? writer.isRunning() : false);
	}

	public void ackAlreadyAcked(SelectiveAckHeaderExtension extension, int timestampDifference,
			long windowSize) throws IOException {
		UtpPacket ackPacket = new UtpPacket();
		ackPacket.setAckNumber(longToUshort(getAckNumber()));
		SelectiveAckHeaderExtension[] extensions = { extension };
		ackPacket.setExtensions(extensions);
		ackPacket.setTimestampDifference(timestampDifference);
		ackPacket.setTimestamp(timeStamper.utpTimeStamp());
		ackPacket.setConnectionId(longToUshort(getConnectionIdsending()));
		ackPacket.setTypeVersion(STATE);
		ackPacket.setWindowSize(longToUint(windowSize));
		sendPacket(ackPacket);

	}

	@Override
	public void sendPacket(DatagramPacket pkt) throws IOException {
		synchronized (sendLock) {
			getDgSocket().send(pkt);
		}

	}

	/* general method to send a packet, will be wrapped by a UDP Packet */
	@Override
	public void sendPacket(UtpPacket packet) throws IOException {
		if (packet != null) {
			byte[] utpPacketBytes = packet.toByteArray();
			int length = packet.getPacketLength();
			DatagramPacket pkt = new DatagramPacket(utpPacketBytes, length,
					getRemoteAdress());
			sendPacket(pkt);
		}
	}

	@Override
	public void setDgSocket(DatagramSocket dgSocket) {
		if (this.dgSocket != null) {
			this.dgSocket.close();
		}
		this.dgSocket = dgSocket;
	}

	@Override
	public void setAckNumber(int ackNumber) {
//		log.debug("ack nubmer set to: " + ackNumber);
		this.ackNumber = ackNumber;
	}

	public void setServer(UtpServerSocketChannelImpl utpServerSocketChannelImpl) {
		this.server = utpServerSocketChannelImpl;

	}

	@Override
	public void setRemoteAddress(SocketAddress remoteAdress) {
		this.remoteAddress = remoteAdress;
	}

	public void returnFromReading() {
		reader = null;
		//TODO: dispatch:
		if (!isWriting()) {
			this.state = UtpSocketState.CONNECTED;
		} else {
			
		}
	}

	public void removeWriter() {
		writer = null;
	}
	
	/*
	 * Start a connection time out counter which will frequently resend the syn packet.
	 */
	@Override
	protected void startConnectionTimeOutCounter(UtpPacket synPacket) {
		retryConnectionTimeScheduler = Executors
				.newSingleThreadScheduledExecutor();
		ConnectionTimeOutRunnable runnable = new ConnectionTimeOutRunnable(
				synPacket, this, stateLock);
		log.debug("starting scheduler");
		// retryConnectionTimeScheduler.schedule(runnable, 2, TimeUnit.SECONDS);
		retryConnectionTimeScheduler.scheduleWithFixedDelay(runnable,
				UtpAlgConfiguration.CONNECTION_ATTEMPT_INTERVALL_MILLIS,
				UtpAlgConfiguration.CONNECTION_ATTEMPT_INTERVALL_MILLIS,
				TimeUnit.MILLISECONDS);
	}
	
	/**
	 * Called by the time out counter {@see ConnectionTimeOutRunnable}
	 * @param exception, is optional.
	 */
	public void connectionFailed(IOException exp) {
		log.debug("got failing message");
		setSequenceNumber(DEF_SEQ_START);
		setRemoteAddress(null);
		abortImpl();
		setState(CLOSED);
		retryConnectionTimeScheduler.shutdown();
		retryConnectionTimeScheduler = null;
		connectFuture.finished(exp);

	}

	/**
	 * Resends syn packet. called by {@see ConnectionTimeOutRunnable}
	 * @param synPacket 
	 */
	public void resendSynPacket(UtpPacket synPacket) {
		stateLock.lock();
		try {
			int attempts = getConnectionAttempts();
			log.debug("attempt: " + attempts);
			if (getState() == UtpSocketState.SYN_SENT) {
				try {
					if (attempts < UtpAlgConfiguration.MAX_CONNECTION_ATTEMPTS) {
						incrementConnectionAttempts();
						log.debug("REATTEMPTING CONNECTION");
						sendPacket(synPacket);
					} else {
						connectionFailed(new SocketTimeoutException());
					}
				} catch (IOException e) {
					if (attempts >= UtpAlgConfiguration.MAX_CONNECTION_ATTEMPTS) {
						connectionFailed(e);
					} // else ignore, try in next attempt
				}
			}
		} finally {
			stateLock.unlock();
		}

	}

	public void setTimetamper(MicroSecondsTimeStamp stamp) {
		this.timeStamper = stamp;

	}
	/*
	 * Creates an ACK packet. 
	 */
	protected UtpPacket createAckPacket(UtpPacket pkt, int timedifference,
			long advertisedWindow) {
		UtpPacket ackPacket = new UtpPacket();
		if (pkt.getTypeVersion() != FIN) {
			setAckNrFromPacketSqNr(pkt);			
		}
		ackPacket.setAckNumber(longToUshort(getAckNumber()));

		ackPacket.setTimestampDifference(timedifference);
		ackPacket.setTimestamp(timeStamper.utpTimeStamp());
		ackPacket.setConnectionId(longToUshort(getConnectionIdsending()));
		ackPacket.setTypeVersion(STATE);
		ackPacket.setWindowSize(longToUint(advertisedWindow));
		return ackPacket;
	}
	
	protected UtpPacket createDataPacket() {
		UtpPacket pkt = new UtpPacket();
		pkt.setSequenceNumber(longToUshort(getSequenceNumber()));
		incrementSequenceNumber();
		pkt.setAckNumber(longToUshort(getAckNumber()));
		pkt.setConnectionId(longToUshort(getConnectionIdsending()));
		pkt.setTimestamp(timeStamper.utpTimeStamp());
		pkt.setTypeVersion(UtpPacketUtils.DATA);
		return pkt;
	}

}
 public class UtpAlgConfiguration {

	public static final int MAX_CONNECTION_ATTEMPTS = 5;
	public static final int CONNECTION_ATTEMPT_INTERVALL_MILLIS = 5000;
	
	public static long MINIMUM_DELTA_TO_MAX_WINDOW_MICROS = 1000000;
	// ack every second packets
	public static int SKIP_PACKETS_UNTIL_ACK = 2;
	

	/**
	 * TWEAKING SECTION
	 */

	/**
	 * Auto ack every packet that is smaller than ACK_NR from ack packet. 
	 * Some Implementations like libutp do this. 
	 */
	public static boolean AUTO_ACK_SMALLER_THAN_ACK_NUMBER = true;
	/**
	 * if oldest mindelay sample is older than that, update it. 
	 */
	public static long MINIMUM_DIFFERENCE_TIMESTAMP_MICROSEC = 120000000L;

	/**
	 * timeout 
	 */
	public static int MINIMUM_TIMEOUT_MILLIS = 500;

	/**
	 * Packet size modus
	 */
	public static PacketSizeModus PACKET_SIZE_MODE = PacketSizeModus.CONSTANT_1472;

	/**
	 * maximum packet size should be dynamically set once path mtu discovery
	 * implemented.
	 */

	public volatile static int MAX_PACKET_SIZE = 1472;

	/**
	 * minimum packet size.
	 */
	public volatile static int MIN_PACKET_SIZE = 150;

	/**
	 * Minimum path MTU
	 */
	public volatile static int MINIMUM_MTU = 576;

	/**
	 * Maximal window increase per RTT - increase to allow uTP throttle up
	 * faster.
	 */
	public volatile static int MAX_CWND_INCREASE_PACKETS_PER_RTT = 3000;

	/**
	 * maximal buffering delay
	 */
	public volatile static int C_CONTROL_TARGET_MICROS = 100000;

	/**
	 * activate burst sending
	 */
	public volatile static boolean SEND_IN_BURST = true;

	/**
	 * Reduce burst sending artificially
	 */
	public volatile static int MAX_BURST_SEND = 5;

	/**
	 * Minimum number of acks past seqNr=x to trigger a resend of seqNr=x;
	 */
	public volatile static int MIN_SKIP_PACKET_BEFORE_RESEND = 3;

	public volatile static long MICROSECOND_WAIT_BETWEEN_BURSTS = 28000;

	public volatile static long TIME_WAIT_AFTER_LAST_PACKET = 3000000;

	public volatile static boolean ONLY_POSITIVE_GAIN = false;
	
	public volatile static boolean DEBUG = false;
	

	/**
	 * @return information about the algorithm. This is only used for debugging 
	 */
	public static String getString() {
		String toReturn = "";
		toReturn += "MINIMUM_TIMEOUT_MILLIS: " + MINIMUM_TIMEOUT_MILLIS + " ";
		toReturn += "PACKET_SIZE_MODE: " + PACKET_SIZE_MODE + " ";
		toReturn += "MAX_PACKET_SIZE: " + MAX_PACKET_SIZE + " ";
		toReturn += "MIN_PACKET_SIZE: " + MIN_PACKET_SIZE + " ";
		toReturn += "MINIMUM_MTU: " + MINIMUM_MTU + " ";
		toReturn += "MAX_CWND_INCREASE_PACKETS_PER_RTT: " + MAX_CWND_INCREASE_PACKETS_PER_RTT + " ";
		toReturn += "C_CONTROL_TARGET_MICROS: " + C_CONTROL_TARGET_MICROS + " ";
		toReturn += "SEND_IN_BURST: " + SEND_IN_BURST + " ";
		toReturn += "MAX_BURST_SEND: " + MAX_BURST_SEND + " ";
		toReturn += "MIN_SKIP_PACKET_BEFORE_RESEND: " + MIN_SKIP_PACKET_BEFORE_RESEND + " ";
		toReturn += "MICROSECOND_WAIT_BETWEEN_BURSTS: " + MICROSECOND_WAIT_BETWEEN_BURSTS + " ";
		toReturn += "TIME_WAIT_AFTER_FIN_MICROS: " + TIME_WAIT_AFTER_LAST_PACKET + " ";
		toReturn += "ONLY_POSITIVE_GAIN: " + ONLY_POSITIVE_GAIN + " ";
		toReturn += "DEBUG: " + DEBUG + " ";
		return toReturn;
	}

	
} public class MicroSecondsTimeStamp {

	private static long initDateMillis = System.currentTimeMillis();
	private static long startNs = System.nanoTime();
	
	/**
	 * Returns a uTP time stamp for packet headers. 
	 * @return timestamp
	 */
	public int utpTimeStamp() {
		int returnStamp;
		//TODO: if performance issues, try bitwise & operator since constant MAX_UINT equals 0xFFFFFF
		// (see http://en.wikipedia.org/wiki/Modulo_operation#Performance_issues )
		long stamp = timeStamp() % UnsignedTypesUtil.MAX_UINT;
		try {
			returnStamp  = longToUint(stamp);
		} catch (ByteOverflowException exp) {
			stamp = stamp % MAX_UINT;
			returnStamp = longToUint(stamp);
		}
		return returnStamp;
	}
	
	/**
	 * Calculates the Difference of the uTP timestamp between now and parameter. 
	 * @param othertimestamp timestamp
	 * @return difference 
	 */
	public int utpDifference(int othertimestamp) {
		return utpDifference(utpTimeStamp(), othertimestamp);
	}
	/**
	 * calculates the utp Difference of timestamps (this - other)
	 * @param thisTimeStamp
	 * @param othertimestamp
	 * @return difference. 
	 */
	public int utpDifference(int thisTimeStamp, int othertimestamp) {
		int nowTime = thisTimeStamp;
		long nowTimeL = nowTime & 0xFFFFFFFF;
		long otherTimeL = othertimestamp & 0xFFFFFFFF;
		long differenceL = nowTimeL - otherTimeL;
		// TODO: POSSIBLE BUG NEGATIVE DIFFERENCE
		if (differenceL < 0) {
			differenceL += MAX_UINT;
		}
		return longToUint(differenceL);
	}
	

	/**
	 * @return timestamp with micro second resulution
	 */
	public long timeStamp() {
		
		long currentNs = System.nanoTime();
		long deltaMs = (currentNs - startNs)/1000;
		return (initDateMillis*1000 + deltaMs);
	}
	
	
	public long getBegin() {
		return (initDateMillis * 1000);
	}
	
	public long getStartNs() {
		return startNs;
	}
	
	public long getInitDateMs() {
		return initDateMillis;
	}

}
 public abstract class UtpHeaderExtension {
	
	public static UtpHeaderExtension resolve(byte b) {
		if (b == longToUbyte(1)) {
			return new SelectiveAckHeaderExtension();
		} else {
			return null;
		}
	}

	public abstract byte getNextExtension();
	public abstract void setNextExtension(byte nextExtension) ;
	
	public abstract byte getLength();
	
	public abstract byte[] getBitMask();
	public abstract void setBitMask(byte[] bitMask);
	
	public abstract byte[] toByteArray();

}
 public class UtpPacket {
	
	private byte typeVersion;
	private byte firstExtension;
	private short connectionId;
	private int timestamp;
	private int timestampDifference;
	private int windowSize;
	private short sequenceNumber;
	private short ackNumber;
	private UtpHeaderExtension[] extensions;
	private byte[] payload;
	
	
	public byte[] getPayload() {
		return payload;
	}
	
	public void setPayload(byte[] payload) {
		this.payload = payload;
	}
	
	public void setExtensions(UtpHeaderExtension[] extensions) {
		this.extensions = extensions;
	}
	
	public UtpHeaderExtension[] getExtensions() {
		return this.extensions;
	}
	
	public byte getFirstExtension() {
		return firstExtension;
	}
	
	public void setFirstExtension(byte firstExtension) {
		this.firstExtension = firstExtension;
	}
	
	public byte getTypeVersion() {
		return typeVersion;
	}

	public void setTypeVersion(byte typeVersion) {
		this.typeVersion = typeVersion;
	}

	public short getConnectionId() {
		return connectionId;
	}

	public void setConnectionId(short connectionId) {
		this.connectionId = connectionId;
	}

	public int getTimestamp() {
		return timestamp;
	}

	public void setTimestamp(int timestamp) {
		this.timestamp = timestamp;
	}

	public int getTimestampDifference() {
		return timestampDifference;
	}

	public void setTimestampDifference(int timestampDifference) {
		this.timestampDifference = timestampDifference;
	}

	public int getWindowSize() {
		return windowSize;
	}

	public void setWindowSize(int windowSize) {
		this.windowSize = windowSize;
	}

	public short getSequenceNumber() {
		return sequenceNumber;
	}

	public void setSequenceNumber(short sequenceNumber) {
		this.sequenceNumber = sequenceNumber;
	}

	public short getAckNumber() {
		return ackNumber;
	}

	public void setAckNumber(short ackNumber) {
		this.ackNumber = ackNumber;
	}

	/**
	 * Returns a byte array version of this packet.
	 */
	public byte[] toByteArray() {
		
		if (!hasExtensions()) {
			return joinByteArray(getExtensionlessByteArray(), getPayload());
		}
		

		int offset = DEF_HEADER_LENGTH;
		int headerLength = offset + getTotalLengthOfExtensions();
		byte[] header = new byte[headerLength];
		byte[] extensionlessArray = getExtensionlessByteArray();
		
		for (int i = 0; i< extensionlessArray.length; i++) {
			header[i] = extensionlessArray[i];
		}
		
		for (int i = 0; i < extensions.length; i++) {
			byte[] extenionBytes = extensions[i].toByteArray();
			for (int j = 0; j < extenionBytes.length; j++) {
				header[offset++] = extenionBytes[j];
			}
		}
		return joinByteArray(header, getPayload());
		
	}

	private byte[] getExtensionlessByteArray() {
		byte[] array = {typeVersion, firstExtension, (byte) (connectionId >> 8), (byte) (connectionId),
		(byte) (timestamp >> 24), (byte) (timestamp >> 16), (byte) (timestamp >> 8), (byte) (timestamp),
		(byte) (timestampDifference >> 24), (byte) (timestampDifference >> 16), (byte) (timestampDifference >> 8), (byte) (timestampDifference),
		(byte) (windowSize >> 24), (byte) (windowSize >> 16), (byte) (windowSize >> 8), (byte) (windowSize),
		(byte) (sequenceNumber >> 8), (byte) (sequenceNumber), (byte) (ackNumber >> 8), (byte) (ackNumber), };
		return array;
	}

	private boolean hasExtensions() {
		return !((extensions == null || extensions.length == 0) && firstExtension == 0);
	}

	private int getTotalLengthOfExtensions() {
		if (!hasExtensions()) {
			return 0;
		}
		int length = 0;
		for (int i = 0; i < extensions.length; i++) {
			length += 2 + extensions[i].getBitMask().length;
		}
		return length;
	}
	
	/**
	 * returns the total packet length.
	 * @return bytes.
	 */
	public int getPacketLength() {
		byte[] pl = getPayload();
		int plLength = pl != null ? pl.length : 0;
		return DEF_HEADER_LENGTH + getTotalLengthOfExtensions() + plLength;
	}
	
	/**
	 * Sets the packet data from the array
	 * @param array the array
	 * @param length total packet length
	 * @param offset the packet starts here
	 */
	public void setFromByteArray(byte[] array, int length, int offset) {
		if (array == null) {
			return;
		}
		
		typeVersion = array[0];
		firstExtension = array[1];
		connectionId = UnsignedTypesUtil.bytesToUshort(array[2], array[3]);
		timestamp = UnsignedTypesUtil.bytesToUint(array[4], array[5], array[6], array[7]);
		timestampDifference = UnsignedTypesUtil.bytesToUint(array[8], array[9], array[10], array[11]);
		windowSize = UnsignedTypesUtil.bytesToUint(array[12], array[13], array[14], array[15]);
		sequenceNumber = UnsignedTypesUtil.bytesToUshort(array[16], array[17]);
		ackNumber = UnsignedTypesUtil.bytesToUshort(array[18], array[19]);
		
		int utpOffset = offset + UtpPacketUtils.DEF_HEADER_LENGTH;
		if (firstExtension != 0) {
			utpOffset += loadExtensions(array);
		}
		
		payload = new byte[length - utpOffset];
		System.arraycopy(array, utpOffset, payload, 0, length - utpOffset);
		
	}
	
	
	private int loadExtensions(byte[] array) {
		byte extensionType = array[1];
		int extensionStartIndex = 20;
		int totalLength = 0;
		
		ArrayList<UtpHeaderExtension> list = new ArrayList<UtpHeaderExtension>();
		UtpHeaderExtension extension = UtpHeaderExtension.resolve(extensionType);
	
		while (extension != null) {
			int extensionLength = array[extensionStartIndex+1] & 0xFF;
			byte[] bitmask = new byte[extensionLength];
			System.arraycopy(array, extensionStartIndex+2, bitmask, 0, extensionLength);
			extension.setNextExtension(array[extensionStartIndex]);
			extension.setBitMask(bitmask);
			list.add(extension);
			totalLength = extensionLength+2;
			int nextPossibleExtensionIndex = extensionLength+2+extensionStartIndex;
			// packet is enough big
			if (array.length > nextPossibleExtensionIndex) {
				extension = UtpHeaderExtension.resolve(array[nextPossibleExtensionIndex]);
				extensionStartIndex = nextPossibleExtensionIndex;				
			} else { // packet end reached. 
				extension = null;
			}
		}

		UtpHeaderExtension[] extensions = list.toArray(new UtpHeaderExtension[list.size()]);
		setExtensions(extensions);
		return totalLength;
	}



	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		} 
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof UtpPacket)) {
			return false;
		} else {
			UtpPacket pkt = (UtpPacket) obj;
			
			byte[] their = pkt.toByteArray();
			byte[] mine = this.toByteArray();
			
			if (their.length != mine.length) {
				return false;
			}
			for (int i = 0; i < mine.length; i++) {
				if (mine[i] != their[i]) {
					return false;
				}
			}
			
			return true;
		}
		
	}
	@Override
	public String toString() {
		String ret = "[Type: " + (typeVersion & 0xFF) + "] " + 
					"[FirstExt: " + (firstExtension & 0xFF) + "] " +
					"[ConnId: "	+ (connectionId & 0xFFFF) + "] " + 
					"[Wnd: " + (windowSize & 0xFFFFFFFF) + " " + 
					"[Seq: " + (sequenceNumber & 0xFFFF) + "] " + 
					"[Ack: " +	(ackNumber & 0xFFFF) + "] ";
		
		if (extensions != null) {
			for (int i = 0; i< extensions.length; i++) {
				ret += "[Ext_" + i + ": " + (extensions[i].getNextExtension() & 0xFF) + " " + extensions[i].getLength() + "] ";
			}
		}
		return ret;
		

	}
	
	
    @Override
    public int hashCode() {
      int code = 0;
      //TB: multiply with prime and xor
      //TODO: check if hashCode is needed
		code = typeVersion + 10*firstExtension + 100* connectionId 
				+ 1000*timestamp + 10000*timestampDifference + 100000*windowSize
				+ 1000000*sequenceNumber + 10000000*ackNumber + 1000000000*toByteArray().length;
      return code;
    }
	
}
 public class UtpPacketUtils {
	
	public static final byte VERSION = longToUbyte(1);

	public static final byte DATA = (byte) (VERSION | longToUbyte(0));
	public static final byte FIN = (byte) (VERSION | longToUbyte(16));
	public static final byte STATE = (byte) (VERSION | longToUbyte(32));
	public static final byte RESET = (byte) (VERSION | longToUbyte(48));
	public static final byte SYN = (byte) (VERSION | longToUbyte(64));

	
	public static final byte NO_EXTENSION = longToUbyte(0);
	public static final byte SELECTIVE_ACK = longToUbyte(1);
	
	public static final int MAX_UTP_PACKET_LENGTH = 1500;
	public static final int MAX_UDP_HEADER_LENGTH = 48;
	public static final int DEF_HEADER_LENGTH = 20;
	
	private static final Logger log = LoggerFactory.getLogger(UtpPacketUtils.class);
	
	public static byte[] joinByteArray(byte[] array1, byte[] array2) {
		
		int length1 = array1 == null ? 0 : array1.length;
		int length2 = array2 == null ? 0 : array2.length;

		
		int totalLength = length1 + length2;
		byte[] returnArray = new byte[totalLength];
		
		int i = 0;
		for (; i < length1; i++) {
			returnArray[i] = array1[i];
		}
		
		for (int j = 0; j < length2; j++) {
			returnArray[i] = array2[j];
			i++;
		}
		
		return returnArray;
		
	}
	
	/**
	 * Creates an Utp-Packet to initialize a connection
	 * Following values will be set:
	 * <ul>
	 * <li>Type and Version</li>
	 * <li>Sequence Number</li>
	 * </ul>
	 * @return {@link UtpPacket}
	 */
	public static UtpPacket createSynPacket() {
		
		UtpPacket pkt = new UtpPacket();
		pkt.setTypeVersion(SYN);
		pkt.setSequenceNumber(longToUbyte(1));
		byte[] pl = { 1, 2, 3, 4, 5, 6};
		pkt.setPayload(pl);
		return pkt;
	}
	
	public static UtpPacket extractUtpPacket(DatagramPacket dgpkt) {
		
		UtpPacket pkt = new UtpPacket();
		byte[] pktb = dgpkt.getData();
		pkt.setFromByteArray(pktb, dgpkt.getLength(), dgpkt.getOffset());
		return pkt;
	}

	public static boolean isSynPkt(UtpPacket packet) {
		
		if (packet == null) {
			return false;
		}
		
		return packet.getTypeVersion() == SYN;
		
	}
	
	
	private static boolean isPacketType(DatagramPacket packet, byte flag) {
		if (packet == null) {
			return false;
		}
		
		byte[] data = packet.getData();
		
		if (data != null && data.length >= DEF_HEADER_LENGTH) {
			return data[0] == flag;
		}
		
		return false;
	}

	public static boolean isSynPkt(DatagramPacket packet) {
		return isPacketType(packet, SYN);
	}

	public static boolean isResetPacket(DatagramPacket udpPacket) {
		return isPacketType(udpPacket, RESET);
	}

	public static boolean isDataPacket(DatagramPacket udpPacket) {
		return isPacketType(udpPacket, DATA);
	}

	public static boolean isStatePacket(DatagramPacket udpPacket) {
		return isPacketType(udpPacket, STATE);
	}

	public static boolean isFinPacket(DatagramPacket udpPacket) {
		return isPacketType(udpPacket, FIN);
	}

}

import static net.utp4j.data.bytes.UnsignedTypesUtil.longToUbyte;
import static net.utp4j.data.bytes.UnsignedTypesUtil.longToUshort;
import static net.utp4j.data.bytes.BinaryToStringTestHelper.toBinaryString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import net.utp4j.channels.UtpSocketState;
import net.utp4j.channels.impl.UtpSocketChannelImpl;
import net.utp4j.channels.impl.alg.UtpAlgConfiguration;
import net.utp4j.data.MicroSecondsTimeStamp;
import net.utp4j.data.UtpHeaderExtension;
import net.utp4j.data.UtpPacket;
import net.utp4j.data.UtpPacketUtils;

/* Copyright 2013 Ivan Iljkic
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package net.utp4j.channels.impl;






@RunWith(org.mockito.runners.MockitoJUnitRunner.class)
public class UtpChannelReadingTest {

	@Test
	public void test() throws InterruptedException {
		
		UtpAlgConfiguration.AUTO_ACK_SMALLER_THAN_ACK_NUMBER = false;
		UtpAlgConfiguration.SKIP_PACKETS_UNTIL_ACK = 1;
		
		//mocking stuff
		UtpSocketChannelImpl channel = new UtpSocketChannelImpl();
		channel.setState(UtpSocketState.CONNECTED);
		DatagramSocket socket = mock(DatagramSocket.class);
		
		MicroSecondsTimeStamp stamp = mock(MicroSecondsTimeStamp.class);
		when(stamp.utpDifference(anyInt(), anyInt())).thenReturn(500000000);
		when(stamp.timeStamp()).thenReturn(10000000000000L);
		when(stamp.utpTimeStamp()).thenReturn(1251241241);
		
		channel.setTimetamper(stamp);
		channel.setDgSocket(socket);
		channel.setRemoteAddress(new InetSocketAddress("localhost", 12345));
		
		// last recieved packet has seqNr. 2, next one will be packet with seqNr. 3
		channel.setAckNumber(2);

		/*
		 * argument captor on socket, which will record all invocations of socket.send(packet) 
		 * and capture the arguments, in this case the arguments are ack packets. 
		 */
		ArgumentCaptor<DatagramPacket> ackOne = ArgumentCaptor.forClass(DatagramPacket.class);

		try {
			// order of recieving the data packets -> 3,4,6,8,5, 7
			channel.recievePacket(createPacket(3)); // ack 3
			channel.recievePacket(createPacket(4)); // ack 4
			channel.recievePacket(createPacket(6)); // ack 4, Sack 6 -> 00000001
			channel.recievePacket(createPacket(8)); // ack 4, Sack 6, 8 -> 00000101
			channel.recievePacket(createPacket(5)); // ack 6, sack 8 -> 00000001
			channel.recievePacket(createPacket(7)); // ack 8

			// allocating reading buffer and start test.
			ByteBuffer buffer = ByteBuffer.allocate(30000);
			channel.read(buffer);
			
			// wait for read to finish
			Thread.sleep(1000);
			
			// verify 6 ack packets where send and capture them
			verify(socket, times(6)).send(ackOne.capture());
			List<DatagramPacket> allValues = ackOne.getAllValues();
			Iterator<DatagramPacket> iterator = allValues.iterator();
			
			// extract utp packets from the udp packets. 

			UtpPacket three = UtpPacketUtils.extractUtpPacket(iterator.next());
