package org.kne.cloud.network.kltp;

import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;

import org.kne.cloud.network.NetworkPacket;
import org.kne.cloud.network.ipv6.IPv6Packet.IPv6Payload;
import org.kne.io.KNEChannels;

public class KLTPPacket extends IPv6Payload {
	public static final int KLTP_PROTOCOL_NUMBER=253;
	public static final int KLTP_HEADER_LENGTH=24+8;
	
	public static final int KLTP_TYPE_DATA=0;
	public static final int KLTP_TYPE_DATAFIN=1;
	public static final int KLTP_TYPE_ACK=2;
	
	protected ByteBuffer kltpHeader;

	protected ByteBuffer kltpData;
	

	public KLTPPacket() {
		super(KLTP_PROTOCOL_NUMBER);
		kltpHeader=NetworkPacket.bufferAllocator.allocate(KLTP_HEADER_LENGTH);
	}
	
	public KLTPPacket(UUID uuid,int type,long seq,int mtulimit) {
		super(KLTP_PROTOCOL_NUMBER);
		kltpHeader=NetworkPacket.bufferAllocator.allocate(KLTP_HEADER_LENGTH);
		setUUID(uuid);
		setType(type);
		setSequence(seq);
		kltpData=NetworkPacket.bufferAllocator.allocate(mtulimit);
	}
	
	
	
	public UUID getUUID() {
		long h=kltpHeader.getLong(0);
		long l=kltpHeader.getLong(8);
		return new UUID(h,l);
	}
	
	public void setUUID(UUID uuid) {
		kltpHeader.putLong(0, uuid.getMostSignificantBits());
		kltpHeader.putLong(8, uuid.getLeastSignificantBits());
	}
	
	public int getPayloadLength() {
		return kltpHeader.getInt(16);
	}
	
	public void setPayloadLength(int payloadLength) {
		kltpHeader.putInt(16,payloadLength);
	}
	
	public int getChecksum() {
		return kltpHeader.getChar(20);
	}
	
	public void setChecksum(int checksum) {
		kltpHeader.putChar(20, (char) checksum);
	}

	public int getType() {
		return kltpHeader.get(22);
	}
	
	public void setType(int type) {
		kltpHeader.put(22, (byte) type);
	}
	
	public boolean isCE() {
		int v=kltpHeader.get(23)&1;
		return v!=0;
	}
	
	public void setCE(boolean b) {
		kltpHeader.put(23, (byte) (b?1:0));
	}
	
	public long getSequence() {
		return kltpHeader.getLong(24);
	}
	
	
	public void setSequence(long kseq) {
		kltpHeader.putLong(24,kseq);
	}
	

	
	@Override
	public long getTotalLength() {
		return KLTP_HEADER_LENGTH+kltpData.limit();
	}

	@Override
	public void writeToChannel(WritableByteChannel dto) throws IOException {
		setPayloadLength(kltpData.limit());
		dto.write(kltpHeader.slice(0, KLTP_HEADER_LENGTH));
		dto.write(kltpData.slice(0, kltpData.limit()));
	}

	@Override
	public void readFromChannel(ReadableByteChannel din, long length) throws IOException {
		kltpHeader.clear();
		KNEChannels.readFully(din ,kltpHeader);
		kltpHeader.flip();
		kltpData=NetworkPacket.bufferAllocator.allocate(getPayloadLength());
		KNEChannels.readFully(din, kltpData);
		kltpData.flip();
	}

	public static IPv6Payload readKLTPPacketFromChannel(ReadableByteChannel din) throws IOException {
		KLTPPacket pack=new KLTPPacket();
		pack.readFromChannel(din);
		return pack;
	}

	
	
	private int sendCounter=0;

	public int getSendCounter() {
		return sendCounter;
	}
	
	public void incSendCounter() {
		sendCounter++;
	}
	
	public int getDataSize() {
		return kltpData.limit();
	}
	
	public String toString() {
		StringBuilder sb=new StringBuilder();
		switch(getType()) {
		case KLTP_TYPE_DATA:
		sb.append("DATA ");
		sb.append(getUUID());
		sb.append(' ');
		sb.append(getSequence());
		sb.append(' ');
		sb.append(getKLTPData());
		break;
		case KLTP_TYPE_DATAFIN:
			sb.append("DATAFIN ");
			sb.append(getUUID());
			sb.append(' ');
			sb.append(getSequence());
			sb.append(' ');
			sb.append(getKLTPData());
			break;
		case KLTP_TYPE_ACK:
			sb.append("ACK ");
			sb.append(getUUID());
			sb.append(' ');
			sb.append(getSequence());
			break;
		default:
			sb.append("UNKNOWN ");
			sb.append(getUUID());
			break;
		}
		return sb.toString();
	}

	public ByteBuffer getKLTPData() {
		return kltpData;
	}




	

}
