using System.Numerics;
using Voile.Input;
using Voile.Rendering;
using Voile.Resources;

namespace Voile.UI;

public class UISystem : IUpdatableSystem, IRenderableSystem, IReloadableSystem
{
    public IReadOnlyList<IElement> Elements => _elements;

    public bool RenderDebugRects { get; set; }
    public Color DebugSizeRectColor { get; set; } = Color.Red;
    public Color DebugDirtyRectColor { get; set; } = new Color(1.0f, 1.0f, 0.0f, 0.5f);

    public UISystem(InputSystem inputSystem)
    {
        _input = inputSystem;
    }

    public UISystem(InputSystem inputSystem, List<UIElement> elements)
    {
        _input = inputSystem;
        _elements = elements;
    }

    public void SetStyleSheet(ResourceRef<StyleSheet> styleSheet)
    {
        _styleSheet = styleSheet;
    }

    public void AddElement(UIElement element)
    {
        element.StyleSheetOverride = _styleSheet;
        _elements.Add(element);
        _inputElementIndices.Add(element.GlobalPosition, _elements.Count - 1);
    }
    public void RemoveElement(UIElement element) => _elements.Remove(element);

    public void Update(double deltaTime)
    {
        // HandleInput();
    }

    public void Render(RenderSystem renderer)
    {
        // Update elements each time UI system is rendered.
        HandleInput();

        foreach (var element in _elements)
        {
            if (element is not IUpdatableElement updatable) continue;
            updatable.Update();
        }


        foreach (var element in _elements)
        {
            if (element is IRenderableElement renderable)
            {
                var styleSheet = _styleSheet.Value;

                if (!styleSheet.TryGet(element.StyleName, out var style))
                {
                    style = new Style();
                }

                renderable.Render(renderer, style);
            }
        }

        if (!RenderDebugRects) return;

        foreach (var element in _elements)
        {
            if (element is not UIElement uiElement) continue;
            DrawDebugForElement(renderer, uiElement);
        }
    }

    public void Reload()
    {
        throw new NotImplementedException();
    }

    private void DrawDebugForElement(RenderSystem renderer, UIElement element)
    {
        var size = new Vector2(element.Size.Width, element.Size.Height);
        renderer.SetTransform(element.GlobalPosition, Vector2.Zero);
        renderer.DrawRectangleOutline(size, DebugSizeRectColor);

        if (element.Dirty)
        {
            renderer.DrawRectangle(size, DebugDirtyRectColor);
        }

        if (element is IParentableElement parentableElement)
        {
            foreach (var child in parentableElement.Children)
            {
                if (child is not UIElement childElement) continue;
                DrawDebugForElement(renderer, childElement);
            }
        }
    }

    private void HandleInput()
    {
        int charPressed = _input.GetCharPressed();
        Vector2 mousePos = _input.GetMousePosition();

        Vector2 currentMousePosition = _input.GetMousePosition();

        foreach (var (actionName, mappings) in InputSystem.InputMappings)
        {
            foreach (var action in mappings)
            {
                if (action.IsPressed(_input))
                {
                    // TODO: specify which mouse button is used in the context.
                    var context = new UIInputContext(action, mousePos, actionName, charPressed)
                    {
                        MouseDown = _input.IsMouseButtonDown(MouseButton.Left),
                        MouseReleased = _input.IsMouseButtonReleased(MouseButton.Left),
                        MousePressed = _input.IsMouseButtonReleased(MouseButton.Left),
                    };

                    if (PropagateInput(_elements, context))
                        return;
                }
            }
        }

        if (charPressed != 0)
        {
            var context = new UIInputContext(new KeyInputAction(KeyboardKey.Null), mousePos, "", charPressed);
            PropagateInput(_elements, context);
        }


        if (currentMousePosition != _lastMousePosition)
        {
            // TODO: specify which mouse button is used in the context.
            var context = new UIInputContext(new MouseInputAction(MouseButton.Left), mousePos, "", charPressed)
            {
                MouseDown = _input.IsMouseButtonDown(MouseButton.Left),
                MouseReleased = _input.IsMouseButtonReleased(MouseButton.Left),
                MousePressed = _input.IsMouseButtonPressed(MouseButton.Left),
            };
            PropagateInput(_elements, context);
        }
    }

    private bool PropagateInput(List<UIElement> elements, UIInputContext context)
    {
        for (int i = elements.Count - 1; i >= 0; i--)
        {
            var element = elements[i];

            // if (!element.ContainsPoint(context.MousePosition)) continue;

            if (element is IInputElement inputElement && !inputElement.IgnoreInput)
            {
                inputElement.Input(context);
                _input.SetAsHandled();
                // return true;
            }

            if (element is IParentableElement parent)
            {
                if (PropagateInput(parent.Children.ToList(), context))
                    return true;
            }
        }

        return false;
    }

    private ResourceRef<StyleSheet> _styleSheet;
    private List<UIElement> _elements = new();
    private InputSystem _input;

    private GridSet<int> _inputElementIndices = new();

    private Vector2 _lastMousePosition = Vector2.Zero;
}