//<auto-generated/>
using Microsoft.Extensions.Logging;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Windows.Foundation;
using Windows.Foundation.Diagnostics;
using Windows.UI;

namespace Danmaku.Core
{
    public class DanmakuFrostMaster
    {
        public const uint DefaultDanmakuLayerCount = DanmakuDefaultLayerDef.DefaultLayerCount;

        private readonly DanmakuRender _render;
        private readonly Queue<uint> _updateTimeQueue = new Queue<uint>();
        private readonly AutoResetEvent _updateEvent = new AutoResetEvent(false);
        private List<DanmakuItem> _danmakuList = new List<DanmakuItem>();
        private volatile bool _isSeeking;
        private volatile bool _isClosing;
        private volatile int _lastIndex;
        private volatile uint _lastTimeMs;

        public bool DebugMode
        {
            get => _render.DebugMode;
            set => _render.DebugMode = value;
        }

        /// <summary>
        /// Must be called in UI thread
        /// </summary>
        public DanmakuFrostMaster(Grid rootGrid, ILogger<DanmakuFrostMaster> logger = default)
        {
            rootGrid?.Children.Clear();
            if (rootGrid.XamlRoot is null)
            {
                return;
            }

            var canvas = new CanvasAnimatedControl();
            canvas.UseSharedDevice = true;
            canvas.DpiScale = (float)rootGrid.XamlRoot.RasterizationScale;
            canvas.ClearColor = Colors.Transparent;
            canvas.Background = new SolidColorBrush(Colors.Transparent);
            rootGrid.Children.Add(canvas);
            Logger.SetLogger(logger);

            _render = new DanmakuRender(canvas, rootGrid);

            Windows.System.Threading.ThreadPool.RunAsync(Updater_DoWork).AsTask();

            Logger.Log("DanmakuFrostMaster is created");
        }

        public void SetAutoControlDensity(bool value)
        {
            _render?.SetAutoControlDensity(value);
        }

        /// <summary>
        /// Default: -1
        /// 1->10 2->20 3->30 4-50 -1->unlimited
        /// </summary>
        public void SetRollingDensity(int value)
        {
            _render?.SetRollingDensity(value);
        }

        /// <summary>
        /// Default: 9
        /// </summary>
        /// <param name="value">in (0,10]</param>
        public void SetRollingAreaRatio(int value)
        {
            _render?.SetRollingAreaRatio(value);
        }

        /// <summary>
        /// Default: 5
        /// speed = value * 0.02
        /// </summary>
        /// <param name="value">in [1,10]</param>
        public void SetRollingSpeed(double value)
        {
            _render?.SetRollingSpeed(value);
        }

        /// <param name="value">in [0,1]</param>
        public void SetOpacity(double value)
        {
            _render?.SetOpacity(value);
        }

        public void SetIsTextBold(bool value)
        {
            _render?.SetIsTextBold(value);
        }

        public void SetDanmakuFontSizeOffset(DanmakuFontSize value)
        {
            _render?.SetDanmakuFontSizeOffset(value);
        }

        /// <summary>
        /// Null or empty to use system default
        /// </summary>
        public void SetFontFamilyName(string value)
        {
            _render?.SetDefaultFontFamilyName(value);
        }

        public void SetBorderColor(Color borderColor)
        {
            _render?.SetBorderColor(borderColor);
        }

        public void SetNoOverlapSubtitle(bool value)
        {
            _render?.SetNoOverlapSubtitle(value);
        }

        public void UpdateTime(uint currentMs)
        {
            if (_render is null)
            {
                return;
            }

            lock (_updateTimeQueue)
            {
                _updateTimeQueue.Enqueue(currentMs);
            }
            _updateEvent.Set();
        }

        public void Pause()
        {
            if (!_isClosing && _render is not null)
            {
                _render.Pause();
            }
        }

        public void Resume()
        {
            _render?.Start();
        }

        public void Stop()
        {
            Pause();
            _render?.Stop();
            lock (_updateTimeQueue)
            {
                _updateTimeQueue.Clear();
            }
        }

        public void Restart()
        {
            Seek(0);
        }

        public void SetLayerRenderState(uint layerId, bool render)
        {
            _render?.SetLayerRenderState(layerId, render);
        }

