using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Collections.Generic;

using FilterAPI;

namespace FilterItemWatcher
{
    internal static class Program
    {
        public static Authentication Auth;
        public static Database SQL;

        private static List<UInt16> ItemIDs = new List<UInt16>();
        private static List<Item> ExistingItems = new List<Item>();
        private static List<UInt64> WrittennItemKeys = new List<UInt64>();

        private static Boolean CheckerIsDone = true;

        private static void Main(String[] Args)
        {
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

            Program.Auth = new Authentication();
            Program.SQL = new Database(Program.Auth);

            Console.Title = String.Format("{0} Filter [Item Watcher] :: Aeris.NET :: 2017/2018", Program.Auth.ConfigValues["ServerName"]);

            foreach (String Line in File.ReadAllLines(String.Format("{0}ItemIDs.txt", AppDomain.CurrentDomain.BaseDirectory))) { ItemIDs.Add(Convert.ToUInt16(Line)); }

            Thread NewThread = new Thread(delegate ()
            {
                while (true)
                {
                    if (CheckerIsDone) { RunChecker(); }

                    Thread.Sleep(50);
                }
            });

            NewThread.Start();
        }

        private static void CurrentDomain_UnhandledException(Object Sender, UnhandledExceptionEventArgs Args) { File.WriteAllLines(String.Format("{0}{1}Crash.txt", AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName.Replace(".exe", "")), new String[] { ((Exception)Args.ExceptionObject).ToString() }); }

        private static void RunChecker()
        {
            CheckerIsDone = false;

            try
            {
                using (SqlCommand Command = Program.SQL.Connection.CreateCommand())
                {
                    Dictionary<Int32, String> sIDs = new Dictionary<Int32, String>();

                    Command.CommandText = String.Format("SELECT nCharNo, sID FROM {0}..tCharacter WHERE bDeleted = '0'", Program.Auth.ConfigValues["CharacterDB"]);

                    using (SqlDataReader Reader = Command.ExecuteReader())
                    {
                        while (Reader.Read()) { sIDs.Add(Convert.ToInt32(Reader["nCharNo"]), Convert.ToString(Reader["sID"])); }
                    }

                    String IDCommand = "WHERE ";

                    foreach (UInt16 ItemID in ItemIDs) { IDCommand += String.Format("nItemID = '{0}' OR ", ItemID); }

                    IDCommand = IDCommand.TrimEnd(" OR ".ToCharArray());

                    Command.CommandText = String.Format("SELECT nItemKey, nOwner, nItemID, dDate FROM {0}..tItem {1}", Program.Auth.ConfigValues["CharacterDB"], IDCommand);

                    using (SqlDataReader Reader = Command.ExecuteReader())
                    {
                        while (Reader.Read())
                        {
                            if (ExistingItems.Where(nItemKey => nItemKey.nItemKey == Convert.ToUInt64(Reader["nItemKey"])).FirstOrDefault() == null)
                            {
                                ExistingItems.Add(new Item
                                {
                                    nItemKey = Convert.ToUInt64(Reader["nItemKey"]),
                                    nOwner = Convert.ToInt32(Reader["nOwner"]),
                                    nItemID = Convert.ToUInt16(Reader["nItemID"]),
                                    dDate = Convert.ToDateTime(Reader["dDate"])
                                });
                            }
                        }
                    }

                    foreach (Item ExistingItem in ExistingItems.ToArray())
                    {
                        Command.CommandText = String.Format("SELECT COUNT(*) FROM {0}..tItem WHERE nItemKey = '{1}'", Program.Auth.ConfigValues["CharacterDB"], ExistingItem.nItemKey);

                        if (Convert.ToInt32(Command.ExecuteScalar()) == 0)
                        {
                            Thread.Sleep(230);

                            Command.CommandText = String.Format("SELECT COUNT(*) FROM {0}..tGameLog WHERE (nType = '69' OR nType = '89' OR nType = '511') AND nItemKey = '{1}'", Program.Auth.ConfigValues["GameLogDB"], ExistingItem.nItemKey);

                            if (sIDs.ContainsKey(ExistingItem.nOwner) && Convert.ToInt32(Command.ExecuteScalar()) == 0)
                            {
                                Console.ForegroundColor = ConsoleColor.Red;
                                Console.WriteLine("[{0}] :: {1}'s item was deleted, but was never destoryed/used/dropped. nItemKey: {2}. nItemID: {3}. nCharNo: {4}.", DateTime.Now.ToString("HH:mm:ss"), sIDs[ExistingItem.nOwner], ExistingItem.nItemKey, ExistingItem.nItemID, ExistingItem.nOwner);

                                WrittennItemKeys.Add(ExistingItem.nItemKey);
                            }

                            ExistingItems.Remove(ExistingItem);
                        }
                        else
                        {
                            Command.CommandText = String.Format("SELECT TOP 1 nOptionData FROM {0}..tItemOptions WHERE nItemKey = '{1}' AND nOptionType = '1001'", Program.Auth.ConfigValues["CharacterDB"], ExistingItem.nItemKey);

                            Int64 nOptionData = Convert.ToInt64(Command.ExecuteScalar());

                            if (nOptionData != 0 && !WrittennItemKeys.Contains(ExistingItem.nItemKey))
                            {
                                Console.ForegroundColor = ConsoleColor.Magenta;
                                Console.WriteLine("[{0}] :: {1}'s item has value that isn't 0, it is set to \"{2}\". nItemKey: {3}. nItemID: {4}. nCharNo: {5}.", DateTime.Now.ToString("HH:mm:ss"), sIDs[ExistingItem.nOwner], nOptionData, ExistingItem.nItemKey, ExistingItem.nItemID, ExistingItem.nOwner);

                                WrittennItemKeys.Add(ExistingItem.nItemKey);
                            }
                        }
                    }
                }
            }
            catch { }

            CheckerIsDone = true;
        }
    }

    public class Item
    {
        public UInt64 nItemKey { get; set; }
        public Int32 nOwner { get; set; }
        public UInt16 nItemID { get; set; }
        public DateTime dDate { get; set; }
    }
}
