package net.fs.cap;

import net.fs.rudp.Route;

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;

public class VDatagramSocket extends DatagramSocket {

    final Object syn_tun = new Object();
    boolean useTcpTun = true;
    boolean client = true;
    LinkedBlockingQueue<TunData> packetList = new LinkedBlockingQueue<>();
    CapEnv capEnv;
    int localPort;
    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;
        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;
        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;
    }

}