using InABox.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace InABox.DynamicGrid;

public interface IOptionControl
{
    public string? GetValue();
    public void SetValue(string value);

    public bool IsEmpty();
}

public class ButtonsOptionControl : Grid, IOptionControl
{
    private Action OnChanged;

    private class OptionButton : Border
    {
        public string Option { get; set; }

        private bool _selected { get; set; }

        public bool Selected
        {
            get => _selected;
            set
            {
                _selected = value;
                if (value == true)
                {
                    Background = new SolidColorBrush(Colors.LightGray);
                }
                else if (value == false)
                {
                    Background = new SolidColorBrush(Colors.DarkGray);
                }
            }
        }

        public OptionButton(string option, bool selected)
        {
            Option = option;
            Selected = selected;
        }
    }

    public ButtonsOptionControl(string[] options, Action onChanged)
    {
        OnChanged = onChanged;

        foreach (var option in options)
        {
            ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            var button = new OptionButton(option, false);

            var label = new Label()
            {
                Content = option.Replace("\r", "").Replace("\n", "")
            };
            label.HorizontalContentAlignment = HorizontalAlignment.Center;
            label.VerticalContentAlignment = VerticalAlignment.Center;
            button.Child = label;

            button.Margin = new Thickness(
                Children.Count == 0 ? 0.0F : 2.5F,
                0.0F,
                Children.Count == options.Length - 1 ? 0.0F : 2.5F,
                0.0F
            );
            button.Padding = new Thickness(5.0F, 0.0F, 5.0F, 0.0F);

            button.MouseUp += Button_MouseUp;

            button.SetValue(ColumnProperty, Children.Count);
            Children.Add(button);
        }
    }

    private void Button_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        SelectButton((OptionButton)sender);
        OnChanged();
    }

    private void SelectButton(OptionButton button)
    {
        foreach (var child in Children)
        {
            if (child is OptionButton childButton && childButton != button)
            {
                childButton.Selected = false;
            }
        }
        button.Selected = true;
    }

    public string? GetValue()
    {
        foreach (var child in Children)
        {
            if (child is OptionButton button)
            {
                if (button.Selected)
                {
                    return button.Option;
                }
            }
        }
        return null;
    }

    public void SetValue(string value)
    {
        foreach (var child in Children)
        {
            if (child is OptionButton button)
            {
                if (button.Option == value)
                {
                    SelectButton(button);
                }
            }
        }
    }

    public bool IsEmpty() => GetValue() == null;
}

public class RadioOptionControl : Grid, IOptionControl
{
    private Action OnChanged;

    public RadioOptionControl(string[] options, Action onChanged)
    {
        Margin = new Thickness(4,2,4,2);
        
        OnChanged = onChanged;

        foreach (var option in options)
        {
            RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) });
            var button = new RadioButton();
            button.Margin = new Thickness(0, 2, 0, 2);
            button.Content = option.Replace("\r", "").Replace("\n", "");
            button.VerticalContentAlignment = VerticalAlignment.Center;
            button.Padding = new Thickness(5.0F, 0.0F, 5.0F, 0.0F);
            button.SetValue(RowProperty, Children.Count);
            button.Tag = option;
            button.Checked += Button_Checked;
            button.Unchecked += Button_Checked;
            Children.Add(button);
        }
    }

    private void Button_Checked(object sender, RoutedEventArgs e)
    {
        OnChanged();
    }

    public string? GetValue()
    {
        foreach (var child in Children)
        {
            if (child is RadioButton radio)
            {
                if (radio.IsChecked == true)
                {
                    return radio.Tag as string;
                }
            }
        }
        return null;
    }

    public void SetValue(string value)
    {
        foreach (var child in Children)
        {
            if (child is RadioButton radio)
            {
                if ((string)radio.Tag == value)
                {
                    radio.IsChecked = true;
                }
            }
        }
    }
    public bool IsEmpty() => GetValue() == null;
}

public class ComboBoxOptionControl : ComboBox, IOptionControl
{
    private readonly Action OnChanged;

    public ComboBoxOptionControl(string[] options, Action onChanged)
    {
        SetResourceReference(StyleProperty, typeof(ComboBox));

        OnChanged = onChanged;

        foreach (var option in options)
            Items.Add(option);
        VerticalContentAlignment = VerticalAlignment.Center;
        SelectionChanged += ComboBoxOptionControl_SelectionChanged;
    }

    private void ComboBoxOptionControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        OnChanged();
    }

    public string? GetValue()
    {
        return SelectedValue as string;
    }

    public void SetValue(string value)
    {
        SelectedValue = value;
    }
    public bool IsEmpty() => GetValue() == null;
}

public class CheckBoxOptionControl : CheckBox, IOptionControl
{
    private readonly Action OnChanged;

    private readonly string TrueValue;
    private readonly string FalseValue;

    public CheckBoxOptionControl(string trueValue, string falseValue, Action onChanged)
    {
        OnChanged = onChanged;
        TrueValue = trueValue;
        FalseValue = falseValue;

        IsThreeState = false;
        HorizontalContentAlignment = HorizontalAlignment.Center;
        VerticalContentAlignment = VerticalAlignment.Center;
        Checked += CheckBoxOptionControl_Checked;
        Unchecked += CheckBoxOptionControl_Checked;
    }

    private void CheckBoxOptionControl_Checked(object sender, RoutedEventArgs e)
    {
        OnChanged();
    }

    public string? GetValue()
    {
        return IsChecked == true
            ? TrueValue
            : FalseValue;
    }

    public void SetValue(string value)
    {
        IsChecked = value == TrueValue;
    }

    public bool IsEmpty() => false;
}

public class DFOptionControl : DynamicFormFieldControl<DFLayoutOptionField, DFLayoutOptionFieldProperties, string, string?>
{
    private IOptionControl OptionControl = null!; // Late-initialised

    protected override FrameworkElement Create()
    {
        var options = string.IsNullOrWhiteSpace(Field.Properties.Options) 
            ? new string[] { } 
            : Field.Properties.Options.Replace(",","\n").Split('\n').Select(x=>x.Trim()).ToArray();

        switch (Field.Properties.OptionType)
        {
            case DFLayoutOptionType.Buttons:
                return SetControl(new ButtonsOptionControl(options, ChangeField));
            case DFLayoutOptionType.Radio:
                return SetControl(new RadioOptionControl(options, ChangeField));
        }
        return SetControl(new ComboBoxOptionControl(options, ChangeField));
    }

    private T SetControl<T>(T value)
        where T : FrameworkElement, IOptionControl
    {
        OptionControl = value;
        return value;
    }

    public override void SetSerializedValue(string? value)
    {
        SetValue(value);
    }

    public override string? GetSerializedValue()
    {
        return OptionControl.GetValue();
    }

    public override string GetValue() => OptionControl.GetValue() ?? "";

    public override void SetValue(string? value) => OptionControl.SetValue(value ?? "");

    protected override bool IsEmpty() => OptionControl.IsEmpty();
}
