package org.kne.cloud.network;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class PacketRebuilder implements ReadableByteChannel{
	private static final ByteBufferPool bbfp=new ByteBufferPool(100, 65535);
	private long timeout;
	
	public PacketRebuilder(ReadableByteChannel readableChannel,long timeout) {
		this.timeout=timeout;
		this.readableChannel=readableChannel;
	}
	
	@Override
	public String toString() {
		return "PacketRebuilder [timeout=" + timeout + "]";
	}

	public long getTimeout() {
		return timeout;
	}

	private Map<Integer,ReceiveEntry> recvc=new HashMap<>();
	private class ReceiveEntry{
		private long createTime=System.nanoTime();
		private ByteBuffer[] byteBuffers;
		public ReceiveEntry(ByteBuffer[] byteBuffers) {
			super();
			this.byteBuffers = byteBuffers;
		}
		
		
		public ByteBuffer[] getByteBuffers() {
			return byteBuffers;
		}
		
		public boolean isFull() {
			for (int i = 0; i < byteBuffers.length; i++) {
				if(byteBuffers[i]==null)
					return false;
			}
			return true;
		}

		public boolean checkTimeout() {
			return (System.nanoTime()-createTime)>timeout;
		}
		
		
	}
	
	private ReadableByteChannel readableChannel;
	
	
	public ReadableByteChannel getReadableChannel() {
		return readableChannel;
	}

	public int read(ByteBuffer des) throws IOException {
		while(readableChannel.isOpen()) {
			ByteBuffer cac=bbfp.borrow();
			readableChannel.read(cac);
			cac.flip();
			int ser=cac.getInt(0);
			int countofpiece=cac.get(4);
			int i=cac.get(5);
			//System.out.println(cac+" "+ser+" "+countofpiece+" "+i);
			if(recvc.containsKey(ser)) {
				
			}else {
				recvc.put(ser, new ReceiveEntry(new ByteBuffer[countofpiece]));
			}
			ReceiveEntry reet=recvc.get(ser);
			reet.getByteBuffers()[i]=cac;
			if(reet.isFull()) {
				int rez= rebuild(des ,reet.getByteBuffers());
				for (int j = 0; j < reet.getByteBuffers().length; j++) {
					bbfp.back(reet.getByteBuffers()[j]);
				}
				recvc.remove(ser);
				return rez;
			}
			for (Iterator<Entry<Integer, ReceiveEntry>> iterator = recvc.entrySet().iterator(); iterator.hasNext();) {
				Entry<Integer, ReceiveEntry> type = (Entry<Integer, ReceiveEntry>) iterator.next();
				ReceiveEntry rcvn=type.getValue();
				if(rcvn.checkTimeout()) {
					for (int j = 0; j < rcvn.getByteBuffers().length; j++) {
						if(rcvn.getByteBuffers()[j]!=null)
						bbfp.back(rcvn.getByteBuffers()[j]);
					}
					iterator.remove();
				}
			}
		}
		return -1;
	}
	public int rebuild(ByteBuffer des, ByteBuffer[]src) {
		//StringBuilder sbd=new StringBuilder("Rebuild:");
		int oldpos=des.position();
		for (int i = 0; i < src.length; i++) {
			src[i].getInt();
			src[i].get();
			src[i].get();
			des.put(src[i]);
		//	sbd.append(src[i].limit()+" ");
		}
		//System.out.println(sbd);
		return des.position()-oldpos;
	}

	@Override
	public boolean isOpen() {
		return readableChannel.isOpen();
	}

	@Override
	public void close() throws IOException {
		readableChannel.close();
	}
	
	

}
