package org.kne.cloud.network.klalb;

import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.BindException;
import java.net.ConnectException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NoRouteToHostException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketImpl;
import java.net.SocketOptions;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import org.kne.acclerate.FastLib;
import org.kne.cloud.network.ThreadTool;
import org.kne.cloud.network.VirtualSocketImpl;
import org.kne.debug.TimeDebugger;
import org.kne.io.Data;

import com.google.gson.internal.Pair;

import java.util.*;

public class KLALBVirtualSocketImpl extends VirtualSocketImpl implements BindableKLALBPacketConsumer{
	
	/*
	private PrintStream dbg;
	{
	try {
	dbg=	new PrintStream("debug.csv");
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	}
	
	}
	*/
	
	private KLALBController controller;

	protected int getInputchachesize() {
		return inputchachesize;
	}

	protected void setInputchachesize(int inputchachesize) {
		this.inputchachesize = inputchachesize;
	}

	protected int getOutputchachesize() {
		return outputchachesize;
	}

	protected void setOutputchachesize(int outputchachesize) {
		this.outputchachesize = outputchachesize;
	}

	private volatile int inputchachesize = LIMIT * 2000;
	private volatile int outputchachesize = LIMIT * 200;
	private volatile boolean nodelay=false;
	private volatile long delaytime=1;
	
	public long getDelaytime() {
		return delaytime;
	}

