using HSUI.Config;
using HSUI.Enums;
using HSUI.Interface;
using HSUI.Interface.GeneralElements;
using HSUI.Interface.Jobs;
using HSUI.Interface.StatusEffects;
using Dalamud.Bindings.ImGui;
using System;
using System.Collections.Generic;
using System.Numerics;

namespace HSUI.Helpers
{
    public static class DraggablesHelper
    {
        public static void DrawGrid(GridConfig config, HUDOptionsConfig? hudConfig, DraggableHudElement? selectedElement)
        {
            ImGui.SetNextWindowPos(Vector2.Zero);
            ImGui.SetNextWindowSize(ImGui.GetMainViewport().Size);

            ImGui.SetNextWindowBgAlpha(config.BackgroundAlpha);

            ImGui.Begin("DelvUI_grid",
                ImGuiWindowFlags.NoTitleBar
              | ImGuiWindowFlags.NoScrollbar
              | ImGuiWindowFlags.AlwaysAutoResize
              | ImGuiWindowFlags.NoInputs
              | ImGuiWindowFlags.NoDecoration
              | ImGuiWindowFlags.NoBringToFrontOnFocus
              | ImGuiWindowFlags.NoFocusOnAppearing
            );

            ImDrawListPtr drawList = ImGui.GetWindowDrawList();
            Vector2 screenSize = ImGui.GetMainViewport().Size;
            Vector2 offset = hudConfig != null && hudConfig.UseGlobalHudShift ? hudConfig.HudOffset : Vector2.Zero;
            Vector2 center = screenSize / 2f + offset;

            // grid
            if (config.ShowGrid)
            {
                int count = (int)(Math.Max(screenSize.X, screenSize.Y) / config.GridDivisionsDistance) / 2 + 1;

                for (int i = 0; i < count; i++)
                {
                    var step = i * config.GridDivisionsDistance;

                    drawList.AddLine(new Vector2(center.X + step, 0), new Vector2(center.X + step, screenSize.Y), 0x88888888);
                    drawList.AddLine(new Vector2(center.X - step, 0), new Vector2(center.X - step, screenSize.Y), 0x88888888);

                    drawList.AddLine(new Vector2(0, center.Y + step), new Vector2(screenSize.X, center.Y + step), 0x88888888);
                    drawList.AddLine(new Vector2(0, center.Y - step), new Vector2(screenSize.X, center.Y - step), 0x88888888);

                    if (config.GridSubdivisionCount > 1)
                    {
                        for (int j = 1; j < config.GridSubdivisionCount; j++)
                        {
                            var subStep = j * (config.GridDivisionsDistance / config.GridSubdivisionCount);

                            drawList.AddLine(new Vector2(center.X + step + subStep, 0), new Vector2(center.X + step + subStep, screenSize.Y), 0x44888888);
                            drawList.AddLine(new Vector2(center.X - step - subStep, 0), new Vector2(center.X - step - subStep, screenSize.Y), 0x44888888);

                            drawList.AddLine(new Vector2(0, center.Y + step + subStep), new Vector2(screenSize.X, center.Y + step + subStep), 0x44888888);
                            drawList.AddLine(new Vector2(0, center.Y - step - subStep), new Vector2(screenSize.X, center.Y - step - subStep), 0x44888888);
                        }
                    }
                }
            }

            // center lines
            if (config.ShowCenterLines)
            {
                drawList.AddLine(new Vector2(center.X, 0), new Vector2(center.X, screenSize.Y), 0xAAFFFFFF);
                drawList.AddLine(new Vector2(0, center.Y), new Vector2(screenSize.X, center.Y), 0xAAFFFFFF);
            }

            if (config.ShowAnchorPoints && selectedElement != null)
            {
                Vector2 parentAnchorPos = center + selectedElement.ParentPos();
                Vector2 anchorPos = parentAnchorPos + selectedElement.GetConfig().Position;

                drawList.AddLine(parentAnchorPos, anchorPos, 0xAA0000FF, 2);

                var anchorSize = new Vector2(10, 10);
                drawList.AddRectFilled(anchorPos - anchorSize / 2f, anchorPos + anchorSize / 2f, 0xAA0000FF);
            }

            ImGui.End();
        }

        public static void DrawElements(
            Vector2 origin,
            HudHelper hudHelper,
            IList<DraggableHudElement> elements,
            JobHud? jobHud,
            DraggableHudElement? selectedElement)
        {
            foreach (DraggableHudElement element in elements)
            {
                if (!hudHelper.IsElementHidden(element))
                {
                    element.PrepareForDraw(origin);
                }
            }

            jobHud?.PrepareForDraw(origin);

            bool clip = ConfigurationManager.Instance?.LockHUD == true &&
                ClipRectsHelper.Instance?.Enabled == true &&
                ClipRectsHelper.Instance?.Mode == WindowClippingMode.Performance;

            bool needsDraw = true;

            if (clip)
            {
                ClipRect? clipRect = ClipRectsHelper.Instance?.GetClipRectForArea(Vector2.Zero, ImGui.GetMainViewport().Size);
                if (clipRect.HasValue)
                {
                    needsDraw = false;

                    ClipRect[] invertedClipRects = ClipRectsHelper.GetInvertedClipRects(clipRect.Value);
                    for (int i = 0; i < invertedClipRects.Length; i++)
                    {
                        ImGui.PushClipRect(invertedClipRects[i].Min, invertedClipRects[i].Max, false);
                        Draw(origin, hudHelper, elements, jobHud, selectedElement);
                        ImGui.PopClipRect();
                    }
                }
            }

            if (needsDraw)
            {
                Draw(origin, hudHelper, elements, jobHud, selectedElement);
            }
        }

