package org.kne.cloud.network.congestion;

import java.util.function.Consumer;

import org.jctools.counters.FixedSizeStripedLongCounter;
import org.kne.cloud.network.klalb.KLALBUtils;

public class DCTCP2CongestionAlgorithm implements CongestionAlgorithm {

	private static final long BURST_TIME = 1000000L;
	private static final long BIAS = 2000000L;
	private static long MIN_WINDOW = 8192;

	private Consumer<Long> windowControlConsumer;

	private long MIN_RTTVAR = 100000000L;
	private volatile long RTTVar = 1000000000L;
	private volatile long RTTAvg = 1000000000L;
	private double ECNAvg2 = 0L;
	private double alpha=0;
	private volatile FixedSizeStripedLongCounter ECNSize = KLALBUtils.createCounter();
	private volatile FixedSizeStripedLongCounter TotalSize = KLALBUtils.createCounter();
	private volatile long RTO = 500000000L;


	private volatile long window=MIN_WINDOW;
    private volatile long maxwindow = 99999999999L;
	private volatile long congressWindowSize=MIN_WINDOW;

	private long RTTstartTime = System.nanoTime();
	
	@Override
	public void reset() {
		window = MIN_WINDOW;
		if (windowControlConsumer != null) {
			windowControlConsumer.accept(window);
		}
	}

	@Override
	public void setWindowControlConsumer(Consumer<Long> windowControlConsumer) {
		this.windowControlConsumer = windowControlConsumer;
	}


	@Override
	public synchronized void putAck(long packetSize, long latencyns, boolean ecn) {
		
		
		RTTVar = (RTTVar * 3 + Math.abs(RTTAvg - latencyns)) / 4;
		RTTAvg = (RTTAvg * 7 + latencyns) / 8;
		
		RTO = RTTAvg + Math.max(MIN_RTTVAR, RTTVar * 4);// RTTVar*4

		if(ecn) {
		ECNSize.inc(packetSize);
		}
		TotalSize.inc(packetSize);
		
		
		long curr=System.nanoTime();
		if(curr-RTTstartTime>(RTTAvg+BIAS)) {
			RTTstartTime=curr;
			
			long totalSize=TotalSize.getAndReset();
			long ecnSize=ECNSize.getAndReset();
			if(totalSize!=0) {
			ECNAvg2=ecnSize/(double)totalSize;
			alpha=(alpha*7+ECNAvg2)/8;
			}
			
			if(ECNAvg2>0.5) {
					congressWindowSize-=200;
			}else if(ECNAvg2>0.01){
					congressWindowSize+=200;
			}else {
					congressWindowSize+=4000;
			}
			//System.out.println(ECNAvg2);
			//System.out.println(speed+" "+window);
			
					updateWindowSize();
			
		}
		
		
	}

	private void updateWindowSize() {
		if(congressWindowSize<MIN_WINDOW) {
			congressWindowSize=MIN_WINDOW;
		}else if(congressWindowSize>maxwindow) {
			congressWindowSize=maxwindow;
		}
		window=Math.max( congressWindowSize,MIN_WINDOW);
		if (windowControlConsumer != null) {
			windowControlConsumer.accept(window);
		}
	}

	@Override
	public void putLoss(long packetSize, long lossns, int losscounter) {

	}

	@Override
	public long getRTO() {
		return RTO;
	}

	@Override
	public void setCurrentWindowUsed(long used) {
		this.maxwindow=(long) (used*1.5)+MIN_WINDOW;
	}

	@Override
	public void setCurrentBandwidth(long bandwidth) {
		
	}

	@Override
	public String getName() {
		return "DCTCP2";
	}

}
