package emu.grasscutter.server.game;

import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;

import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.Utils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.DefaultEventLoop;
import kcp.highway.KcpListener;
import kcp.highway.Ukcp;

public class GameSessionManager {
    private static final DefaultEventLoop logicThread = new DefaultEventLoop();
    private static final ConcurrentHashMap<Ukcp,GameSession> sessions = new ConcurrentHashMap<>();
    private static final KcpListener listener = new KcpListener(){
        @Override
        public void onConnected(Ukcp ukcp) {
            int times = 0;
            GameServer server = Grasscutter.getGameServer();
            while (server==null){//Waiting server to establish
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    ukcp.close();
                    return;
                }
                if(times++>5){
                    Grasscutter.getLogger().error("Service is not available!");
                    ukcp.close();
                    return;
                }
                server = Grasscutter.getGameServer();
            }
            GameSession conversation = new GameSession(server);
            conversation.onConnected(new KcpTunnel(){
                @Override
                public InetSocketAddress getAddress() {
                    return ukcp.user().getRemoteAddress();
                }

                @Override
                public void writeData(byte[] bytes) {
                    ByteBuf buf = Unpooled.wrappedBuffer(bytes);
                    ukcp.write(buf);
                    buf.release();
                }

                @Override
                public void close() {
                    ukcp.close();
                }

                @Override
                public int getSrtt() {
                    return ukcp.srtt();
                }
            });
            sessions.put(ukcp,conversation);
        }

        @Override
        public void handleReceive(ByteBuf buf, Ukcp kcp) {
            byte[] byteData = Utils.byteBufToArray(buf);
            logicThread.execute(() -> {
                try {
                    GameSession conversation = sessions.get(kcp);
                    if(conversation!=null) {
                        conversation.handleReceive(byteData);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            });
        }

        @Override
        public void handleException(Throwable ex, Ukcp ukcp) {

        }

        @Override
        public void handleClose(Ukcp ukcp) {
            GameSession conversation = sessions.get(ukcp);
            if(conversation!=null) {
                conversation.handleClose();
                sessions.remove(ukcp);
            }
        }
    };

    public static KcpListener getListener() {
        return listener;
    }

    interface KcpTunnel{
        InetSocketAddress getAddress();
        void writeData(byte[] bytes);
        void close();
        int getSrtt();
    }
    interface KcpChannel{
        void onConnected(KcpTunnel tunnel);
        void handleClose();
        void handleReceive(byte[] bytes);
    }
}
