using System.Numerics;
using GraphicsManager.Enums;
using GraphicsManager.Interfaces;
using GraphicsManager.Objects;
using Luski.Enums;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;

namespace Luski.GUI.MainScreen.UI.LuskiControls;

public class NumberSelector<TNumber> : UserControl where TNumber : INumber<TNumber>
{
    public event Func<NumberSelector<TNumber>, Task>? ValueChanged; 
    
    public int Space
    {
        get
        {
            return space;
        }
        set
        {
            space = value;
            progressBar.Location = new(value, progressBar.Location.Y);
            progressBar.Size = new(base.Size.X - value - value, progressBar.Size.Y);
            //progressBar.SetLocation(value, progressBar.Location.Y);
            //progressBar.SetSize(base.Size.X - value - value, progressBar.Size.Y);
            progressBar.UpdateProgress();
            Min = Min;
        }
    }
    
    public TNumber Min
    {
        get
        {
            return _Min;
        }
        set
        {
            _Min = value;
            L1.Text = value.ToString() + Suffix;
            int x = GetX(value);
            L1.SetLocation(x - (L1.Size.X/2) + space, L1.Location.Y);
            L1.ForceDistanceUpdate(this);
            MinLine.SetLocation(x + space, MinLine.Location.Y);
            Max = Max;
            Value = Value;
        }
    }

    public TNumber Max
    {
        get
        {
            return progressBar.MaxProgressValue + Min;
        }
        set
        {
            progressBar.MaxProgressValue = value - Min;
            L3.Text = value.ToString() + Suffix;
            int x = GetX(value);
            L3.SetLocation(x - (L3.Size.X/2) + space, L3.Location.Y);
            MaxLine.SetLocation(x + space, MaxLine.Location.Y);
            progressBar.UpdateProgress();
        }
    }

    private int GetX(TNumber number)
    {
        return progressBar.GetParentLocation(number - Min, IgnoreEnd: true);
    }
    
    public TNumber Default { get; set; } = TNumber.Zero;
    public TNumber Value
    {
        get
        {
            return progressBar.ProgressValue + Min;
        }
        set
        {
            int i = 0;
            TNumber tmp = value - Min;
            if (progressBar.ProgressValue == tmp) return;
            BlockDraw = true;
            progressBar.ProgressValue = tmp;
            Type tt = typeof(TNumber);
            if (tt == typeof(double))
                L2Cursor.Text = Math.Round((double)(object)value, 2) + Suffix;
            else if (tt == typeof(float))
                L2Cursor.Text = Math.Round((float)(object)value, 2) + Suffix;
            else
                L2Cursor.Text = value.ToString() + Suffix;
            int x = GetX(value);
            L2Cursor.SetLocation(x - (L2Cursor.Size.X/2) + space, L2Cursor.Location.Y);
            Cursor.SetLocation(x - (Cursor.Size.X/2) + space, MaxLine.Location.Y);
            Cursor.ForceDistanceUpdate(this);
            BlockDraw = false;
            progressBar.UpdateProgress();
            if (Loaded && ValueChanged is not null) ValueChanged.Invoke(this);
        }
    }
    public bool LockToPoints { get; set; } = false;
    public string Suffix = "";
    
    
    private ProgressBar<TNumber> progressBar = new();
    private Rectangle Cursor = new(), MinLine = new(), MaxLine = new();
    private List<TNumber> ExtraPoints = new();
    private int space = 5;
    private TNumber _Min = TNumber.Zero, _Default = TNumber.Zero;
    public Label
        L1 = new(Globals.DefaultFont),
        L2Default = new(Globals.DefaultFont),
        L2Cursor = new(Globals.DefaultFont),
        L3 = new(Globals.DefaultFont);

