package org.kne.cloud.network.srv6;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

import org.kne.cloud.network.ipv6.IPv6Packet;

public class SRv6PacketReorder {

	private IPv6PacketConsumer packetConsumer;

	public SRv6PacketReorder(IPv6PacketConsumer packetConsumer) {
		this.packetConsumer=packetConsumer;
	}

	private long jumpTime=400000000L;
	

	private long maxCacheingTime=5000000000L;
	
	private class PacketTimeEntry{
		private IPv6Packet packet;
		private boolean accepted=false;
		private long addTime;
		public PacketTimeEntry(IPv6Packet packet) {
			super();
			this.packet = packet;
			this.addTime = System.nanoTime();
		}
		
	}
	
	private Map<Long,PacketTimeEntry>map=new ConcurrentHashMap<>();
	
	
	private long orderTime=System.nanoTime();
	private AtomicLong seqptr=new AtomicLong();
	
	public void put(IPv6Packet pack, long sequence) throws IOException {
		//System.out.println(pack.getFlowLabel()+" "+sequence);
		map.put(sequence,new PacketTimeEntry(pack));
		runOrdering();
	}
	private ReentrantLock orderLock=new ReentrantLock();
	public void runOrdering() throws IOException {
		orderLock.lock();
		try {
			boolean whi=false;
			do {
				whi=false;
			
			while(true) {
			PacketTimeEntry i6p=map.remove(seqptr.get());
			if(i6p!=null) {
				System.out.println("Ord:"+i6p.packet.getFlowLabel()+" "+seqptr.get());
				seqptr.getAndIncrement();
				orderTime=System.nanoTime();
				if(packetConsumer!=null&&(!i6p.accepted)) {
					i6p.accepted=true;
					packetConsumer.accept(i6p.packet);
				}
			}else {
				/*for(int i=1;i<3;i++) {
					PacketTimeEntry i6px=map.get(seqptr.get()+i);
					if(i6px!=null) {
						if(packetConsumer!=null&&(!i6px.accepted)) {
							i6px.accepted=true;
							packetConsumer.accept(i6px.packet);
						}
					}
				}*/
				break;
			}
			}
			long ct=System.nanoTime();
			if(ct-orderTime>jumpTime) {
				orderTime=ct;
				long l=Long.MAX_VALUE;
				for(Long ent:map.keySet()) {
					if(ent<l) {
						l=ent;
					}
				}
				seqptr.set(l);
				whi=true;
			}
					
			}while(whi);
			
		}finally {
			orderLock.unlock();
		}
		for (Iterator<PacketTimeEntry> iterator = map.values().iterator(); iterator.hasNext();) {
			PacketTimeEntry type = (PacketTimeEntry) iterator.next();
			if(System.nanoTime()-type.addTime>maxCacheingTime) {
				iterator.remove();
			}
		}
	}

}
