using System;
using System.Net;
using System.Threading;
using System.Reflection;
using System.Net.Sockets;
using System.Collections.Generic;

using FilterAPI.Portals;
using FilterAPI.Networking;

namespace FilterZone.ZoneNetworking
{
    internal class ZoneClient : ClientMulti, IDisposable
    {
        public Guid ClientGUID = Guid.NewGuid();

        public FiestaCrypto CCrypto = null;
        public FiestaCrypto SCrypto = null;

        public String sID = String.Empty;
        public Int32 nCharNo = -1;
        public Int32 nUserNo = -1;
        public Boolean IsReady;
        public UInt16 MapObjectID;
        public Int32 X = -1;
        public Int32 Y = -1;
        public Boolean CanWalk = true;
        public Boolean SentMessages = false;
        public UInt32 BuyItemID = UInt32.MaxValue;
        public Boolean BuyNeedsConfirm = false;
        public UInt16 RidingID = UInt16.MaxValue;
        public Boolean NeedGDC = true;
        public Boolean SPMC = true;
        public Boolean ShowPlayers = true;
        public String sMap = String.Empty;
        public String InstanceID = String.Empty;
        public Int32 sMapPlayers = -1;
        public Int32 sMapMobs = -1;
        public Int32 ZoneTotalPlayers = -1;
        public Int32 ZoneTotalMobs = -1;
        public Boolean SentWarning = false;
        public MultiPortal MP = null;
        public Boolean AutoAttacking = false;
        
        public UInt16 ClickedNPCID = UInt16.MaxValue;
        public UInt16 ClickedMapObjectID = UInt16.MaxValue;

        public Boolean IsVending = false;
        public Boolean IsMounted = false;
        public DateTime OpenDateTime = DateTime.Now;
        public DateTime EditDateTime = DateTime.Now;
        public DateTime RoarDateTime = DateTime.Now;

        public Boolean TradeWaitAccept = false;
        public Boolean InTrade = false;
        public UInt16 TradeMapObjectID = UInt16.MaxValue;

        public DateTime SellDateTime = DateTime.Now;
        public DateTime PremiumDateTime = DateTime.Now;
        public DateTime SkillDateTime = DateTime.Now;
        
        public Dictionary<Int64, Int16> ItemAmounts = new Dictionary<Int64, Int16>();
        public List<MapObject> MapObjects = new List<MapObject>();

        public ZoneClient(Socket AcceptedSocket, IPEndPoint AcceptedEndPoint)
            : base(AcceptedSocket, AcceptedEndPoint, IPAddress.Parse(Program.Auth.ConfigValues[String.Format("Zone{0}ConnectIP", Program.ZoneID)]), Convert.ToInt32(Program.Auth.ConfigValues[String.Format("Zone{0}ConnectPort", Program.ZoneID)]))
        {
            Program.ZoneClients.TryAdd(ClientGUID, this);

            if (Program.SQL.GetbBlockForsIP(Convert.ToString(ClientEndPoint.Address)) || Program.SQL.GetnAmountForsIP(Convert.ToString(ClientEndPoint.Address)) >= Convert.ToInt64(Program.Auth.ConfigValues["MaximumCPIP"])) { Dispose(); }
        }

        public void SendCrypto()
        {
            CCrypto = new FiestaCrypto(Program.Auth, 2);

            using (var ServerPacket = new Packet(2, 7))
            {
                ServerPacket.PacketWriter.Write((UInt16)2);

                SendPacketClient(ServerPacket);
            }
        }

        public override void ReceivedClient(Byte[] Buffer)
        {
            if (CCrypto != null) { CCrypto.Crypt(Buffer, 0, Buffer.Length); }

            if (Buffer != null && Buffer.Length >= 2)
            {
                using (var ClientPacket = new Packet(Buffer))
                {
                    if (!ClientPacket.SetHeaderAndType()) { Dispose(); }
                    else if (nUserNo == -1 && (ClientPacket.Header != 6 || ClientPacket.Type != 1)) { Dispose(); }
                    else if (Program.ZoneHandlers.HasHandler(ClientPacket.Header, ClientPacket.Type))
                    {
                        try
                        {
                            MethodInfo PacketMethod = Program.ZoneHandlers.GetHandler(ClientPacket.Header, ClientPacket.Type);

                            Action PacketAction = Program.ZoneHandlers.GetAction(PacketMethod, this, ClientPacket);
                            PacketAction();
                        }
                        catch (Exception Error)
                        {
                            SendPacketServer(ClientPacket);

                            Program.SendConsoleText(ConsoleColor.Red, String.Format("Header: {0} || Type: {1}\n{2}", ClientPacket.Header, ClientPacket.Type, Error.ToString()));
                        }
                    }
                    else { SendPacketServer(ClientPacket); }
                }
            }
            else { Dispose(); }
        }

        public void SendPacketClient(Packet ServerPacket)
        {
            Byte[] PacketBuffer;

            ServerPacket.ToArray(out PacketBuffer);

            SendClient(PacketBuffer);
        }

        public override void ReceivedServer(Byte[] Buffer)
        {
            if (Buffer != null && Buffer.Length >= 2)
            {
                using (var ServerPacket = new Packet(Buffer))
                {
                    if (!ServerPacket.SetHeaderAndType()) { Dispose(); }
                    else if (Program.ZoneHandlers.HasHandler(ServerPacket.Header, ServerPacket.Type))
                    {
                        try
                        {
                            MethodInfo PacketMethod = Program.ZoneHandlers.GetHandler(ServerPacket.Header, ServerPacket.Type);

                            Action PacketAction = Program.ZoneHandlers.GetAction(PacketMethod, this, ServerPacket);
                            PacketAction();
                        }
                        catch (Exception Error)
                        {
                            SendPacketClient(ServerPacket);

                            Program.SendConsoleText(ConsoleColor.Red, String.Format("Header: {0} || Type: {1}\n{2}", ServerPacket.Header, ServerPacket.Type, Error.ToString()));
                        }
                    }
                    else { SendPacketClient(ServerPacket); }
                }
            }
            else { Dispose(); }
        }

        public void SendPacketServer(Packet ClientPacket)
        {
            Byte[] PacketBuffer;

            ClientPacket.ToArray(SCrypto, out PacketBuffer);

            SendServer(PacketBuffer);
        }

        public override void Disconnected()
        {
            ZoneClient ZClient;

            Program.ZoneClients.TryRemove(ClientGUID, out ZClient);
        }
    }
}
