using System;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using MoonSharp.Interpreter;

namespace Barotrauma
{
    internal enum LuaCsMessageOrigin
    {
        LuaCs,
        Unknown,
        LuaMod,
        CSharpMod,
    }

    partial class LuaCsLogger
    {
#if SERVER
        private const string LogPrefix = "SV";
        private const int NetMaxLength = 1024;
        private const int NetMaxMessages = 60;

        // This is used so its possible to call logging functions inside the serverLog
        // hook without creating an infinite loop
        private static bool lockLog = false;
#else
        private const string LogPrefix = "CL";
#endif

        public static LuaCsMessageLogger MessageLogger;
        public static LuaCsExceptionHandler ExceptionHandler;

        public static void HandleException(Exception ex, LuaCsMessageOrigin origin)
        {
            switch (ex)
            {
                case NetRuntimeException netRuntimeException:
                    if (netRuntimeException.DecoratedMessage == null)
                    {
                        LogError(netRuntimeException.ToString(), origin);
                    }
                    else
                    {
                        // FIXME: netRuntimeException.ToString() doesn't print the InnerException's stack trace...
                        LogError($"{netRuntimeException.DecoratedMessage}: {netRuntimeException}", origin);
                    }
                    break;
                case InterpreterException interpreterException:
                    if (interpreterException.DecoratedMessage == null)
                    {
                        LogError(interpreterException.ToString(), origin);
                    }
                    else
                    {
                        LogError(interpreterException.DecoratedMessage, origin);
                    }
                    break;
                default:
                    var msg = ex.StackTrace != null
                        ? ex.ToString()
                        : $"{ex}\n{Environment.StackTrace}";
                    LogError(msg, origin);
                    break;
            }
        }

        public static void LogError(string message, LuaCsMessageOrigin origin)
        {
            switch (origin)
            {
                case LuaCsMessageOrigin.LuaCs:
                case LuaCsMessageOrigin.Unknown:
                    LogError($"[{LogPrefix} ERROR] {message}");
                    break;
                case LuaCsMessageOrigin.LuaMod:
                    LogError($"[{LogPrefix} LUA ERROR] {message}");
                    break;
                case LuaCsMessageOrigin.CSharpMod:
                    LogError($"[{LogPrefix} CS ERROR] {message}");
                    break;
            }
        }

        public static void LogError(string message)
        {
            Log($"{message}", Color.Red, ServerLog.MessageType.Error);
        }

        public static void LogMessage(string message, Color? serverColor = null, Color? clientColor = null)
        {
            if (serverColor == null) { serverColor = Color.MediumPurple; }
            if (clientColor == null) { clientColor = Color.Purple; }

#if SERVER
            Log(message, serverColor);
#else
            Log(message, clientColor);
#endif
        }

        public static void Log(string message, Color? color = null, ServerLog.MessageType messageType = ServerLog.MessageType.ServerMessage)
        {
            MessageLogger?.Invoke(message);

            DebugConsole.NewMessage(message, color);

#if SERVER
            void broadcastMessage(string m)
            {
                foreach (var client in GameMain.Server.ConnectedClients)
                {
                    //if (client.ChatMsgQueue.Count > NetMaxMessages)
                    //{
                        // If there's an error or message happening many times per second (inside Update loop for example)
                        // we will need to discart some messages so the client doesn't get overloaded by all
                        // those net messages.
                    //    continue;
                    //}

                    ChatMessage consoleMessage = ChatMessage.Create("", m, ChatMessageType.Console, null, textColor: color);
                    GameMain.Server.SendDirectChatMessage(consoleMessage, client);

                    if (!GameMain.Server.ServerSettings.SaveServerLogs || !client.HasPermission(ClientPermissions.ServerLog))
                    {
                        continue;
                    }

                    ChatMessage logMessage = ChatMessage.Create(messageType.ToString(), "[LuaCs] " + m, ChatMessageType.ServerLog, null);
                    GameMain.Server.SendDirectChatMessage(logMessage, client);
                }
            }

            if (GameMain.Server != null)
            {
                if (GameMain.Server.ServerSettings.SaveServerLogs)
                {
                    string logMessage = "[LuaCs] " + message;
                    GameMain.Server.ServerSettings.ServerLog.WriteLine(logMessage, messageType, false);

                    if (!lockLog)
                    {
                        lockLog = true;
                        GameMain.LuaCs?.Hook?.Call("serverLog", logMessage, messageType);
                        lockLog = false;
                    }
                }

                for (int i = 0; i < message.Length; i += NetMaxLength)
                {
                    string subStr = message.Substring(i, Math.Min(1024, message.Length - i));

                    broadcastMessage(subStr);
                }
            }
#endif
        }
    }

    partial class LuaCsSetup
    {
        // Compatibility with cs mods that use this method.
        public static void PrintLuaError(object message) => LuaCsLogger.LogError($"{message}", LuaCsMessageOrigin.LuaMod);
        public static void PrintCsError(object message) => LuaCsLogger.LogError($"{message}", LuaCsMessageOrigin.CSharpMod);
        public static void PrintGenericError(object message) => LuaCsLogger.LogError($"{message}", LuaCsMessageOrigin.LuaCs);

        internal void PrintMessage(object message) => LuaCsLogger.LogMessage($"{message}");

        public static void PrintCsMessage(object message) => LuaCsLogger.LogMessage($"{message}");

        internal void HandleException(Exception ex, LuaCsMessageOrigin origin) => LuaCsLogger.HandleException(ex, origin);
    }
}
