package com.yeejoin.amos.transmit.tcp;

import io.netty.channel.Channel;
import org.apache.log4j.Logger;

import java.net.InetSocketAddress;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;

public class TcpQueue implements RpcServerListener<String>{
    ScheduledExecutorService service_send = null;
    ScheduledExecutorService service_recv = null;
    private volatile static TcpQueue instance = null;
    private static final BlockingQueue<TcpTransmitParam> sendQueue = new LinkedBlockingQueue<TcpTransmitParam>();
    private static final BlockingQueue<TcpTransmitParam> recvQueue = new LinkedBlockingQueue<TcpTransmitParam>();
    private static final ConcurrentHashMap<String, Channel> mapping = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, RpcRecvListener> recvListeners = new ConcurrentHashMap<>();

    private TcpQueue() {
        RpcTcpServer rpc =  RpcTcpServer.getInstance();
        rpc.setListener(this);
        rpc.start();
    }

    public static TcpQueue getInstance(){
        //先检查实例是否存在，如果不存在才进入下面的同步块
        if(instance == null){
            //同步块，线程安全的创建实例
            synchronized(TcpQueue.class){
                //再次检查实例是否存在，如果不存在才真的创建实例
                if(instance == null){
                    instance = new TcpQueue();
                }
            }
        }
        return instance;
    }

    public void addSendData(TcpTransmitParam b) {
        sendQueue.add(b);
    }

    public void addRecvData(TcpTransmitParam b){
        recvQueue.add(b);
    }

    public void register(String ip, RpcRecvListener listener) {
        recvListeners.put(ip, listener);
    }

    public void unRegister(RpcRecvListener listener) {
        for (String key: recvListeners.keySet()) {
            RpcRecvListener l = recvListeners.get(key);
            if (listener.equals(l)) {
                recvListeners.remove(key);
            }
        }
    }

    public void start() {
        service_send = Executors.newSingleThreadScheduledExecutor();
        service_send.execute(send_runnable);
        service_recv = Executors.newSingleThreadScheduledExecutor();
        service_recv.execute(recv_runnable);
    }


    Runnable send_runnable = new Runnable() {
        public void run() {
            while(true) {
                try {
                    TcpTransmitParam data = sendQueue.take();
                    System.out.println(new String(data.getData()));
                    for (String key: recvListeners.keySet()) {
                        RpcRecvListener l = recvListeners.get(key);
                        if (key.equals(data.getIp())) {
                            l.recv(data.getData());
                        }
                    }
                } catch (Exception  e) {
                    Logger.getLogger(this.getClass()).error(e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    };


    Runnable recv_runnable = new Runnable() {
        public void run() {
            while(true) {
                try {
                    TcpTransmitParam data = recvQueue.take();
                    System.out.println(new String(data.getData()));

                    for (String key: mapping.keySet()) {
                        Channel channel = mapping.get(key);
                        InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress();
                        String ip = insocket.getAddress().getHostAddress();
                        if (ip.equals(data.getIp())) {
                            channel.writeAndFlush(new String(data.getData()));
                        }
                    }
                } catch (Exception  e) {
                    Logger.getLogger(this.getClass()).error(e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    };

    @Override
    public void onMessageResponseServer(String msg, String ip) {
        Channel channel = mapping.get(ip);
        TcpTransmitParam data = new TcpTransmitParam();
        data.setData(msg.getBytes());
        data.setIp(ip);
        this.addSendData(data);
    }

    @Override
    public void onStartServer() {

    }

    @Override
    public void onStopServer() {
        mapping.clear();
    }

    @Override
    public void onChannelConnect(Channel channel) {
        InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress();
        String ip = insocket.getAddress().getHostAddress();
        mapping.put(ip, channel);
    }

    @Override
    public void onChannelDisConnect(Channel channel) {
        InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress();
        String ip = insocket.getAddress().getHostAddress();
        mapping.remove(ip);
    }
}
