package org.kne.cloud.network.klalb;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

import static org.kne.cloud.network.klalb.KLALBUtils.*;
public class Monitor {

	private String name;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	private static Timer t=new Timer("带宽测量线程",true);
	
	
	private TimerTask ptt=new TimerTask() {
		
		@Override
		public void run() {
			runMonitor();
		}
	};
	protected void runMonitor() {
		updateSpeed();
		
	}
	@Override
	protected void finalize() throws Throwable {
		ptt.cancel();
	}
	private static AtomicInteger ai=new AtomicInteger();
	public Monitor() {
		this.name="Line"+ai.getAndIncrement();
		t.scheduleAtFixedRate(ptt, 1000, 1000);
	}
	public Monitor(String name) {
		this.name=name;
		t.scheduleAtFixedRate(ptt, 1000, 1000);
	}
	private AtomicLong inTraffic=new AtomicLong();
	private AtomicLong outTraffic=new AtomicLong();
	

	private AtomicLong inTrafficOld=new AtomicLong();
	private AtomicLong outTrafficOld=new AtomicLong();
	
	private volatile long inSpeed;
	private volatile long outSpeed;
	

	private volatile long inSpeedAvg;
	private volatile long outSpeedAvg;
	
	
	private volatile long inSpeedMax=1024*1024*1024;
	private volatile long outSpeedMax=1024*1024*1024;
	
	public void setInSpeedMax(long inSpeedMax) {
		this.inSpeedMax = inSpeedMax;
	}
	public void setOutSpeedMax(long outSpeedMax) {
		this.outSpeedMax = outSpeedMax;
	}
	private volatile long latencyAvg =Long.MIN_VALUE;
	private volatile long latencyMin =Long.MIN_VALUE;
	private volatile long latencyCurr =Long.MIN_VALUE;
	private volatile long jitter ;
	
	private volatile int state=0;
	public static final int OFFLINE=0;
	public static final int CONNECTING=1;
	public static final int ONLINE=2;
	
	
	private volatile long coolingTime;
	{
		resetCoolingTime();
	}
	
	public int getState() {
		return state;
	}
	public void setState(int state) {
		this.state = state;
	}
	public long getInSpeedMax() {
		return inSpeedMax;
	}
	public void updateLatency(long newlatency) {
			latencyCurr=newlatency;
		if(latencyAvg==Long.MIN_VALUE) {
			latencyAvg=newlatency;
		}else {
		long vj=Math.abs( latencyAvg-newlatency);
			jitter=(9*jitter+vj)/10;
			/*if(newlatency<latency) {
			latency=newlatency;
		}else {*/
		this.latencyAvg=(latencyAvg*9+ newlatency)/10;
		//}
		}
		
		if(latencyMin==Long.MIN_VALUE) {
			latencyMin=newlatency;
		}else {
			/*if(newlatency<latency) {
			latency=newlatency;
		}else {*/
			if(newlatency<=latencyMin) {
				latencyMin=newlatency;
			}else {
		this.latencyMin=(latencyMin*9+ newlatency)/10;
			}
		//}
		}
	}
	public long getOutSpeedMax() {
		return outSpeedMax;
	}
	
	public long getInSpeedAvg() {
		return inSpeedAvg;
	}
	public long getOutSpeedAvg() {
		return outSpeedAvg;
	}
	public long getLatencyMin() {
		return latencyMin;
	}
	public long getLatencyCurr() {
		return latencyCurr;
	}
	public long getInTraffic() {
		return inTraffic.get();
	}

	public long getOutTraffic() {
		return outTraffic.get();
	}

	public AtomicLong getInTrafficAL() {
		return inTraffic;
	}

	public long getJitter() {
		return jitter;
	}
	public AtomicLong getOutTrafficAL() {
		return outTraffic;
	}

	public long getInSpeed() {
		return inSpeed;
	}


	public long getOutSpeed() {
		return outSpeed;
	}
	
	private long updatetime=System.nanoTime();
	public void updateSpeed() {
		long d=System.nanoTime()-updatetime;
		long i=inTraffic.get()-inTrafficOld.get();
		long o=outTraffic.get()-outTrafficOld.get();
		inTrafficOld.set(inTraffic.get());
		outTrafficOld.set(outTraffic.get());
		
		
		inSpeed= i*1000000000/d;
		outSpeed= o*1000000000/d;
		updatetime=System.nanoTime();
		//System.out.println(d+" "+o+" "+i);
		/*if(inSpeed>inSpeedMax) {
			inSpeedMax=inSpeed;
		}else {
			inSpeedMax=(inSpeedMax*9999+inSpeed)/10000;
		}
		if(outSpeed>outSpeedMax) {
			outSpeedMax=outSpeed;
		}else {
			outSpeedMax=(outSpeedMax*9999+outSpeed)/10000;
		}*/

		this.inSpeedAvg=(inSpeedAvg*9+ inSpeed)/10;
		this.outSpeedAvg=(outSpeedAvg*9+ outSpeed)/10;
		
		if(changeListener!=null) {
			changeListener.accept(this);
		}
		
	}
	public void updateOutSpeedMax() {
		if(outSpeed>outSpeedMax) {
			outSpeedMax=outSpeed;
		}else {
			outSpeedMax=(outSpeedMax*9999+outSpeed)/10000;
		}
	}
	private Consumer<Monitor>changeListener;
	
	public Consumer<Monitor> getChangeListener() {
		return changeListener;
	}
	public void setChangeListener(Consumer<Monitor> changeListener) {
		this.changeListener = changeListener;
	}
	public long getLatencyAvg() {
		return latencyAvg;
	}
	public String toString() {
		StringBuilder sb=new StringBuilder();
		sb.append(name);
		sb.append('\t');
		sb.append(getDsc());
		sb.append('\t');
		sb.append(bytesUnit(outTraffic.get())).append("\u2191\t").append(bytesUnit(inTraffic.get())).append("\u2193\t").append(bytesUnit(outSpeed)).append("/s\u2191\t").append(bytesUnit(inSpeed)).append("/s\u2193\t").append(latencyAvg/1000000).append("ms");
		return sb.toString();
	}
	
	public String toString2() {
		StringBuilder sb=new StringBuilder();
		sb.append(name);
		sb.append('\n');
		sb.append(getDsc());
		sb.append('\t');
		sb.append(bytesUnit(outTraffic.get())).append("\t").append(bytesUnit(inTraffic.get())).append("\t").append(bytesUnit(outSpeed)).append("/s\t").append(bytesUnit(inSpeed)).append("/s\t").append(latencyAvg/1000000).append("ms").append("\t").append(jitter/1000000).append("ms\t");
		
		if(state==OFFLINE) {
		sb.append((coolingTime-(System.currentTimeMillis()-mls))/1000L);
		sb.append('s');
		}else {
			sb.append('/');
		}
		
		sb.append('\n');
		sb.append(bytesUnit(outSpeedMax));

		sb.append('\n');
		sb.append(bytesUnit(inSpeedMax));
		
			return sb.toString();
	}
	private String getDsc() {
		switch(state) {
		case OFFLINE:
			return "○离线";
		case CONNECTING:
			return "◐连接中";
		case ONLINE:
			return "●在线";
		}
		return null;
	}
	private volatile long mls;
	public long getCoolingTime() {
		mls=System.currentTimeMillis();
		return coolingTime;
	}
	public void incCoolingTime() {
		coolingTime<<=1;
		if(coolingTime>300000) {
			coolingTime=300000;
		}
	}
	public void resetCoolingTime() {
		coolingTime=10000;
	}

}
