// Copyright (c) 2015 D1SM.net

package net.fs.cap;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.concurrent.LinkedBlockingQueue;

import net.fs.rudp.Route;

public class VDatagramSocket extends DatagramSocket{
	
	boolean useTcpTun=true;
	
	boolean client=true;
	
	LinkedBlockingQueue<TunData> packetList=new LinkedBlockingQueue<TunData> ();
	
	CapEnv capEnv;
	
	int localPort;
	
	Object syn_tun=new Object();
	
	boolean tunConnecting=false;

	public VDatagramSocket() throws SocketException {
		
	}

	public VDatagramSocket(int port) throws SocketException {
		localPort=port;
	}
	
	 public int getLocalPort() {
		 return localPort;
	 }

	public void send(DatagramPacket p) throws IOException  {
		TCPTun tun=null;
		if(client){
			tun=capEnv.tcpManager.getDefaultTcpTun();
			if(tun!=null){
				if(!tun.remoteAddress.getHostAddress().equals(p.getAddress().getHostAddress())
						||CapEnv.toUnsigned(tun.remotePort)!=p.getPort()){
						capEnv.tcpManager.removeTun(tun);
						capEnv.tcpManager.setDefaultTcpTun(null);
					}
			}else {
				tryConnectTun_Client(p.getAddress(),(short) p.getPort());
				tun=capEnv.tcpManager.getDefaultTcpTun();
			}
		}else {
			tun=capEnv.tcpManager.getTcpConnection_Server(p.getAddress().getHostAddress(), (short) p.getPort());
		}
		if(tun!=null){
			if(tun.preDataReady){
				tun.sendData(p.getData());
			}else{
				throw new IOException("隧道未连接!");
			}
		}else{
			
			throw new IOException("隧道不存在! "+" thread "+Route.es.getActiveCount()+" "+p.getAddress()+":"+p.getPort());
		}
	}
	
	
	void tryConnectTun_Client(InetAddress dstAddress,short dstPort){
		synchronized (syn_tun) {
			if(capEnv.tcpManager.getDefaultTcpTun()==null){
				if(tunConnecting){
					try {
						syn_tun.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}else {
					tunConnecting=true;
					try {
						capEnv.createTcpTun_Client(dstAddress.getHostAddress(), dstPort);
					} catch (Exception e) {
						e.printStackTrace();
					}
					tunConnecting=false;
				}
			}
		}
	}
	
	
	public synchronized void receive(DatagramPacket p) throws IOException {
		TunData td=null;
		try {
			td=packetList.take();
			p.setData(td.data);
			p.setLength(td.data.length);
			p.setAddress(td.tun.remoteAddress);
			p.setPort(CapEnv.toUnsigned(td.tun.remotePort));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	void onReceinveFromTun(TunData td){
		packetList.add(td);
	}

	public boolean isClient() {
		return client;
	}

	public void setClient(boolean client) {
		this.client = client;
	}

	public CapEnv getCapEnv() {
		return capEnv;
	}

	public void setCapEnv(CapEnv capEnv) {
		this.capEnv = capEnv;
		capEnv.vDatagramSocket=this;
	}

}