using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace InABox.Avalonia.Components;

public partial class ButtonStrip : TemplatedControl
{
    public static StyledProperty<IBrush?> SelectedBackgroundProperty = AvaloniaProperty.Register<ButtonStripItem, IBrush?>(
        nameof(SelectedBackground));
    public static StyledProperty<IBrush?> SelectedForegroundProperty = AvaloniaProperty.Register<ButtonStripItem, IBrush?>(
        nameof(SelectedForeground));
    public static StyledProperty<double> ItemSpacingProperty = AvaloniaProperty.Register<ButtonStripItem, double>(
        nameof(ItemSpacing));
    public static StyledProperty<ObservableCollection<ButtonStripItem>> ItemsProperty =
        AvaloniaProperty.Register<ButtonStripItem, ObservableCollection<ButtonStripItem>>(nameof(Items));
    public static StyledProperty<IEnumerable?> ItemsSourceProperty =
        AvaloniaProperty.Register<ButtonStripItem, IEnumerable?>(nameof(ItemsSource));
    public static StyledProperty<ICommand> SelectedCommandProperty = AvaloniaProperty.Register<ButtonStrip, ICommand>(nameof(SelectedCommand));
    public static StyledProperty<object?> SelectedItemProperty = AvaloniaProperty.Register<ButtonStrip, object?>(nameof(SelectedItem));

    public IBrush? SelectedBackground
    {
        get => GetValue(SelectedBackgroundProperty);
        set => SetValue(SelectedBackgroundProperty, value);
    }
    public IBrush? SelectedForeground
    {
        get => GetValue(SelectedForegroundProperty);
        set => SetValue(SelectedForegroundProperty, value);
    }
    public double ItemSpacing
    {
        get => GetValue(ItemSpacingProperty);
        set => SetValue(ItemSpacingProperty, value);
    }
    public ICommand SelectedCommand
    {
        get => GetValue(SelectedCommandProperty);
        set => SetValue(SelectedCommandProperty, value);
    }

    public IEnumerable? ItemsSource
    {
        get => GetValue(ItemsSourceProperty);
        set => SetValue(ItemsSourceProperty, value);
    }

    [Content]
    public ObservableCollection<ButtonStripItem> Items
    {
        get => GetValue(ItemsProperty);
        set => SetValue(ItemsProperty, value);
    }

    private ButtonStripItem? SelectedButton
    {
        get => Items.FirstOrDefault(x => x.Selected);
        set
        {
            foreach(var item in Items)
            {
                item.Selected = item == value;
            }
            SelectedItem = value?.Tag;
        }
    }

    public object? SelectedItem
    {
        get => GetValue(SelectedItemProperty);
        set => SetValue(SelectedItemProperty, value);
    }

    public event EventHandler? SelectionChanged;

    static ButtonStrip()
    {
        ItemsProperty.Changed.AddClassHandler<ButtonStrip>(Items_Changed);
        ItemsSourceProperty.Changed.AddClassHandler<ButtonStrip>(ItemsSource_Changed);
    }

    private static void ItemsSource_Changed(ButtonStrip strip, AvaloniaPropertyChangedEventArgs args)
    {
        if(strip.ItemsSource is INotifyCollectionChanged notify)
        {
            notify.CollectionChanged += (sender, e) => strip.RebuildItems();
        }
        strip.RebuildItems();
    }

    private void RebuildItems()
    {
        Items.Clear();
        if(ItemsSource is not null)
        {
            foreach(var item in ItemsSource)
            {
                if(item is ButtonStripItem btn)
                {
                    Items.Add(btn);
                }
                else
                {
                    Items.Add(new(item?.ToString() ?? "") { Tag = item });
                }
            }
        }
    }

    private static void Items_Changed(ButtonStrip strip, AvaloniaPropertyChangedEventArgs args)
    {
        strip.LogicalChildren.Clear();
        if (strip.Items is not null)
        {
            strip.SelectedButton = strip.Items.FirstOrDefault();

            strip.LogicalChildren.AddRange(strip.Items);
            foreach(var item in strip.Items)
            {
                item.Command = strip.ItemSelectedCommand;
            }
        }
    }

    public ButtonStrip()
    {
        Items = new();
        Items.CollectionChanged += Items_CollectionChanged;
    }

    private void Items_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        SelectedButton ??= Items.FirstOrDefault();
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                AddControlItemsToLogicalChildren(e.NewItems);
                break;
            case NotifyCollectionChangedAction.Remove:
                RemoveControlItemsFromLogicalChildren(e.OldItems);
                break;
        }
        foreach(var item in Items)
        {
            item.Command = ItemSelectedCommand;
        }
    }

    private void AddControlItemsToLogicalChildren(IEnumerable? items)
    {
        if (items is null) return;

        List<ILogical>? toAdd = null;
        foreach(var i in items)
        {
            if(i is Control control && !LogicalChildren.Contains(control))
            {
                toAdd ??= new();
                toAdd.Add(control);
            }
        }
        if(toAdd is not null)
        {
            LogicalChildren.AddRange(toAdd);
        }
    }
    private void RemoveControlItemsFromLogicalChildren(IEnumerable? items)
    {
        if (items is null) return;

        List<ILogical>? toRemove = null;
        foreach(var i in items)
        {
            if(i is Control control)
            {
                toRemove ??= new();
                toRemove.Add(control);
            }
        }
        if(toRemove is not null)
        {
            LogicalChildren.RemoveAll(toRemove);
        }
    }

    [RelayCommand]
    private void ItemSelected(ButtonStripItem item)
    {
        var children = this.GetLogicalChildren().ToArray();
        SelectedButton = item;
        SelectionChanged?.Invoke(this, new());
        SelectedCommand?.Execute(item.Tag);
    }

    public void AddRange(IEnumerable<string> items)
    {
        foreach(var item in items)
        {
            Items.Add(new(item));
        }
    }
}

[PseudoClasses(":selected")]
public class ButtonStripItem : TemplatedControl
{
    public static StyledProperty<string> TextProperty = AvaloniaProperty.Register<ButtonStripItem, string>(nameof(Text));
    public static StyledProperty<bool> SelectedProperty = AvaloniaProperty.Register<ButtonStripItem, bool>(nameof(Selected));
    public static StyledProperty<int> IndexProperty = AvaloniaProperty.Register<ButtonStripItem, int>(nameof(Index));
    public static StyledProperty<ICommand> CommandProperty = AvaloniaProperty.Register<ButtonStripItem, ICommand>(nameof(Command));

    public string Text
    {
        get => GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }
    public bool Selected
    {
        get => GetValue(SelectedProperty);
        set => SetValue(SelectedProperty, value);
    }
    public int Index
    {
        get => GetValue(IndexProperty);
        set => SetValue(IndexProperty, value);
    }
    public ICommand Command
    {
        get => GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }

    public ButtonStripItem()
    {
    }

    public ButtonStripItem(string text)
    {
        Text = text;
    }

    static ButtonStripItem()
    {
        SelectedProperty.Changed.AddClassHandler<ButtonStripItem>(Selected_Changed);
    }

    private static void Selected_Changed(ButtonStripItem item, AvaloniaPropertyChangedEventArgs args)
    {
        item.PseudoClasses.Set(":selected", item.Selected);
    }
}
