using System;
using HSUI.Helpers;
using Dalamud.Bindings.ImGui;
using System.Collections.Generic;
using System.Numerics;
using HSUI.Config;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;

namespace HSUI.Interface.GeneralElements
{
    public class MouseGCDIndicatorHud : DraggableHudElement, IHudElementWithActor, IHudElementWithVisibilityConfig
    {
        private MouseGCDIndicatorConfig Config => (MouseGCDIndicatorConfig)_config;
        public VisibilityConfig VisibilityConfig => Config.VisibilityConfig;

        public IGameObject? Actor { get; set; } = null;

        private float _lastTotalCastTime = 0;

        public MouseGCDIndicatorHud(MouseGCDIndicatorConfig config, string displayName) : base(config, displayName) { }

        protected override (List<Vector2>, List<Vector2>) ChildrenPositionsAndSizes()
        {
            return (new List<Vector2>(), new List<Vector2>());
        }

        protected override void DrawDraggableArea(Vector2 origin)
        {
            return;
        }

        public override void DrawChildren(Vector2 origin)
        {
            if (!Config.Enabled || Actor == null || Actor is not IPlayerCharacter)
            {
                return;
            }

            GCDHelper.GetGCDInfo((IPlayerCharacter)Actor, out var elapsed, out var total);

            if (!Config.AlwaysShow && total == 0)
            {
                _lastTotalCastTime = 0;
                return;
            }

            if (_lastTotalCastTime == 0 && Utils.IsActorCasting(Actor))
            {
                _lastTotalCastTime = ((IBattleChara)Actor).TotalCastTime;
            }

            var scale = elapsed / total;
            if (scale <= 0)
            {
                _lastTotalCastTime = 0;
                return;
            }

            bool instantGCDsOnly = Config.InstantGCDsOnly && _lastTotalCastTime != 0;
            bool thresholdGCDs = Config.LimitGCDThreshold && _lastTotalCastTime > Config.GCDThreshold;

            if (instantGCDsOnly || thresholdGCDs)
            {
                if (Config.AlwaysShow)
                {
                    elapsed = 0;
                    total = 0;
                }
                else
                {
                    return;
                }
            }

            Vector2 center = ImGui.GetMousePos() + Config.CursorCenterOffset;
            AddDrawAction(_config.StrataLevel, () =>
            {
                DrawCircularIndicator(center, Config.CircleRadius, elapsed, total);
            });
        }

        private void DrawCircularIndicator(Vector2 position, float radius, float current, float total)
        {
            total = Config.AlwaysShow && total == 0 ? 1 : total;
            current = Config.AlwaysShow && current == 0 ? total : current;

            var size = new Vector2(radius * 2);
            DrawHelper.DrawInWindow(ID, position - size / 2, size, false, (drawList) =>
            {
                current = Math.Min(current, total);

                const int segments = 100;
                const float queueTime = 0.5f;
                float startAngle = 0f;
                float endAngle = 2f * (float)Math.PI;
                float offset = (float)(-Math.PI / 2f + (Config.CircleStartAngle * (Math.PI / 180f)));

                if (Config.RotateCCW)
                {
                    startAngle *= -1;
                    endAngle *= -1;
                }

                if (Config.AlwaysShow && current == total)
                {
                    drawList.PathArcTo(position, radius, startAngle + offset, endAngle + offset, segments);
                    drawList.PathStroke(Config.FillColor.Base, ImDrawFlags.None, Config.CircleThickness);
                }
                else
                {
                    float progressAngle = Math.Min(current, total - (Config.ShowGCDQueueIndicator ? queueTime : 0f)) / total * endAngle;

                    drawList.PathArcTo(position, radius, startAngle + offset, progressAngle + offset, segments);
                    drawList.PathStroke(Config.FillColor.Base, ImDrawFlags.None, Config.CircleThickness);

                    if (Config.ShowGCDQueueIndicator && current > total - queueTime)
                    {
                        float oldAngle = progressAngle - 0.0003f * total * endAngle;
                        progressAngle = current / total * endAngle;
                        drawList.PathArcTo(position, radius, oldAngle + offset, progressAngle + offset, segments);
                        drawList.PathStroke(Config.QueueColor.Base, ImDrawFlags.None, Config.CircleThickness);
                    }

                    drawList.PathArcTo(position, radius, progressAngle + offset, endAngle + offset, segments);
                    drawList.PathStroke(Config.BackgroundColor.Base, ImDrawFlags.None, Config.CircleThickness);
                }

                if (Config.ShowBorder)
                {
                    drawList.PathArcTo(position, radius - Config.CircleThickness / 2f, 0, endAngle, segments);
                    drawList.PathStroke(0xFF000000, ImDrawFlags.None, 1);

                    drawList.PathArcTo(position, radius + Config.CircleThickness / 2f, 0, endAngle, segments);
                    drawList.PathStroke(0xFF000000, ImDrawFlags.None, 1);
                }
            });
        }
    }
}