    public NumberSelector()
    {
        base.SetSize(300.ScaleInt());
        progressBar.SetSize(base.Size.X, 8.ScaleInt());
        progressBar.Anchor = ObjectAnchor.Left | ObjectAnchor.Top | ObjectAnchor.Right;
        progressBar.DisallowAboveMax = false;
        progressBar.DrawingGap.X = 1.ScaleInt();
        progressBar.UpdateProgress();
        progressBar.DrawingGap.X = 0;
        progressBar.DrawingGap.Y = progressBar.DrawingGap.X;
        progressBar.DrawingGap.Z = progressBar.DrawingGap.X;
        progressBar.DrawingGap.W = progressBar.DrawingGap.X;
        progressBar.ProgressGap.X = 4.ScaleInt();
        progressBar.ProgressGap.Y = progressBar.ProgressGap.X;
        progressBar.BackgroundColor = Color4.DarkRed;
        progressBar.ProgressColor = Globals.DodgerBlue;
        progressBar.UpdateOnDraw = false;
        progressBar.Anchor = ObjectAnchor.Left | ObjectAnchor.Right;
        progressBar.UpdateProgress();
        progressBar.SetLocation(progressBar.Location.X, base.Size.Y-progressBar.Size.Y-progressBar.Size.Y);
        progressBar.Clicked += ProgressBarOnClicked;
        Cursor.SetSize(10.ScaleInt(),24.ScaleInt());
        Cursor.SetLocation(0, base.Size.Y-Cursor.Size.Y);
        Cursor.BackgroundColor = Color4.White;
        Cursor.Anchor = ObjectAnchor.PreventWidthChange;
        MinLine.SetSize(2.ScaleInt(), Cursor.Size.Y);
        MinLine.SetLocation(0, Cursor.Location.Y);
        MaxLine.SetSize(MinLine.Size.X, Cursor.Size.Y);
        MaxLine.SetLocation(0, Cursor.Location.Y);
        L1.Text = Min.ToString() + Suffix;
        L1.Anchor = ObjectAnchor.Top | ObjectAnchor.Left;
        L2Default.Text = Default.ToString() + Suffix;
        L2Cursor.Text = Value.ToString() + Suffix;
        L3.Text = Max.ToString() + Suffix;
        L3.Anchor = ObjectAnchor.Top | ObjectAnchor.Right;
        MaxLine.Anchor = L3.Anchor;
        Controls.Add(progressBar);
        MaxLine.BackgroundColor = Color4.Cyan;
        MinLine.BackgroundColor = MaxLine.BackgroundColor;
        Controls.Add(MinLine);
        Controls.Add(L1);
        Controls.Add(L3);
        Controls.Add(L2Cursor);
        Controls.Add(MaxLine);
        Controls.Add(Cursor); 
        Cursor.ForceDistanceUpdate(this);
        real_x = Cursor.Location.X;
        //Controls.Add(L2Cursor);
        progressBar.ForceDistanceUpdate(this);
        AllowHoverFromBehind = true;
    }

    private Task ProgressBarOnClicked(IRenderObject arg)
    {
        Value = progressBar.GetValueFromX((int)Window!.MousePosition.X - progressBar.GetWindowLocation().X) + Min;
        return Task.CompletedTask;
    }

    public void AddPoint(TNumber value)
    {
        if (value < _Min || value > Max) return;
        ExtraPoints.Add(value);
    }

    public override void SetSize(int w, int h)
    {
        base.SetSize(w, 50.ScaleInt());
        progressBar.SetSize(w - space - space, 8.ScaleInt());
        progressBar.UpdateProgress();
        progressBar.ForceDistanceUpdate(this);
        Min = Min;
        Value--;
        Value++;
        progressBar.UpdateProgress();
    }

    public override void LoadToParent(IParent p, IWindow w)
    {
        base.LoadToParent(p, w);
        w.MouseUp += WindowOnMouseUp;
        w.MouseMove += WindowOnMouseMove;
        Cursor.Clicked += CursorOnClicked;
    }

    public bool Draging;

    private int real_x, real_dif;

    private Task CursorOnClicked(IRenderObject arg)
    {
        Draging = true;
        real_dif = ((int)Window!.MousePosition.X - Cursor.GetWindowLocation().X);
        real_x = Cursor.Location.X + real_dif;
        return Task.CompletedTask;
    }

    private void WindowOnMouseMove(MouseMoveEventArgs obj)
    {
        if (!Draging) return;
        real_x += (int)obj.DeltaX;
        Value = progressBar.GetValueFromX(real_x) + Min;
    }

    private void WindowOnMouseUp(MouseButtonEventArgs obj)
    {
        if (!Draging) return;
        Draging = false;
    }
}