using GraphicsManager.Enums;
using GraphicsManager.Interfaces;
using GraphicsManager.Objects;
using GraphicsManager.Objects.Core;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;

namespace Luski.GUI.MainScreen.UI.LuskiControls;

public class DropDown<TSelection> : UserControl where TSelection : DropDownOption
{
    public readonly FlowLayout DropDownContainer;

    private IParent ip;

    public required IParent DropDownParentOverride
    {
        get
        {
            return ip;
        }
        set
        {
            ip = value;
            DropDownContainer.TransferOwners(Globals.ms, value);
        }
    }
    private bool IsOpen;
    public event Func<bool, Task>? OpenStatusChanged;

    public DropDown(TSelection defaultOption)
    {
        ip = this;
        DropDownContainer = new()
        {
            Visible = false
        };
        Clicked += OnClicked;
        WindowLoaded += OnWindowLoaded;
        AddOption(defaultOption);
        SelectedOption = defaultOption;
    }
    
    public DropDown(Texture t, TSelection defaultOption)
    :base(t)
    {
        ip = this;
        TextureDisplay = TextureDisplay.HorizontalCenter;
        DropDownContainer = new()
        {
            Visible = false,
            Anchor = ObjectAnchor.Left | ObjectAnchor.Top | ObjectAnchor.Right
        };
        Clicked += OnClicked;
        WindowLoaded += OnWindowLoaded;
        DropDownContainer.Size = new(base.Size.X, DropDownContainer.Size.Y + defaultOption.Size.Y);
        DropDownContainer.Controls.Add(defaultOption);
        defaultOption.Clicked += OptionOnClicked;
        Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context];
        SelectedOption = defaultOption;
    }

    public void SetSelected(TSelection obj)
    {
        SelectedOption = obj;
        Controls.Clear();
        obj.LoadDisplay.Invoke(); 
    }
    
    public DropDown(Texture t, IRenderObject spacer, TSelection defaultOption)
        :base(t)
    {
        ip = this;
        TextureDisplay = TextureDisplay.HorizontalCenter;
        DropDownContainer = new()
        {
            Visible = false,
            Anchor = ObjectAnchor.Left | ObjectAnchor.Top | ObjectAnchor.Right
        };
        Clicked += OnClicked;
        WindowLoaded += OnWindowLoaded;
        DropDownContainer.Size = new(base.Size.X, DropDownContainer.Size.Y + spacer.Size.Y);
        DropDownContainer.Controls.Add(spacer);
        DropDownContainer.Size = new(base.Size.X, DropDownContainer.Size.Y + defaultOption.Size.Y);
        DropDownContainer.Controls.Add(defaultOption);
        defaultOption.Clicked += OptionOnClicked;
        Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context];
        SelectedOption = defaultOption;
    }
    
    public DropDown(Texture t, IRenderObject spacer)
        :base(t)
    {
        ip = this;
        TextureDisplay = TextureDisplay.HorizontalCenter;
        DropDownContainer = new()
        {
            Visible = false,
            Anchor = ObjectAnchor.Left | ObjectAnchor.Top | ObjectAnchor.Right
        };
        Clicked += OnClicked;
        WindowLoaded += OnWindowLoaded;
        DropDownContainer.Size = new(base.Size.X, DropDownContainer.Size.Y + spacer.Size.Y);
        DropDownContainer.Controls.Add(spacer);
        Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context];
    }

    public TSelection SelectedOption { get; private set; }

    private Task OnWindowLoaded(IRenderObject arg)
    {
        SelectedOption.LoadDisplay.Invoke();
        Window!.MouseDown += WindowOnMouseDown;
        return Task.CompletedTask;
    }

    private void WindowOnMouseDown(MouseButtonEventArgs obj)
    {
        if (IsOpen && !MouseInside)
        {
            IsOpen = false;
            DropDownContainer.Visible = IsOpen;
            if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen);
            TryDraw();
        }
    }

    private Task OnClicked(IRenderObject arg)
    {
        BlockDraw = true;
        if (IsOpen)
        {
            IsOpen = false;
            DropDownContainer.Visible = IsOpen;
            if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen);
            BlockDraw = false;
            TryDraw();
            return Task.CompletedTask;
        }
        
        DropDownContainer.Location = this.GetParentLocation(DropDownParentOverride)+ new Vector3i(0, Size.Y, 0);
        DropDownParentOverride.Controls.Add(DropDownContainer);
        DropDownContainer.Size = new(Size.X, DropDownContainer.Size.Y);
        DropDownContainer.ForceDistanceUpdate(DropDownParentOverride);
        IsOpen = true;
        DropDownContainer.Visible = IsOpen;
        DropDownParentOverride.Controls.MoveControlToEnd(DropDownContainer);
        if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen);
        BlockDraw = false;
        TryDraw();
        return Task.CompletedTask;
    }

    public void AddOption(TSelection Option)
    {
        DropDownContainer.Size = new(Size.X, DropDownContainer.Size.Y + Option.Size.Y);
        DropDownContainer.Controls.Add(Option);
        Option.Clicked += OptionOnClicked;
    }

    public event Func<TSelection, Task>? OptionSelected; 

    private Task OptionOnClicked(IRenderObject arg)
    {
        try
        {
            TSelection but =(arg as TSelection)!;
            if (but == SelectedOption)
            {
                IsOpen = false;
                DropDownContainer.Visible = IsOpen;
                if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen);
                TryDraw();
                return Task.CompletedTask;
            }
            SelectedOption = but;
            Controls.Clear();
            SelectedOption.LoadDisplay.Invoke();
            IsOpen = false;
            DropDownContainer.Visible = IsOpen;
            if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen);
            if (OptionSelected is not null) OptionSelected.Invoke(SelectedOption);
            TryDraw();
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
        
        return Task.CompletedTask;
    }
}