        public void Seek(uint targetMs)
        {
            if (_render is null)
            {
                return;
            }

            _isSeeking = true;
            Stop();
            lock (_danmakuList)
            {
                _lastIndex = 0;
                if (_danmakuList.Count > 0)
                {
                    while (_danmakuList[_lastIndex].StartMs < targetMs)
                    {
                        _lastIndex++;
                        if (_lastIndex >= _danmakuList.Count)
                        {
                            break;
                        }
                    }
                }
                _lastTimeMs = targetMs;
                Resume();
                _isSeeking = false;
            }
        }

        public void Clear()
        {
            _danmakuList?.Clear();
            _render?.Stop();
        }

        public void Close()
        {
            if (!_isClosing)
            {
                _isClosing = true;
                _updateEvent?.Set();
                _render?.Close();

                Logger.Log("DanmakuFrostMaster is closed");
            }
        }

        public void AddRealtimeDanmaku(DanmakuItem item, bool insertToList, uint layerId = DanmakuDefaultLayerDef.DefaultLayerId)
        {
            if (_render is null)
            {
                return;
            }

            item.AllowDensityControl = false;
            item.IsRealtime = true;
            _render.RenderDanmakuItem(layerId, item);
            if (insertToList)
            {
                lock (_danmakuList)
                {
                    bool added = false;
                    for (int i = 0; i < _danmakuList.Count; i++)
                    {
                        if (_danmakuList[i].StartMs > item.StartMs)
                        {
                            _danmakuList.Insert(i, item);
                            added = true;
                            break;
                        }
                    }
                    if (!added)
                    {
                        // No danmaku in current list
                        _danmakuList.Add(item);
                    }
                }
            }
        }

        public List<DanmakuItem> GetDanmakuList()
            => _danmakuList;

        /// <param name="danmakuList">Must be pre-ordered by StartMs</param>
        public void SetDanmakuList(List<DanmakuItem> danmakuList)
        {
            Clear();
            _danmakuList = danmakuList ?? new List<DanmakuItem>();
        }

        public void AddDanmakuList(List<DanmakuItem> danmakuList)
        {
            if (_danmakuList == null)
            {
                _danmakuList = danmakuList;
            }
            else
            {
                var items = danmakuList.Where(p => !_danmakuList.Any(v => v.Id == p.Id));
                _danmakuList.AddRange(items);
            }
        }

        private void Updater_DoWork(IAsyncAction action)
        {
            if (_render is null)
            {
                return;
            }

            try
            {
                while (!_isClosing)
                {
                    _updateEvent.WaitOne();

                    uint currentTimeMs = 0;
                    lock (_updateTimeQueue)
                    {
                        if (_updateTimeQueue.Count > 0)
                        {
                            currentTimeMs = _updateTimeQueue.Dequeue();
                        }
                    }
                    if (currentTimeMs == 0)
                    {
                        continue;
                    }

                    lock (_danmakuList)
                    {
                        // Check if app/thread has been suspended for a while or danmaku engine has been restarted
                        if (currentTimeMs < _lastTimeMs || currentTimeMs - _lastTimeMs > 5000)
                        {
                            Logger.Log("Reseek after a long time suspension");
                            Seek(currentTimeMs);
                        }
                        else
                        {
                            _lastTimeMs = currentTimeMs;
                        }

                        while (currentTimeMs > 0 && _lastIndex < _danmakuList.Count && _danmakuList[_lastIndex].StartMs <= currentTimeMs)
                        {
                            if (_isClosing)
                            {
                                return;
                            }
                            if (_isSeeking)
                            {
                                break;
                            }

                            bool skip = false;

                            if (_danmakuList[_lastIndex].IsRealtime)
                            {
                                _danmakuList[_lastIndex].IsRealtime = false;
                                skip = true;
                            }

                            if (!skip && !_isClosing)
                            {
                                uint layerId = _danmakuList[_lastIndex].Mode switch
                                {
                                    DanmakuMode.Bottom => DanmakuDefaultLayerDef.BottomLayerId,
                                    DanmakuMode.Top => DanmakuDefaultLayerDef.TopLayerId,
                                    DanmakuMode.ReverseRolling => DanmakuDefaultLayerDef.ReverseRollingLayerId,
                                    DanmakuMode.Advanced => DanmakuDefaultLayerDef.AdvancedLayerId,
                                    _ => DanmakuDefaultLayerDef.RollingLayerId,
                                };
                                _render.RenderDanmakuItem(layerId, _danmakuList[_lastIndex]);
                            }

                            _lastIndex++;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Log(ex.Message);
            }
            finally
            {
                Logger.Log("Exited");
            }
        }
    }
}