        private static void Draw(
            Vector2 origin,
            HudHelper hudHelper,
            IList<DraggableHudElement> elements,
            JobHud? jobHud,
            DraggableHudElement? selectedElement)
        {
            bool canTakeInput = true;
            bool jobHudNeedsDraw = jobHud != null && jobHud != selectedElement && !hudHelper.IsElementHidden(jobHud);

            // selected
            if (selectedElement != null)
            {
                if (!hudHelper.IsElementHidden(selectedElement))
                {
                    selectedElement.CanTakeInputForDrag = true;
                    selectedElement.Draw(origin);
                    canTakeInput = !selectedElement.NeedsInputForDrag;
                }
                else if (selectedElement is IHudElementWithMouseOver elementWithMouseOver)
                {
                    elementWithMouseOver.StopMouseover();
                }
            }

            // all
            foreach (DraggableHudElement element in elements)
            {
                if (element == selectedElement) { continue; }

                if (jobHudNeedsDraw && jobHud != null && element.GetConfig().StrataLevel > jobHud.GetConfig().StrataLevel)
                {
                    jobHud.CanTakeInputForDrag = canTakeInput;
                    jobHud.Draw(origin);
                    jobHudNeedsDraw = false;
                }

                if (!hudHelper.IsElementHidden(element))
                {
                    element.CanTakeInputForDrag = canTakeInput;
                    element.Draw(origin);
                    canTakeInput = !canTakeInput ? false : !element.NeedsInputForDrag;
                }
                else if (element is IHudElementWithMouseOver elementWithMouseOver)
                {
                    elementWithMouseOver.StopMouseover();
                }
            }
        }

        public static bool DrawArrows(Vector2 position, Vector2 size, string tooltipText, out Vector2 offset)
        {
            offset = Vector2.Zero;

            var windowFlags = ImGuiWindowFlags.NoScrollbar
                | ImGuiWindowFlags.NoTitleBar
                | ImGuiWindowFlags.NoResize
                | ImGuiWindowFlags.NoBackground
                | ImGuiWindowFlags.NoDecoration
                | ImGuiWindowFlags.NoSavedSettings;

            var margin = new Vector2(4, 0);
            var windowSize = ArrowSize + margin * 2;

            // left, right, up, down
            var positions = GetArrowPositions(position, size);
            var offsets = new Vector2[]
            {
                new Vector2(-1, 0),
                new Vector2(1, 0),
                new Vector2(0, -1),
                new Vector2(0, 1)
            };

            for (int i = 0; i < 4; i++)
            {
                var pos = positions[i] - margin;

                ImGui.SetNextWindowSize(windowSize, ImGuiCond.Always);
                ImGui.SetNextWindowPos(pos);

                ImGui.Begin("DelvUI_draggablesArrow " + i.ToString(), windowFlags);

                // fake button
                ImGuiP.ArrowButtonEx($"arrow button {i}", (ImGuiDir)i, new Vector2(ArrowSize.X, ArrowSize.Y));
                if (ImGui.IsMouseHoveringRect(pos, pos + windowSize))
                {
                    // track click manually to not deal with window focus stuff
                    if (ImGui.IsMouseClicked(ImGuiMouseButton.Left))
                    {
                        offset = offsets[i];
                    }

                    // tooltip
                    TooltipsHelper.Instance.ShowTooltipOnCursor(tooltipText);
                }

                ImGui.End();
            }

            return offset != Vector2.Zero;
        }

        public static Vector2 ArrowSize = new Vector2(40, 40);

        public static Vector2[] GetArrowPositions(Vector2 position, Vector2 size)
        {
            return GetArrowPositions(position, size, ArrowSize);
        }

        public static Vector2[] GetArrowPositions(Vector2 position, Vector2 size, Vector2 arrowSize)
        {
            return new Vector2[]
            {
                new Vector2(position.X - arrowSize.X + 10, position.Y + size.Y / 2f - arrowSize.Y / 2f - 2),
                new Vector2(position.X + size.X - 8, position.Y + size.Y / 2f - arrowSize.Y / 2f - 2),
                new Vector2(position.X + size.X / 2f - arrowSize.X / 2f + 2, position.Y - arrowSize.Y + 1),
                new Vector2(position.X + size.X / 2f - arrowSize.X / 2f + 2, position.Y + size.Y - 7)
            };
        }
    }
}
