using C4IT.FASD.Base;
using C4IT.Logging;
using FasdDesktopUi.Basics.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using static C4IT.Logging.cLogManager;

namespace FasdDesktopUi.Basics.UserControls
{
    public partial class AdjustableParameterSection : UserControl, IFocusInvoker
    {
        #region Variables & Properties

        private double parameterBorderHeight = 0.0;

        public int? ParentIndex { get; private set; }

        public UIElement ParentElement { get; private set; }

        #region Parameters
        public Dictionary<string, cAdjustableParameter> AdjustableParameters
        {
            get { return (Dictionary<string, cAdjustableParameter>)GetValue(AdjustableParametersProperty); }
            set { SetValue(AdjustableParametersProperty, value); }
        }

        public static readonly DependencyProperty AdjustableParametersProperty =
            DependencyProperty.Register("AdjustableParameters", typeof(Dictionary<string, cAdjustableParameter>), typeof(AdjustableParameterSection), new PropertyMetadata(new Dictionary<string, cAdjustableParameter>(), new PropertyChangedCallback(AdjustableParametersChanged)));

        private static void AdjustableParametersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is AdjustableParameterSection _me))
                return;

            _me.UpdateAdjustableParameters();
        }

        #endregion

        public Dictionary<cAdjustableParameter, object> ParameterValues { get; set; } = new Dictionary<cAdjustableParameter, object>();

        #endregion

        public AdjustableParameterSection()
        {
            InitializeComponent();
        }

        private void UpdateIFocusInvokerValues()
        {
            try
            {
                if (Parent is UIElement parentElement)
                {
                    ParentElement = parentElement;

                    if (parentElement is Panel panel)
                        ParentIndex = panel.Children.IndexOf(this);
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void UpdateAdjustableParameters()
        {
            try
            {
                ParameterStack.Children.Clear();
                ParameterValues.Clear();

                ParameterBorder.BeginAnimation(HeightProperty, null);
                ParameterBorder.Height = double.NaN;

                if (AdjustableParameters is null)
                    return;

                foreach (var parameter in AdjustableParameters)
                {
                    try
                    {
                        if (!(ParameterValues.ContainsKey(parameter.Value)))
                            ParameterValues.Add(parameter.Value, null);

                        var parameterValue = parameter.Value;

                        switch (parameterValue)
                        {
                            case cAdjustableParameterBoolean booleanParameter:
                                AddBooleanParameter(parameter.Key, booleanParameter);
                                break;
                            case cAdjustableParameterNumerical numericalParameter:
                                AddNumericalParameter(parameter.Key, numericalParameter);
                                break;
                            case cAdjustableParameterDropDown dropDownParameter:
                                AddDropDownParameter(parameter.Key, dropDownParameter);
                                break;
                            default:
                                break;
                        }

                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }
                }

                ParameterBorder.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                parameterBorderHeight = ParameterBorder.DesiredSize.Height;
                ParameterBorder.Height = parameterBorderHeight;

                UpdateIFocusInvokerValues();
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
            }
        }

        #region Helper

        private void AddBooleanParameter(string parameterKey, cAdjustableParameterBoolean booleanParameter)
        {
            try
            {
                DockPanel outerPanel = new DockPanel() { Margin = new Thickness(2.5) };

                ParameterValues[booleanParameter] = booleanParameter.Default ? $"-{booleanParameter.ParameterName}" : string.Empty;

                CheckBox valueCheckBox = new CheckBox() { IsChecked = booleanParameter.Default };
                valueCheckBox.Checked += new RoutedEventHandler((sender, e) => ParameterValues[booleanParameter] = $"-{booleanParameter.ParameterName}");
                valueCheckBox.Unchecked += new RoutedEventHandler((sender, e) => ParameterValues[booleanParameter] = string.Empty);

                DockPanel.SetDock(valueCheckBox, Dock.Right);
                valueCheckBox.SetResourceReference(StyleProperty, "ToggleSwitch");
                outerPanel.Children.Add(valueCheckBox);

                TextBlock parameterNameTextBlock = new TextBlock() { Text = booleanParameter.Names.GetValue(), Margin = new Thickness(0, 0, 7.5, 0) };
                if(!string.IsNullOrEmpty(booleanParameter.Descriptions.GetValue()))
                {
                    parameterNameTextBlock.ToolTip = booleanParameter.Descriptions.GetValue();
                }
                parameterNameTextBlock.SetResourceReference(ForegroundProperty, "FontColor.DetailsPage.DataHistory.Value");
                outerPanel.Children.Add(parameterNameTextBlock);

                ParameterStack.Children.Add(outerPanel);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void AddNumericalParameter(string parameterKey, cAdjustableParameterNumerical numericalParameter)
        {
            try
            {
                DockPanel outerPanel = new DockPanel() { Margin = new Thickness(2.5) };

                ParameterValues[numericalParameter] = numericalParameter.Default;

                TextBox valueTextBox = new TextBox() { Text = numericalParameter.Default.ToString(), Width = 35, TextAlignment = TextAlignment.Center };
                valueTextBox.SetResourceReference(ForegroundProperty, "FontColor.DetailsPage.DataHistory.Value");
                valueTextBox.SetResourceReference(BackgroundProperty, "BackgroundColor.DetailsPage.DataHistory.TitleColumn");
                valueTextBox.SetResourceReference(BorderBrushProperty, "BackgroundColor.DetailsPage.DataHistory.ValueColumn");

                valueTextBox.PreviewKeyDown += ((sender, e) =>
                {
                    if (double.TryParse(valueTextBox.Text, out var parsedValue))
                        ParameterValues[numericalParameter] = parsedValue;
                });

                valueTextBox.PreviewTextInput += new TextCompositionEventHandler((sender, e) =>
                {
                    var plannedValue = valueTextBox.Text.Insert(valueTextBox.CaretIndex, e.Text);
                    if (double.TryParse(plannedValue, out var parsedValue))
                        ParameterValues[numericalParameter] = parsedValue;
                    else
                        e.Handled = true;
                });

                DockPanel.SetDock(valueTextBox, Dock.Right);
                outerPanel.Children.Add(valueTextBox);

                TextBlock parameterNameTextBlock = new TextBlock() { Text = numericalParameter.Names.GetValue(), Margin = new Thickness(0, 0, 7.5, 0) };
                if (!string.IsNullOrEmpty(numericalParameter.Descriptions.GetValue()))
                {
                    parameterNameTextBlock.ToolTip = numericalParameter.Descriptions.GetValue();
                }
                parameterNameTextBlock.SetResourceReference(ForegroundProperty, "FontColor.DetailsPage.DataHistory.Value");
                outerPanel.Children.Add(parameterNameTextBlock);

                ParameterStack.Children.Add(outerPanel);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void AddDropDownParameter(string parameterKey, cAdjustableParameterDropDown dropDownParameter)
        {
            try
            {
                DockPanel outerPanel = new DockPanel() { Margin = new Thickness(2.5) };

                ParameterValues[dropDownParameter] = dropDownParameter.Default;

                ComboBox valueComboBox = new ComboBox() { SelectedItem = dropDownParameter.Default, MinWidth = 125 };
                valueComboBox.Style = (Style)FindResource("DarkComboBox");

                valueComboBox.DropDownOpened += (sender, e) => cFocusInvoker.InvokeGotFocus(this, e);
                valueComboBox.DropDownClosed += (sender, e) => cFocusInvoker.InvokeLostFocus(this, e);

                foreach (var dropDownValue in dropDownParameter.DropdownValues)
                {
                    try
                    {
                        var tempComboBoxItem = new ComboBoxItem() { Content = dropDownValue.DisplayValues.GetValue(), Tag = dropDownValue.Value };

                        if (dropDownParameter.DropdownValues?.First() == dropDownValue && dropDownParameter.DropdownValues?.Last() == dropDownValue)
                            tempComboBoxItem.SetResourceReference(StyleProperty, "ComboBoxSingleItem");
                        else if (dropDownParameter.DropdownValues?.First() == dropDownValue)
                            tempComboBoxItem.SetResourceReference(StyleProperty, "ComboBoxFirstItem");
                        else if (dropDownParameter.DropdownValues?.Last() == dropDownValue)
                            tempComboBoxItem.SetResourceReference(StyleProperty, "ComboBoxLastItem");

                        valueComboBox.Items.Add(tempComboBoxItem);
                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }
                }

                var selectedIndex = -1;
                var defaultValue = dropDownParameter.DropdownValues.FirstOrDefault(value => value.Value == dropDownParameter.Default);
                if (defaultValue != null)
                    selectedIndex = dropDownParameter.DropdownValues.IndexOf(defaultValue);


                valueComboBox.SelectionChanged += ((sender, e) =>
                {
                    try
                    {
                        if (!(sender is ComboBox senderComboBox))
                            return;

                        if (!(senderComboBox.SelectedItem is FrameworkElement selectedElement))
                            return;

                        ParameterValues[dropDownParameter] = selectedElement.Tag;
                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }
                });

                valueComboBox.SelectedIndex = selectedIndex;
                DockPanel.SetDock(valueComboBox, Dock.Right);
                outerPanel.Children.Add(valueComboBox);

                TextBlock parameterNameTextBlock = new TextBlock() { VerticalAlignment = VerticalAlignment.Center, Text = dropDownParameter.Names.GetValue(), Margin = new Thickness(0, 0, 7.5, 0) };
                if (!string.IsNullOrEmpty(dropDownParameter.Descriptions.GetValue()))
                {
                    parameterNameTextBlock.ToolTip = dropDownParameter.Descriptions.GetValue();
                }
                parameterNameTextBlock.SetResourceReference(ForegroundProperty, "FontColor.DetailsPage.DataHistory.Value");
                outerPanel.Children.Add(parameterNameTextBlock);

                ParameterStack.Children.Add(outerPanel);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        #endregion

        private void ToggleCollapse(bool shouldCollapse)
        {
            try
            {
                const double animatinDurationInSeconds = 0.2;

                if (shouldCollapse)
                    ParameterBorder.BeginAnimation(HeightProperty, new DoubleAnimation(0, TimeSpan.FromSeconds(animatinDurationInSeconds)));
                else
                    ParameterBorder.BeginAnimation(HeightProperty, new DoubleAnimation(parameterBorderHeight, TimeSpan.FromSeconds(animatinDurationInSeconds)));

                CollapseButton.SelectedMaterialIcon = shouldCollapse ? MaterialIcons.MaterialIconType.ic_add : MaterialIcons.MaterialIconType.ic_remove;
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public void ToggleIsActive(bool isActive)
        {
            try
            {
                if (isActive)
                {
                    UpdateAdjustableParameters();
                    foreach (UIElement element in ParameterStack.Children)
                    {
                        element.IsEnabled = true;
                    }
                }
                else
                {
                    ToggleCollapse(true);
                    foreach (UIElement element in ParameterStack.Children)
                    {
                        element.IsEnabled = false;
                    }
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        #region CollapseButton_Click

        private void CollapseButton_Click()
        {
            try
            {
                bool shouldCollapse = ParameterBorder.ActualHeight - ParameterBorder.Padding.Top - ParameterBorder.Padding.Bottom != 0;
                ToggleCollapse(shouldCollapse);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void CollapseButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            CollapseButton_Click();
        }

        private void CollapseButton_TouchDown(object sender, TouchEventArgs e)
        {
            CollapseButton_Click();
        }

        #endregion
    }
}
