using Avalonia.Logging;
using Avalonia.Utilities;
using Gommon;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common.Logging;
using System;
using System.Text;

namespace Ryujinx.Ava.UI.Helpers
{
    using AvaLogger = Avalonia.Logging.Logger;
    using AvaLogLevel = LogEventLevel;
    using RyuLogClass = LogClass;
    using RyuLogger = Ryujinx.Common.Logging.Logger;

    internal class LoggerAdapter : ILogSink
    {
        private static bool _avaloniaLogsEnabled = ConfigurationState.Instance.Logger.EnableAvaloniaLog;

        public static void Register()
        {
            AvaLogger.Sink = new LoggerAdapter();
            ConfigurationState.Instance.Logger.EnableAvaloniaLog.Event
                += (_, e) => _avaloniaLogsEnabled = e.NewValue;
        }

        private static RyuLogger.Log? GetLog(AvaLogLevel level, string area)
        {
            if (!_avaloniaLogsEnabled)
                return null;

            return level switch
            {
                AvaLogLevel.Verbose => RyuLogger.Debug,
                AvaLogLevel.Debug => RyuLogger.Debug,
                AvaLogLevel.Information => RyuLogger.Debug,
                AvaLogLevel.Warning => RyuLogger.Debug,
                AvaLogLevel.Error => area is "IME" ? RyuLogger.Debug : RyuLogger.Error,
                AvaLogLevel.Fatal => RyuLogger.Error,
                _ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
            };
        }

        public bool IsEnabled(AvaLogLevel level, string area)
        {
            return GetLog(level, area) != null;
        }

        public void Log(AvaLogLevel level, string area, object source, string messageTemplate)
        {
            GetLog(level, area)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, null));
        }

        public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
        {
            GetLog(level, area)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, propertyValues));
        }

        private static string Format(AvaLogLevel level, string area, string template, object source, object[] v)
        {
            StringBuilder result = new();
            CharacterReader r = new(template.AsSpan());
            int i = 0;

            result.Append('[');
            result.Append(level);
            result.Append("] ");

            result.Append('[');
            result.Append(area);
            result.Append("] ");

            while (!r.End)
            {
                char c = r.Take();

                if (c != '{')
                {
                    result.Append(c);
                }
                else
                {
                    if (r.Peek != '{')
                    {
                        result.Append('\'');
                        result.Append(i < v.Length ? v[i++] : null);
                        result.Append('\'');
                        r.TakeUntil('}');
                        r.Take();
                    }
                    else
                    {
                        result.Append('{');
                        r.Take();
                    }
                }
            }

            if (source != null)
            {
                result.Append(" (");
                result.Append(source.GetType().AsFullNamePrettyString());
                result.Append(" #");
                result.Append(source.GetHashCode());
                result.Append(')');
            }

            return result.ToString();
        }
    }
}
