// Copyright 2018 RED Software, LLC. All Rights Reserved.

using System;
using System.Data.Odbc;
using System.IO;
using System.Runtime;
using System.Threading;
using GameLogServer.Handlers;
using IgniteEngine;
using IgniteEngine.IO;
using IgniteEngine.Networking;

namespace GameLogServer
{
	internal class Program
	{
		internal static INIFile Config;
		internal static NetworkServer LogServer = new NetworkServer(NetworkConnectionType.NCT_DB_GAMELOG);
		internal static OdbcConnection AccountLogODBC;
		internal static OdbcConnection GameLogODBC;

		private static readonly string configFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{Assembly.Name}.ini");

		internal static void Main()
		{
			var stopwatch = new System.Diagnostics.Stopwatch();
			stopwatch.Start();

			Console.Title = $"{Assembly.Name} {Assembly.Version}{(System.Diagnostics.Debugger.IsAttached ? " (Debugging)" : "")} ";

			Debug.Log($"Local Time:          {DateTime.Now}");
			Debug.Log($"System (UTC) Time:   {DateTime.UtcNow}\n");

			// Check application recommendations
			if (!Environment.Is64BitProcess)
			{
				Debug.LogWarning("The process is currently not in 64-bit mode. Performance issues may occur. Press any key to continue code execution.");
				Console.ReadKey();
			}

			if (!GCSettings.IsServerGC)
			{
				Debug.LogWarning("Garbage collection is not configured to server mode.");
			}

			Debug.Log($"Loading configuration file '{Path.GetFileName(configFilePath)}'.");

			// Load configuration
			if (!File.Exists(configFilePath))
			{
				Debug.LogError("Configuration file was not found, please make sure it exists and has the correct properties. Press any key to quit.");
				Console.ReadKey();
				return;
			}

			Config = new INIFile(configFilePath, "Config");

			Debug.Log("Configuration file loaded successfully.");
			Debug.Log("Launching the server...\n");

			// Connect to the database
			InitDBConnection();

			// Store handlers
			StoreHandlers();

			// Setup networking
			LogServer.Listen(Config.GetString("gamelog_server_ip"), (ushort)Config.GetInt16("gamelog_server_port"));
			
			stopwatch.Stop();
			Debug.Log($"Time taken to start: {stopwatch.ElapsedMilliseconds}ms\n");

			// Starts the main server loop.
			new Thread(() =>
			{
				while (true)
				{
					Update(Time.Milliseconds);
					Thread.Sleep(10);
				}

			}).Start();

			// Handle console commands
			while (true)
			{
				if (Console.ReadKey(true).Key == ConsoleKey.Enter)
				{
					Console.Write("$ ");

					var input = Console.ReadLine();
					if (input?.Length > 0)
					{
						var parts = input.Split(new[] { ' ' }, 2);

						if (parts.Length > 1)
						{
							HandleConsoleCommand(parts[0], parts[1].Split(' '));
							continue;
						}

						HandleConsoleCommand(parts[0], null);
					}
				}
			}
		}

		private static void HandleConsoleCommand(string command, string[] args)
		{
			switch (command.ToLower())
			{
				case "clear":
					Console.Clear();
					break;
				case "performance":
					Debug.Log($"CPU: {Profiler.CPUUsage}% RAM: {Profiler.RAMUsage}MB");
					break;
				case "clients":
					Debug.Log($"Log Clients: {LogServer.Connections.Count}");
					break;
			}
		}

		private static void InitDBConnection()
		{
			try
			{
			    AccountLogODBC = new OdbcConnection(Config.GetString("accountlog_odbc_connection_string"));
			    AccountLogODBC.Open();

			    var accountLogStartupCommand = AccountLogODBC.CreateCommand();
			    accountLogStartupCommand.CommandText = Config.GetString("accountlog_odbc_startup_command");
			    accountLogStartupCommand.ExecuteNonQuery();

                GameLogODBC = new OdbcConnection(Config.GetString("gamelog_odbc_connection_string"));
				GameLogODBC.Open();

				var gameLogStartupCommand = GameLogODBC.CreateCommand();
			    gameLogStartupCommand.CommandText = Config.GetString("gamelog_odbc_startup_command");
			    gameLogStartupCommand.ExecuteNonQuery();
			}
			catch (Exception e)
			{
				Debug.LogException($"Could not initialize the ODBC database connections: {e.Message}");
			}
		}

		private static void StoreHandlers()
		{
			// S2S
			NetworkMessageHandler.Store(NetworkCommand.NC_MISC_S2SCONNECTION_REQ, MiscHandlers.NC_MISC_S2SCONNECTION_REQ);

			// User Logs
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_USER_LOGINFAIL, LogHandlers.NC_LOG_USER_LOGINFAIL);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_USER_LOGIN, LogHandlers.NC_LOG_USER_LOGIN);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_USER_LOGOUT, LogHandlers.NC_LOG_USER_LOGOUT);

			// Avatar Logs
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_CREATE_AVATAR, LogHandlers.NC_LOG_GAME_CREATE_AVATAR);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DELETE_AVATAR, LogHandlers.NC_LOG_GAME_DELETE_AVATAR);

			// Game Data Type Logs
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_0, LogHandlers.NC_LOG_GAME_DATA_TYPE_0);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_1, LogHandlers.NC_LOG_GAME_DATA_TYPE_1);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_2, LogHandlers.NC_LOG_GAME_DATA_TYPE_2);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_3, LogHandlers.NC_LOG_GAME_DATA_TYPE_3);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_4, LogHandlers.NC_LOG_GAME_DATA_TYPE_4);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_5, LogHandlers.NC_LOG_GAME_DATA_TYPE_5);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_6, LogHandlers.NC_LOG_GAME_DATA_TYPE_6);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_7, LogHandlers.NC_LOG_GAME_DATA_TYPE_7);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_8, LogHandlers.NC_LOG_GAME_DATA_TYPE_8);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_9, LogHandlers.NC_LOG_GAME_DATA_TYPE_9);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_A, LogHandlers.NC_LOG_GAME_DATA_TYPE_A);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_B, LogHandlers.NC_LOG_GAME_DATA_TYPE_B);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_C, LogHandlers.NC_LOG_GAME_DATA_TYPE_C);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_D, LogHandlers.NC_LOG_GAME_DATA_TYPE_D);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_E, LogHandlers.NC_LOG_GAME_DATA_TYPE_E);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_F, LogHandlers.NC_LOG_GAME_DATA_TYPE_F);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_G, LogHandlers.NC_LOG_GAME_DATA_TYPE_G);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_H, LogHandlers.NC_LOG_GAME_DATA_TYPE_H);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_I, LogHandlers.NC_LOG_GAME_DATA_TYPE_I);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_J, LogHandlers.NC_LOG_GAME_DATA_TYPE_J);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_K, LogHandlers.NC_LOG_GAME_DATA_TYPE_K);
			NetworkMessageHandler.Store(NetworkCommand.NC_LOG_GAME_DATA_TYPE_L, LogHandlers.NC_LOG_GAME_DATA_TYPE_L);

		}

		private static void Update(long now)
		{
			// Do stuff?
		}
	}
}
