using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NeonTea.Quakeball.Players;
using NeonTea.Quakeball.Networking.Packets;
using NeonTea.Quakeball.Interface;
using NeonTea.Quakeball.TeaNet.Peers;
using NeonTea.Quakeball.TeaNet.Packets;

namespace NeonTea.Quakeball.Networking.Instances {
    public class Client : NetInstance {

        private NetChaperone Net;

        private Connection Server;

        private Dictionary<ulong, NetPlayer> Players = new Dictionary<ulong, NetPlayer>();
        private NetPlayer LocalPlayer;
        private bool SelfIdentified = false;

        public override void Start(string address, int port, PeerMessageListener listener) {
            if (Peer != null) {
                return;
            }
            Peer = new Peer(Fingerprint);
            Peer.MessageListener = listener;
            Peer.Start(0);
            byte ident = Peer.RegisterProtocol(new GameProtocol(this));
            Peer.Connect(address, port, ident);

            Net = GameObject.FindGameObjectWithTag("Net").GetComponent<NetChaperone>();

            LocalPlayer = new NetPlayer(ulong.MaxValue - 1);
            LocalPlayer.Controlled = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
        }

        public override void Connected(Connection conn) {
            Terminal.Singleton.Println($"{conn.uid} Connected");
            if (Server == null) {
                Server = conn;
            } else {
                Peer.Disconnect(conn.uid);
            }
        }

        public override void Disconnected(Connection conn) {
            Terminal.Singleton.Println($"{conn.uid} Disconnected: {conn.ClosingReason}");
            if (Server == conn) {
                Server = null;
            }
        }

        public override void Handle(Connection conn, Packet packet) {
            if (packet is SelfIdentPckt) {
                SelfIdentPckt ident = (SelfIdentPckt)packet;
                LocalPlayer.Id = ident.PlayerId;
                Players.Add(LocalPlayer.Id, LocalPlayer);
                SelfIdentified = true;

                SpawnPckt spawn = new SpawnPckt();
                spawn.Location = LocalPlayer.Controlled.transform.position;
                Peer.SendReliable(Server.uid, spawn);
            } else if (packet is SpawnPckt) {
                SpawnPckt spawn = (SpawnPckt)packet;
                if (spawn.PlayerId == LocalPlayer.Id) {
                    return; // Ignore, it's their own.
                }
                Player obj = Net.SpawnPlayer(spawn.Location).GetComponent<Player>();
                NetPlayer player = new NetPlayer(spawn.PlayerId, obj);
                Players.Add(spawn.PlayerId, player);
            } else if (packet is PlayerUpdatePckt) {
                PlayerUpdatePckt updatePckt = (PlayerUpdatePckt)packet;
                HandleUpdatePlayer(updatePckt);
            } else if (packet is MultiplePlayerUpdatesPckt) {
                MultiplePlayerUpdatesPckt multiple = (MultiplePlayerUpdatesPckt)packet;
                foreach (PlayerUpdatePckt pckt in multiple.Updates) {
                    HandleUpdatePlayer(pckt);
                }
            } else if (packet is PlayerJumpPckt) {
                PlayerJumpPckt jump = (PlayerJumpPckt)packet;
                Players[jump.PlayerId].Controlled.Jump();
            } else if (packet is PlayerSyncPacket) {
                PlayerSyncPacket sync = (PlayerSyncPacket)packet;

            }
        }

        private void HandleUpdatePlayer(PlayerUpdatePckt pckt) {
            if (pckt.PlayerId == LocalPlayer.Id) {
                return; // Ignore, again.
            }
            Players[pckt.PlayerId].Controlled.ProcessUpdatePacket(ref pckt);
        }

        public override void UpdateLocalPlayer() {
            if (SelfIdentified && Server != null) {
                PlayerUpdatePckt pckt = LocalPlayer.Controlled.CreateUpdatePacket();
                Peer.SendUnreliable(Server.uid, pckt);
            }
        }

        public override void LocalPlayerJump() {
            if (SelfIdentified && Server != null) {
                PlayerJumpPckt jump = new PlayerJumpPckt();
                Peer.SendReliable(Server.uid, jump);
            }
        }

        public override void SendPlayerSync() {
            if (SelfIdentified && Server != null) {
                PlayerSyncPacket packet; // Get the sync packet for LocalPlayer
                // SendUnreliable(Server.uid, packet);
            }
        }
    }
}