	public void setDelaytime(long delaytime) {
		this.delaytime = delaytime;
	}
	protected Inet6Address remoteaddr;
	protected Inet6Address localaddr;{
		try {
			localaddr=(Inet6Address) Inet6Address.getByName("::0");
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
	}
	//private CountDownLatch reseted=new CountDownLatch(1);
	private BlockingQueue<Object[]> backlogQueue;
	private Thread sendDequeLock;
	private Queue<DATATPacket> sendDeque = new ConcurrentLinkedQueue<>();
	private Map<Long,DATATPacket> sendmap=new HashMap<>();
	private volatile Thread sendthread;
	//private List<DATATPacket> sendlist=new ArrayList<>();
	private TimerTask sendCheckTask=new SendCheckTask();
	private class SendCheckTask extends TimerTask{
		
		public void run() {
			synchronized (sendmap) {
				Collection<DATATPacket> cdp=sendmap.values();
				for (Iterator iterator = cdp.iterator(); iterator.hasNext();) {
					DATATPacket dtp = (DATATPacket) iterator.next();
					try {
						long x=System.nanoTime();
						long dt=x-dtp.resendtimer;
						long limit= dtp.getSendRecord().size()*RTTAvg*6+10000000;
						if(dt>limit) {
							if(dtp.getSendRecord().size()>=20) {
								throw new IOException("send error!");
							}
							controller.sendPacketToAddress((Inet6Address) remoteaddr,dtp, 5-1);
								System.out.println("第"+(dtp.getSendRecord().size()-1)+"次重传："+dtp);
							dtp.resendtimer=x;
							
						}
					} catch (IOException e) {
						e.printStackTrace();
						try {
							close0(true);
						} catch (IOException e1) {
							e1.printStackTrace();
						}
						break;
					}
				}
				
			}
				/*for (int i = 0; i < sendlist.size(); i++) {
					try {
						DATATPacket dtp= sendlist.get(i);
						long x=System.nanoTime();
						long dt=x-dtp.resendtimer;
						long limit= dtp.getSendRecord().size()*(1000000000L*i+RTTAvg);
						if(dt>limit) {
							if(dtp.getSendRecord().size()>=30) {
								throw new IOException("send error!");
							}
							controller.sendPacketToAddress((Inet6Address) address,dtp, 5-1);
								System.out.println("第"+(dtp.getSendRecord().size()-1)+"次重传："+dtp);
							dtp.resendtimer=x;
							
						}
					} catch (IOException e) {
						e.printStackTrace();
						try {
							close();
						} catch (IOException e1) {
							e1.printStackTrace();
						}
						break;
					}
				}*/
			
			//System.out.println(isClosed());
		}
		
	}
	
	private TimerTask flowControlTask=new FlowControlTask();
	private class FlowControlTask extends TimerTask{

		@Override
		public void run() {
			if(sendDeque.isEmpty()) {
				try {if(getLocalPort()!=0&&getPort()!=0)
					if(remoteaddr instanceof Inet6Address&&(!remoteaddr.isAnyLocalAddress()))
					controller.sendPacketToAddress((Inet6Address) remoteaddr, new ACKTPacket(getLocalPort(),getPort(), -1,
							true,0), 0);
				} catch (IOException e) {
					try {
						close0(true);
					} catch (IOException e1) {
						e1.printStackTrace();
					}
					e.printStackTrace();
				}
				
			}
		}
	
	}
	
	
	protected boolean isListening() {
		return backlogQueue != null;
	}

	//private int inputcross = 0;
	private List<Object> inputchache = new ArrayList<>();
	private long inputcount = 0;
	private volatile boolean avaliable = true;
	
	
	private long RTTAvg=1000000000L;
	private volatile boolean ignoreBindCheck;
	private boolean connected;


	
	
	
	private int countInputBytes() {
			AtomicInteger i = new AtomicInteger(0);
			sendDeque.forEach((c) -> {
				i.addAndGet(c.getSize());
			});
			return i.get();
		}
	private int countOutputBytes() {
		AtomicInteger i = new AtomicInteger(0);
		sendmap.values().forEach((c) -> {
			i.addAndGet(c.getSize());
		});
		return i.get();
	}
	public KLALBController getController() {
		return controller;
	}
	public KLALBVirtualSocketImpl(KLALBController kc) {
		super();
		this.controller = kc;
	}

	@Override
	public void setOption(int optID, Object value) throws SocketException {
			switch(optID) {
			case SocketOptions.TCP_NODELAY:
				nodelay=(boolean) value;
				break;
			case SocketOptions.SO_RCVBUF:
				inputchachesize=(int) value;
				break;
			case SocketOptions.SO_SNDBUF:
				outputchachesize=(int)value;
				break;
			}
	}

	@Override
	public Object getOption(int optID) throws SocketException {
		switch(optID) {
		case SocketOptions.TCP_NODELAY:
			return nodelay;
		case SocketOptions.SO_RCVBUF:
			return inputchachesize;
		case SocketOptions.SO_SNDBUF:
			return outputchachesize;
		case SocketOptions.SO_BINDADDR:
			return localaddr;
					default:
						return null;		
		}
		
	}

	@Override
	protected void create(boolean stream) throws IOException {
		if (!stream) {
			throw new RuntimeException("please use KLALBVirtualDatagramSocket to process udp packet");
		}
	}

	@Override
	protected void connect(String host, int port) throws IOException {
		connect(InetAddress.getByName(host), port);
	}

	@Override
	protected void connect(InetAddress address, int port) throws IOException {
		connect(new InetSocketAddress(address, port), 10000);
	}

	@Override
	protected void connect(SocketAddress address, int timeout) throws IOException {
		if(!ignoreBindCheck)
		if (!controller.getStreamPortBinder().checkIsBind(this)) {
			bind(Inet6Address.getByName("::0"), 0);
		}
		port = ((InetSocketAddress) address).getPort();
		this.address=this.remoteaddr = (Inet6Address) ((InetSocketAddress) address).getAddress();
		controller.getStreamPortBinder().connect(this);
		
		
		if(connected)
			throw new SocketException("already connected");
		connected=true;
		
		controller.getTimer().schedule(sendCheckTask, 50, 50);
		//controller.sendPacketToAddress((Inet6Address) this.remoteaddr, new SYNTPacket(localport, port), 0,1);
		try {
		getKVSIOutputStream().write(compress);
		getKVSIOutputStream().forceFlush();
		getKVSIOutputStream().waitForAllAcknowledged(timeout);
			if(compress==1) {
			vout=new DeflaterOutputStream(vout, new Deflater(Deflater.BEST_COMPRESSION, true), LIMIT, true);
			}else {
			vout = getKVSIOutputStream();
			}
		int comp=getKVSIInputStream().read();
		if(comp==1) {
			vin=new InflaterInputStream(getKVSIInputStream(),new Inflater(true),LIMIT);
		}else {
		vin = getKVSIInputStream();
			
		}
		}catch(SocketTimeoutException e) {
			throw new SocketTimeoutException("connect time out");
		}catch(SocketException e) {//e.printStackTrace();
			throw new ConnectException("connect refused");
		}
		
		controller.getTimer().schedule(flowControlTask, 5000, 5000);
	}

	//private volatile boolean connecting = false;

	/*public boolean isConnecting() {
		return connecting;
	}*/
	@Override
	protected void bind(InetAddress host, int port) throws IOException {
		bind(host,port,false);
	}
	protected void bind(InetAddress host, int port,boolean ignoreBindCheck) throws IOException {
		if (host.equals(Inet4Address.getByName("0.0.0.0"))) {
			host = Inet6Address.getByName("::0");
		}
		if (!(host instanceof Inet6Address)) {
			throw new IllegalArgumentException("invalid address type, KLALB socket can only use IPV6 virtualaddress");
		}
		if ((!host.isAnyLocalAddress()) && (!host.equals(controller.getSelf()))) {
			throw new BindException("must bind to self");
		}
		localaddr = (Inet6Address) host;
		localport=port;
		this.ignoreBindCheck=ignoreBindCheck;
		if(!ignoreBindCheck)
		controller.getStreamPortBinder().bind(this);
	}

	@Override
	public int getPort() {
		return super.getPort();
	}

	@Override
	protected void listen(int backlog) throws IOException {
		backlogQueue = new ArrayBlockingQueue<>(backlog);
		controller.getStreamPortBinder().listen(this);
		address=localaddr;
	}

	@Override
	protected void accept(SocketImpl s) throws IOException {
			KLALBVirtualSocketImpl kvsi = (KLALBVirtualSocketImpl) s;
			
			Object[] p=null;
			while(true) {
				if (isClosed())
				throw new SocketException("Socket is closed");
			p=backlogQueue.peek();
			if(p!=null) {
				break;
			}
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			}
			InetSocketAddress isa = (InetSocketAddress) p[0];
			kvsi.inputchachesize=inputchachesize;
			kvsi.outputchachesize=outputchachesize;
			
			kvsi.address =  isa.getAddress();
			kvsi.remoteaddr=(Inet6Address) isa.getAddress();
			
			kvsi.bind(localaddr, localport,true);
			kvsi.accept((KLALBRemoteLine)p[1],(KLALBPacket) p[2]);
			kvsi.connect(isa, 5000);
			backlogQueue.poll();
			/*kvsi.port = isa.getPort();
			kvsi.localport = localport;
			kvsi.localaddr = localaddr;*/
		
	}

	private InputStream vin;
	private OutputStream vout;

	private class KVSIInputStream extends InputStream {
		private DATATPacket dtp = null;
		private int count = 0;

		private boolean shutdown=false;
		@Override
		public int read() throws IOException {
			if (dtp == null ) {
				nextPacket();
			}
			if (dtp.getSize() == 0) {
				return -1;
			} else {
				int ret= dtp.getData()[count++] & 0xff;
				if(count==dtp.getSize()) {
					DATATPacket.arrayRecycle.recycle(dtp.getData());
					dtp=null;
				}
				return ret;
			}
		}

		private DATATPacket nextPacket() throws IOException {
			count = 0;
			while (true) {
				if (isClosed())
					throw new SocketException("Socket is closed");
				DATATPacket dtp2 = sendDeque.poll();
				if (dtp2 != null) {
					dtp = dtp2;
					checkFlowControl(dtp2);
					break;
				}
				sendDequeLock=Thread.currentThread();
				LockSupport.parkNanos(1000000);
			}
			return dtp;
		}
		private void checkFlowControl(DATATPacket dtp2) throws IOException {
			if(countInputBytes() >= inputchachesize-LIMIT*4) {
				controller.sendPacketToAddress(remoteaddr, new ACKTPacket(dtp2.getDport(), dtp2.getSport(), dtp2.getNumber(),
						true,dtp2.getSendcount()), 0);
				}
		}
		
		@Override
		public int read(byte[] b, int off, int len) throws IOException {
			if (b == null) {
				throw new NullPointerException();
			} else if (off < 0 || len < 0 || len > b.length - off) {
				throw new IndexOutOfBoundsException();
			} else if (len == 0) {
				return 0;
			}

			len = Math.min(len, available());
			
			
			if (dtp == null ) {
				nextPacket();
			}
			if (dtp.getSize() == 0) {
				return -1;
			} else {
				b[off]= dtp.getData()[count++] ;
				if(count==dtp.getSize()) {
					DATATPacket.arrayRecycle.recycle(dtp.getData());
					dtp=null;
				}
			}
			int i = 1;
			try {
				while (i < len) {

					if (dtp == null ) {
						nextPacket();
					}
					if (dtp.getSize() == 0) {
						break;
					} 
					int remainD=len-i;
					int min=Math.min(dtp.getSize()-count, remainD);
					System.arraycopy(dtp.getData(), count, b, off + i, min);
					count+=min;
					i+=min;
						//b[off + i]= dtp.getData()[count++] ;
						if(count>=dtp.getSize()) {
							DATATPacket.arrayRecycle.recycle(dtp.getData());
							dtp=null;
						}
					
				}
			} catch (IOException ee) {
			}
			return i;
		}
		/*@Override
		public int read(byte[] b, int off, int len) throws IOException {
			if (b == null) {
				throw new NullPointerException();
			} else if (off < 0 || len < 0 || len > b.length - off) {
				throw new IndexOutOfBoundsException();
			} else if (len == 0) {
				return 0;
			}

			len = Math.min(len, available());
			
			
			if (dtp == null ) {
				nextPacket();
			}
			if (dtp.getSize() == 0) {
				return -1;
			} else {
				b[off]= dtp.getData()[count++] ;
				if(count==dtp.getSize()) {
					DATATPacket.arrayRecycle.recycle(dtp.getData());
					dtp=null;
				}
			}
			int i = 1;
			try {
				for (; i < len; i++) {

					if (dtp == null ) {
						nextPacket();
					}
					if (dtp.getSize() == 0) {
						break;
					} else {
						b[off + i]= dtp.getData()[count++] ;
						if(count==dtp.getSize()) {
							DATATPacket.arrayRecycle.recycle(dtp.getData());
							dtp=null;
						}
					}
				}
			} catch (IOException ee) {
			}
			return i;
		}*/
		@Override
		public void close() throws IOException {
			try {
			close0();
			}finally {
			getKVSIOutputStream().close0();
			}
		}
		private void close0() throws IOException{
			
		}
		@Override
		public int available() throws IOException {
			AtomicInteger i = new AtomicInteger(0);
			sendDeque.forEach((V) -> {
				i.addAndGet(V.getSize());
			});
			if (dtp != null)
				i.addAndGet(dtp.getSize() - count);
			return i.get();
		}

	}

	private volatile long outputcount = 0;
	private int compress=0;

		public int getCompress() {
		return compress;
	}

	public void setCompress(int compress) {
		this.compress = compress;
	}

		private static final int LIMIT=8669;
	private class KVSIOutputStream extends OutputStream {
		
		private byte[] cache=DATATPacket.arrayRecycle.create();
		private int count=0;
		private Lock olock=new ReentrantLock();
		@Override
		public void write(int b) throws IOException {
			if (isClosed())
				throw new SocketException("Socket is closed");
			olock.lock();
				try {
			cache[count++]=(byte) b;//1429?5773?8669
			if (count >= LIMIT) {//1429?5773?8669
				flush0();
			}else {
				flush();
			}	
			}finally {
				olock.unlock();
			}
		}

		


		@Override
		public void write(byte[] b, int off, int len) throws IOException {
			if (isClosed())
				throw new SocketException("Socket is closed");

			olock.lock();
			try {
			while(len>0) {
			int remainD=LIMIT-count;
			int min=Math.min(len, remainD);
			System.arraycopy(b, off, cache, count, min);
			count+=min;
			off+=min;
			len-=min;
			if (count >= LIMIT) {//1429?5773?8669
				flush0();
			}
 			}
			/*int ol=off+len;
			for (int i = off; i < ol; i++) {
				cache[count++]=b[i];
				if (count >= LIMIT) {//1429?5773?8669
					flush0();
				}
			}*/
			flush();
			}finally {
				olock.unlock();
			}
		}
		public void waitForAllAcknowledged(int timeout) throws IOException {
			long start=System.nanoTime();
			while(true){
				if (isClosed())
					throw new SocketException("Socket is closed");
				//System.out.println(sendmap.size());
				if(sendmap.isEmpty())
						break;
				if(timeout!=0&&(System.nanoTime()-start>timeout*1000000))
					throw new SocketTimeoutException("wait for acknowledged timout");
			sendthread=Thread.currentThread();	
			LockSupport.parkNanos(1000000L);
			}
		}
		private volatile TimerTask tt;
		@Override
		public void flush() throws IOException {
				if(nodelay) {
					olock.lock();
					try {
						flush0();
					}finally {
						olock.unlock();
					}
					}else {
						AtomicReference<IOException> ioe=new AtomicReference<>();
						if(tt==null) {
						tt=new TimerTask() {
							
							@Override
							public void run() {
								if(isClosed())
									cancel();
								try {
									olock.lock();
									try {
										flush0();
									}finally {
										olock.unlock();
									}
								} catch (IOException e) {
									ioe.set(e);
								}
							}
						};
						new Timer("粘包计时线程").scheduleAtFixedRate(tt, delaytime, delaytime);
						}
						IOException ioex=ioe.get();
						if(ioex!=null) {
							ioex.fillInStackTrace();
							throw ioex;
						}
					}
		}
		public void forceFlush() throws IOException{
			olock.lock();
			try {
				flush0();
			}finally {
				olock.unlock();
			}
		}
		
		private void flush0() throws IOException {
			if (count > 0) {
				//TimeDebugger td=new TimeDebugger();
				//td.putTime("start");
				while (!avaliable) {
				try {
						Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
					}
				//td.putTime("waitForAvaliable");
					while(true){
						if (isClosed())
							throw new SocketException("Socket is closed");
						//System.out.println(sendmap.size());
							boolean b=sendmap.size()<=outputchachesize/LIMIT;
							if(b)
								break;
					sendthread=Thread.currentThread();	
					LockSupport.parkNanos(1000000L);
					}
				//td.putTime("waitForCache");
					byte[]	ba=cache;
						cache=DATATPacket.arrayRecycle.create();
				//td.putTime("flushBuffer");
						DATATPacket pack=new DATATPacket(localport, port, outputcount++, ba,count);
				//td.putTime("createPacket");
						controller.sendPacketToAddress(remoteaddr,pack,1<outputcount? 5:0,1<outputcount&&outputcount<11?2:1);
				//td.putTime("firstSend");
				synchronized (sendmap) {
						pack.resendtimer=System.nanoTime();
							sendmap.put(pack.getNumber(),pack);
					}
				//td.putTime("addToList");
				//td.print();
				/*SendTask x=new SendTask(controller, (Inet6Address) address, new DATATPacket(localport, port, outputcount++, ba,count), 5,20);
				x.run();
				synchronized (sendlist) {
				sendlist.add(x);
				}*/
				/*controller.sendPacketToAddress((Inet6Address) address,
						new DATATPacket(localport, port, outputcount++, bos.toByteArray()), 5);*/
			}
			count=0;
		}
		@Override
		public void close() throws IOException {
			try {
			close0();
			}finally {
			getKVSIInputStream().close0();
			}
		}
		private void close0() throws IOException {
			//outputclosed=true;
			flush0();
			DATATPacket pack=new DATATPacket(localport, port, outputcount++, DATATPacket.arrayRecycle.create(),0);
			controller.sendPacketToAddress(remoteaddr,pack, 5);
			synchronized (sendmap) {
				pack.resendtimer=System.nanoTime();
				sendmap.put(pack.getNumber(),pack);
		}
			try {
			while(true){
				if (isClosed())
					throw new SocketException("Socket is closed");
				//System.out.println(sendmap.size());
				if(sendmap.isEmpty())
						break;
			sendthread=Thread.currentThread();	
			LockSupport.parkNanos(1000000L);
			}
			/*SendTask x=new SendTask(controller, remoteaddr, new DATATPacket(localport, port, outputcount++, DATATPacket.arrayRecycle.create(),0), 5,20);
			x.run();
				synchronized (sendlist) {
			sendlist.add(x);
			}*/
			/*controller.sendPacketToAddress(remoteaddr,
					new DATATPacket(localport, port, outputcount++, new byte[0]), 5);*/
			}finally {
			if(tt!=null)
				tt.cancel();
			}
		}

	}

	private KVSIInputStream kvsii;
	private KVSIInputStream getKVSIInputStream() {
		if(kvsii==null)
			kvsii=new KVSIInputStream();
		return kvsii;
	}
	@Override
	public InputStream getInputStream() throws IOException {
		/*if (vin == null) {
			vin = getKVSIInputStream();
			int val= vin.read();
			if(val==1) {
				vin=new InflaterInputStream(vin,new Inflater(true),LIMIT);
			}
		}*/
		return vin;
	}
	private KVSIOutputStream kvsio;
	private KVSIOutputStream getKVSIOutputStream() {
		if(kvsio==null)
			kvsio=new KVSIOutputStream();
		return kvsio;
	}
	@Override
	public OutputStream getOutputStream() throws IOException {
		/*if (vout == null) {
			vout = getKVSIOutputStream();
			vout.write(compress);
			((KVSIOutputStream) vout).forceFlush();
			if(compress==1) {
			vout=new DeflaterOutputStream(vout, new Deflater(Deflater.BEST_COMPRESSION, true), LIMIT, true);
			}
		}*/
		return vout;
	}

	@Override
	protected int available() throws IOException {
		return getInputStream().available();
	}

	@Override
	protected void shutdownInput() throws IOException {
		throw new UnsupportedOperationException("Unsupported Shutdown Input");
		//getInputStream().close0();
	}

	@Override
	protected void shutdownOutput() throws IOException {
		throw new UnsupportedOperationException("Unsupported Shutdown Output");
		//getOutputStream().close0();
	}

	@Override
	protected void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
		// TODO 自动生成的方法存根
		super.setPerformancePreferences(connectionTime, latency, bandwidth);
	}

	@Override
	protected void close() throws IOException {
		close0(true);
		
//new Exception().printStackTrace();
	}

	private void close0(boolean b) throws IOException {
		if ( !isClosed()) {
			closed = true;
			if(!isListening() ) {
		sendCheckTask.cancel();
		flowControlTask.cancel();
		if(b)
			if(remoteaddr!=null)
			try {
				
			controller.sendPacketToAddress(remoteaddr, new RSTPacket(super.localport, super.port),
					0,2);
			} catch (NoRouteToHostException e) {
			}
		controller.getStreamPortBinder().disconnect(this);
			}else {
			//System.out.println("unlisten"+getLocalPort());
		controller.getStreamPortBinder().unlisten(this);
			}
		if(!ignoreBindCheck)
				controller.getStreamPortBinder() .unbind(this);
		}
	}

	private volatile boolean closed;

	private boolean isClosed() {
		return closed;
	}

	@Override
	protected void sendUrgentData(int data) throws IOException {
		throw new UnsupportedOperationException("urgent data unspuuorted");
		
	}

	@Override
	public int getLocalPort() {
		// TODO 自动生成的方法存根
		return super.getLocalPort();
	}

	

	public void associateSocket(Socket associateSocket) throws IOException {
		InputStream is=getInputStream();
		OutputStream os=getOutputStream();
		if((!(is instanceof KVSIInputStream))||(!(os instanceof KVSIOutputStream))) {
			throw new SocketException("cant use association because compress is enabled");
		}
		KVSIInputStream kis=(KVSIInputStream) is;
		KVSIOutputStream kos=(KVSIOutputStream) os;
		InputStream lis=associateSocket.getInputStream();
		OutputStream los=associateSocket.getOutputStream();
		Thread t1=ThreadTool.makeVThreadIfSupport("本地发送线程", ()->{
			try {
				DATATPacket dp;
				while((dp=kis.nextPacket()).getSize()!=0) {
					los.write(dp.getData(), 0, dp.getSize());
					los.flush();
				}
			}catch(IOException e) {
				e.printStackTrace();
			}finally {
				try {
					los.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				try {
					kis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
		Thread t2=ThreadTool.makeVThreadIfSupport("本地接收线程", ()->{
			try {
				int len=-1;
				while(true) {
						if((len=lis.read(kos.cache,0,LIMIT))==-1) {
							break;
						}
						kos.olock.lock();
						try {
					kos.count=len;
					kos.flush0();
						}finally {
							kos.olock.unlock();
						
					}
				}
			}catch(IOException e) {
				e.printStackTrace();
			}finally {
				try {
					lis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				try {
					kos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
		t1.start();
		t2.start();
		try {
			t1.join();
		t2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		close();
	}

	@Override
	public void accept(KLALBRemoteLine from, KLALBPacket u) {
		try {
			//System.out.println(this+" "+u);
			switch (u.getType()) {
			case KLALBPacket.RST:
				//reseted.countDown();
				if(!isListening())
					close0(false);
				break;
			case KLALBPacket.DATAT:
				DATATPacket dtp = (DATATPacket) u;
				if(isListening()) {
					if(dtp.getNumber()==0) {
						synchronized (backlogQueue) {
							
						//controller.getStreamPortBinder().checkIsConnected(new Pair);
						InetSocketAddress is=new InetSocketAddress(from.getRemoteVaddr(), dtp.getSport());
						AtomicBoolean ab=new AtomicBoolean(true);
				for (Iterator iterator = backlogQueue.iterator(); iterator.hasNext();) {
					Object[] objects = (Object[]) iterator.next();
					if(is.equals(objects[0])) {
						ab.set(false);
						break;
					}
				}	
				if(ab.get()) {
					if(controller.getStreamPortBinder().checkIsConnect(this,new InetSocketAddress(from.getRemoteVaddr(), dtp.getSport()))) {
						ab.set(false);
					}
				}
					
						
						if(ab.get())
						if(backlogQueue.offer(new Object[] { is,from,dtp})) {
						/*	controller.sendPacketToAddress(from.getRemoteVaddr(), new ACKTPacket(dtp.getDport(), dtp.getSport(),dtp.getNumber(),true,0),
									0,2);*/
						}else {
							controller.sendPacketToAddress(from.getRemoteVaddr(), new RSTPacket(dtp.getDport(), dtp.getSport()),
									0,2);
						}
						

						}
					}else {
						controller.sendPacketToAddress(from.getRemoteVaddr(), new RSTPacket(dtp.getDport(), dtp.getSport()),
								0,2);
					}
				}else {
				controller.sendPacketToAddress(from.getRemoteVaddr(), new ACKTPacket(dtp.getDport(), dtp.getSport(), dtp.getNumber(),
						countInputBytes() < inputchachesize,dtp.getSendcount()), 0,2);
				synchronized (inputchache) {
					if (dtp.getNumber() >= inputcount) {
						int currindex=(int) (dtp.getNumber()-inputcount);
						int reqsize=1+currindex;
						while(reqsize>inputchache.size()) {
							inputchache.add(new AtomicInteger());
						}
						for (int i = 0; i < currindex; i++) {
							Object o=inputchache.get(i);
							if(o instanceof AtomicInteger) {
								((AtomicInteger) o).incrementAndGet();
								if(((AtomicInteger) o).get()==50) {
								controller.sendPacketToAddress(from.getRemoteVaddr(), new NACKTPacket(dtp.getDport(), dtp.getSport(), inputcount+i), 0,1);
								System.out.println("请求快速重传："+(inputcount+i));
								}
							}
						}
						inputchache.set(currindex, dtp);
					
						Iterator<Object>itr=inputchache.iterator();
						while (itr.hasNext()) {
							Object datatPacket =  itr.next();
							if(datatPacket instanceof DATATPacket) {
								itr.remove();
								sendDeque.add((DATATPacket) datatPacket);
								LockSupport.unpark(sendDequeLock);
								inputcount++;
							}else {
								break;
							}
							
						}
						
			/*	int sze=inputchache.size();
						for (int i = 0; i < sze+1; i++) {
							if(i < sze) {
							DATATPacket klalbBlock = inputchache.get(i);
							if(dtp.getNumber()<klalbBlock.getNumber()) {
								inputchache.add(i,dtp);
								break;
							}else if(dtp.getNumber()==klalbBlock.getNumber()) {
								break;
							}
							}else {
								inputchache.add(dtp);
							}
						}
						Iterator<DATATPacket>itr=inputchache.iterator();
						while (itr.hasNext()) {
							DATATPacket datatPacket = (DATATPacket) itr.next();
							if(datatPacket.getNumber()==inputcount) {
								itr.remove();
								synchronized (sendDeque) {
								sendDeque.add(datatPacket);
								sendDeque.notifyAll();
								}
								inputcount++;
								inputcross=0;
							}else {
								inputcross++;
								if(inputcross>5) {
									inputcross=0;
									controller.sendPacketToAddress(from.getRemoteVaddr(), new NACKTPacket(dtp.getDport(), dtp.getSport(), inputcount), 32768);
									System.out.println("请求快速重传："+inputcount);
								}
								break;
							}
						}
						
					*/
					/*
						 if (dtp.getNumber() >= inputcount) {
						inputchache.add(dtp);
						while (true) {
							DATATPacket kkb = null;
							for (int i = 0; i < inputchache.size(); i++) {
								DATATPacket klalbBlock = inputchache.get(i);
								if (klalbBlock.getNumber() == inputcount) {
									inputchache.remove(i);
									i--;
									kkb = klalbBlock;
									break;
								}
							}
							if (kkb == null)
								break;
							sendDeque.add(kkb);
							synchronized (sendDeque) {
							sendDeque.notifyAll();
								
							}
							inputcount++;
						}
					}
						 */
				}
				}
				//dbg.println(from.getMonitor()+","+dtp.getNumber());
				}
				break;
			case KLALBPacket.ACKT:
				ACKTPacket ackt = (ACKTPacket) u;
				if(isListening()) {
					controller.sendPacketToAddress(from.getRemoteVaddr(), new RSTPacket(ackt.getDport(), ackt.getSport()),
							0,2);
				}else {
				avaliable = ackt.isAvaliable();
				DATATPacket kl=null;
				synchronized (sendmap) {
				
					kl=sendmap.remove(ackt.getNumber());
					
				}
				/*synchronized (sendlist) {
				int val=KLALBUtils.binarySearchDATATPacketNumber(sendlist, ackt.getNumber());
				System.out.println(val);
				if(val!=-1) {
					kl=sendlist.remove(val);
				}
				}*/
				/*synchronized (sendlist) {
					int val=0;
				for (Iterator<DATATPacket> iterator = sendlist.iterator(); iterator.hasNext();val++) {
					DATATPacket inetSocketAddress = (DATATPacket) iterator.next();
					if(inetSocketAddress.getNumber()==ackt.getNumber()) {
						iterator.remove();
						kl=inetSocketAddress;
						break;
					}
				}
				//System.out.println(val);
				}*/
				if(kl!=null) {
					if(sendthread!=null)
					LockSupport.unpark(sendthread);
				controller.removeFromSend(from.getRemoteVaddr(),kl);
				DATATPacket.arrayRecycle.recycle(kl.getData());
				if(kl.getSendRecord().size()==1) {
					long RTTC=ackt.getRcvtime()- kl.getSndtime();
					if(RTTC>RTTAvg) {
						RTTAvg=(RTTAvg+RTTC)/2;
					}else {
					RTTAvg= (RTTAvg*99+RTTC)/100;
					}
					//System.out.println(RTTAvg);
				}
				}
				}
				break;
			case KLALBPacket.NACKT:
				NACKTPacket nackt=(NACKTPacket) u;
				DATATPacket st=null;
				synchronized (sendmap) {
					st=sendmap.get(nackt.getNumber());
				}
					/*for (Iterator<DATATPacket> iterator = sendlist.iterator(); iterator.hasNext();) {
						DATATPacket st = (DATATPacket) iterator.next();
						if(st.getNumber()==nackt.getNumber()) {
							
						}
					}*/
				if(st!=null) {
					controller.sendPacketToAddress(remoteaddr,st, 5-1);
				}
				break;
			default:
				break;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public InetAddress getRemoteInetAddress() {
		return remoteaddr;
	}

	@Override
	public InetAddress getLocalInetAddress() {
		return localaddr;
	}

	@Override
	public void setLocalPort(int i) {
		localport=i;
	}

}
