using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using InABox.Core;
using Button = System.Windows.Controls.Button;
using Color = System.Windows.Media.Color;
using ComboBox = System.Windows.Controls.ComboBox;
using HorizontalAlignment = System.Windows.HorizontalAlignment;
using Label = System.Windows.Controls.Label;
using SystemColors = System.Windows.SystemColors;
using TextBox = System.Windows.Controls.TextBox;
using UserControl = System.Windows.Controls.UserControl;

namespace InABox.WPF
{
    public class QAGrid : UserControl
    {
        public delegate void QAFormChangedEvent(object sender, Dictionary<Guid, object> values);

        private readonly Dictionary<QAQuestion, Border> borders = new();

        private readonly Grid MasterGrid;

        public QAGrid()
        {
            BorderBrush = new SolidColorBrush(Colors.Gray);
            BorderThickness = new Thickness(0.75);

            MasterGrid = new Grid();

            MasterGrid.Background = new SolidColorBrush(Colors.WhiteSmoke);

            MasterGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.25F, GridUnitType.Star) });
            MasterGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.25F, GridUnitType.Star) });

            MasterGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Auto) });
            MasterGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) });
            MasterGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Auto) });

            Content = MasterGrid;
        }

        public bool Preview { get; set; }

        public QAFormChangedEvent OnChanged { get; set; }

        private object GetValue(Dictionary<Guid, object> values, Guid guid)
        {
            if (values.ContainsKey(guid))
                return values[guid];
            return "";
        }

        private Border CreateHeader(string title)
        {
            var label = new Label
            {
                Content = title,
                FontSize = 16.0F,
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center
            };

            var border = new Border
            {
                CornerRadius = new CornerRadius(10.0F, 10.0F, 0.0F, 0.0F),
                BorderThickness = new Thickness(1.0F),
                BorderBrush = SystemColors.WindowFrameBrush,
                Background = SystemColors.WindowBrush,
                Margin = new Thickness(5.0F, 5.0F, 5.0F, 2.0F),
                Padding = new Thickness(5.0F)
            };

            border.SetValue(Grid.ColumnProperty, MasterGrid.ColumnDefinitions.Count - 1);
            border.SetValue(Grid.RowProperty, 0);
            border.Child = label;

            MasterGrid.Children.Add(border);
            return border;
        }

        private Thickness GetMargin(Grid grid)
        {
            if (grid.RowDefinitions.Count == 0)
                return new Thickness(2.0F, 0.0F, 2.0F, 0.0F);
            return new Thickness(4.0F, 3.0F, 4.0F, 3.0F);
        }

        public static Color InterpolateColors(Color color1, Color color2, float percentage)
        {
            double a1 = color1.A / 255.0, r1 = color1.R / 255.0, g1 = color1.G / 255.0, b1 = color1.B / 255.0;
            double a2 = color2.A / 255.0, r2 = color2.R / 255.0, g2 = color2.G / 255.0, b2 = color2.B / 255.0;

            var a3 = Convert.ToByte((a1 + (a2 - a1) * percentage) * 255);
            var r3 = Convert.ToByte((r1 + (r2 - r1) * percentage) * 255);
            var g3 = Convert.ToByte((g1 + (g2 - g1) * percentage) * 255);
            var b3 = Convert.ToByte((b1 + (b2 - b1) * percentage) * 255);
            return Color.FromArgb(a3, r3, g3, b3);
        }

        private Color GetBorder(QAQuestion question, Dictionary<Guid, object> answers)
        {
            var parameters = question.ParseParameters();
            var bAnswerRequired = question.Answer != QAAnswer.Comment &&
                                  (!parameters.ContainsKey("Default") || string.IsNullOrWhiteSpace(parameters["Default"]));
            if (bAnswerRequired && (!answers.ContainsKey(question.ID) || answers[question.ID] == null))
                return Colors.Red;
            return Colors.Transparent;
        }

        public void CollapseMargins()
        {
            MasterGrid.ColumnDefinitions.First().Width = new GridLength(0.0F);
            MasterGrid.ColumnDefinitions.Last().Width = new GridLength(0.0F);
        }

        private void Changed(Dictionary<Guid, object> values)
        {
            OnChanged?.Invoke(this, values);
            foreach (var question in borders.Keys)
            {
                var color = GetBorder(question, values);
                borders[question].BorderBrush = new SolidColorBrush(color);
                borders[question].Background = new SolidColorBrush(InterpolateColors(color, Colors.Transparent, 0.8F));
            }
        }

        private Border CreateBorder(Grid grid, QAQuestion check, Dictionary<Guid, object> values)
        {
            var border = new Border();
            border.SetValue(Grid.ColumnProperty, 0);
            border.SetValue(Grid.RowProperty, grid.RowDefinitions.Count);
            border.Padding = new Thickness(2.0F);
            var color = GetBorder(check, values);
            border.BorderBrush = new SolidColorBrush(color);
            border.Background = new SolidColorBrush(InterpolateColors(color, Colors.Transparent, 0.8F));

            border.BorderThickness = new Thickness(1.5F);
            border.Margin = new Thickness(0F, 2F, 0F, 2F);
            border.CornerRadius = new CornerRadius(5.0F);
            grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Auto) });
            grid.Children.Add(border);
            return border;
        }

        private Border CreateCommentPanel(Grid grid, QAQuestion check, Dictionary<Guid, object> values, int number)
        {
            var border = CreateBorder(grid, check, values);

            var row = new Grid();
            row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) });
            row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) });
            var label = new TextBlock
            {
                Text = check.Question,
                FontSize = 14.0F,
                TextAlignment = TextAlignment.Center,
                TextWrapping = TextWrapping.Wrap,
                VerticalAlignment = VerticalAlignment.Center,
                Margin = GetMargin(grid),
                Padding = new Thickness(10)
            };
            label.SetValue(Grid.RowProperty, 0);
            label.SetValue(Grid.ColumnProperty, 0);
            row.Children.Add(label);

            border.Child = row;
            return border;
        }

        private Border CreateChoicePanel(Grid grid, QAQuestion check, Dictionary<Guid, object> values, int number)
        {
            var border = CreateBorder(grid, check, values);

            var row = new Grid();
            row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Auto) });
            row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) });
            var label = new TextBlock
            {
                Text = string.Format("{0}. {1}", number, check.Question),
                FontSize = 14.0F,
                Margin = GetMargin(grid),
                VerticalAlignment = VerticalAlignment.Center,
                TextAlignment = TextAlignment.Left,
                TextWrapping = TextWrapping.Wrap
            };
            label.SetValue(Grid.RowProperty, 0);
            label.SetValue(Grid.ColumnProperty, 0);
            row.Children.Add(label);

            if (!string.IsNullOrEmpty(check.Parameters))
            {
                var parameters = check.ParseParameters();
                var buttons = parameters["Options"].Split(',');
                var colors = parameters["Colors"].Split(',');

                var value = GetValue(values, check.ID).ToString();
                if (string.IsNullOrWhiteSpace(value))
                    value = parameters["Default"];

                for (var i = 0; i < buttons.Length; i++)
                    if (!string.IsNullOrWhiteSpace(buttons[i]))
                    {
                        var brush = new SolidColorBrush(Colors.WhiteSmoke);
                        try
                        {
                            brush = new BrushConverter().ConvertFromString(colors[i]) as SolidColorBrush;
                        }
                        catch (Exception e)
                        {
                            Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
                        }

                        row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80.0F) });
                        var button = new Button
                        {
                            Content = buttons[i],
                            FontSize = 14.0F,
                            Margin = GetMargin(grid),
                            Height = 40,
                            Background = buttons[i].Equals(value) ? brush : new SolidColorBrush(Colors.WhiteSmoke),
                            Tag = brush
                        };
                        button.Click += (o, e) =>
                        {
                            values[check.ID] = button.Content.ToString();
                            foreach (var other in row.Children)
                                if (other is Button)
                                {
                                    if (other == button)
                                        ((Button)other).Background = (SolidColorBrush)button.Tag;
                                    else
                                        ((Button)other).Background = new SolidColorBrush(Colors.WhiteSmoke);
                                }

                            Changed(values);
                        };
                        button.SetValue(Grid.RowProperty, 0);
                        button.SetValue(Grid.ColumnProperty, i + 1);
                        row.Children.Add(button);
                    }
            }

            border.Child = row;
            return border;
        }

        private Border CreateNumberPanel(Grid grid, QAQuestion check, Dictionary<Guid, object> values, int number)
        {
            var border = CreateBorder(grid, check, values);

            var row = new Grid();
            row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) });
            row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) });
            var label = new TextBlock
            {
                Text = string.Format("{0}. {1}", number, check.Question),
                FontSize = 14.0F,
                Margin = GetMargin(grid),
                VerticalAlignment = VerticalAlignment.Center,
                TextAlignment = TextAlignment.Left,
                TextWrapping = TextWrapping.Wrap
            };
            label.SetValue(Grid.RowProperty, 0);
            label.SetValue(Grid.ColumnProperty, 0);
            row.Children.Add(label);

            row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(160.0F) });
            var editor = new TextBox
            {
                Margin = GetMargin(grid),
                TextAlignment = TextAlignment.Center,
                VerticalContentAlignment = VerticalAlignment.Center,
                Height = 40,
                FontSize = 14.0F
            };
            editor.SetValue(Grid.RowProperty, 0);
            editor.SetValue(Grid.ColumnProperty, 1);
            row.Children.Add(editor);

            var def = "";
            if (!string.IsNullOrEmpty(check.Parameters))
            {
                var parameters = check.ParseParameters();
                def = parameters["Default"];
            }

            var value = GetValue(values, check.ID).ToString();
            editor.Text = string.IsNullOrWhiteSpace(value) ? def : value;

            editor.TextChanged += (o, e) =>
            {
                values[check.ID] = editor.Text;
                Changed(values);
            };

            border.Child = row;
            return border;
        }

        private Border CreateTextPanel(Grid grid, QAQuestion check, Dictionary<Guid, object> values, int number)
        {
            var border = CreateBorder(grid, check, values);

            var row = new Grid();
            row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) });

            row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) });
            var label = new TextBlock
            {
                Text = string.Format("{0}. {1}", number, check.Question),
                FontSize = 14.0F,
                Margin = GetMargin(grid),
                VerticalAlignment = VerticalAlignment.Center,
                TextAlignment = TextAlignment.Left,
                TextWrapping = TextWrapping.Wrap
            };
            label.SetValue(Grid.RowProperty, 0);
            label.SetValue(Grid.ColumnProperty, 0);
            row.Children.Add(label);

            row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(320.0F, GridUnitType.Pixel) });
            var editor = new TextBox
            {
                Margin = GetMargin(grid),
                TextAlignment = TextAlignment.Center,
                VerticalContentAlignment = VerticalAlignment.Center,
                Height = 40,
                FontSize = 14.0F
            };
            editor.SetValue(Grid.RowProperty, 0);
            editor.SetValue(Grid.ColumnProperty, 1);
            row.Children.Add(editor);

            var def = "";
            if (!string.IsNullOrEmpty(check.Parameters))
            {
                var parameters = check.ParseParameters();
                def = parameters["Default"];
            }

            var value = GetValue(values, check.ID).ToString();
            editor.Text = string.IsNullOrWhiteSpace(value) ? def : value;

            editor.TextChanged += (o, e) =>
            {
                values[check.ID] = editor.Text;
                Changed(values);
            };

            border.Child = row;
            return border;
        }

        private Border CreateComboPanel(Grid grid, QAQuestion check, Dictionary<Guid, object> values, int number)
        {
            var border = CreateBorder(grid, check, values);

            var row = new Grid();
            row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) });

            row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) });
            var label = new TextBlock
            {
                Text = string.Format("{0}. {1}", number, check.Question),
                FontSize = 14.0F,
                Margin = GetMargin(grid),
                VerticalAlignment = VerticalAlignment.Center,
                TextAlignment = TextAlignment.Left,
                TextWrapping = TextWrapping.Wrap
            };
            label.SetValue(Grid.RowProperty, 0);
            label.SetValue(Grid.ColumnProperty, 0);
            row.Children.Add(label);

            row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(320.0F, GridUnitType.Pixel) });
            var editor = new ComboBox
            {
                Margin = GetMargin(grid),
                HorizontalContentAlignment = HorizontalAlignment.Left,
                VerticalContentAlignment = VerticalAlignment.Center,
                Height = 40
            };

            editor.SetValue(Grid.RowProperty, 0);
            editor.SetValue(Grid.ColumnProperty, 1);
            row.Children.Add(editor);

            if (!string.IsNullOrEmpty(check.Parameters))
            {
                var parameters = check.ParseParameters();
                var options = parameters["Options"].Split(',');
                var def = parameters["Default"];

                for (var i = 0; i < options.Length; i++)
                    editor.Items.Add(options[i]);

                var value = GetValue(values, check.ID).ToString();

                editor.SelectedIndex = editor.Items.IndexOf(string.IsNullOrWhiteSpace(value) ? def : value);
            }

            editor.SelectionChanged += (o, e) =>
            {
                values[check.ID] = editor.SelectedIndex >= 0 ? editor.Items[editor.SelectedIndex].ToString() : "";
                Changed(values);
            };

            border.Child = row;
            return border;
        }

        public void Clear()
        {
            borders.Clear();
            MasterGrid.Children.Clear();
            MasterGrid.ColumnDefinitions.Clear();
            MasterGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.25F, GridUnitType.Star) });
            MasterGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.25F, GridUnitType.Star) });
        }

        public void LoadChecks(string title, IEnumerable<QAQuestion> checks, Dictionary<Guid, object> values)
        {
            var sections = new List<string>();
            foreach (var check in checks)
            {
                var section = string.IsNullOrWhiteSpace(check.Section) ? title : check.Section;
                if (!sections.Contains(section))
                    sections.Add(section);
            }

            if (!sections.Any())
                sections.Add(title);

            foreach (var section in sections)
            {
                var header = CreateHeader(section);

                var border = new Border
                {
                    CornerRadius = new CornerRadius(0.0F, 0.0F, 0.0F, 0.0F),
                    BorderThickness = new Thickness(1.0F),
                    BorderBrush = SystemColors.WindowFrameBrush,
                    Background = SystemColors.WindowBrush,
                    Margin = new Thickness(5.0F, 0.0F, 5.0F, 5.0F),
                    Padding = new Thickness(10.0F, 10.0F, 5.0F, 10.0F)
                };
                border.SetValue(Grid.RowProperty, 1);
                border.SetValue(Grid.ColumnProperty, MasterGrid.ColumnDefinitions.Count - 1);

                if (MasterGrid.ColumnDefinitions.Count > 2)
                {
                    header.Margin = new Thickness(0.0F, 5.0F, 5.0F, 2.0F);
                    border.Margin = new Thickness(0.0F, 0.0F, 5.0F, 5.0F);
                    CollapseMargins();
                }

                MasterGrid.ColumnDefinitions.Insert(MasterGrid.ColumnDefinitions.Count - 1,
                    new ColumnDefinition { Width = new GridLength(1F, GridUnitType.Star) });
                MasterGrid.Children.Add(border);

                var scroll = new ScrollViewer
                {
                    VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
                    HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled,
                    Padding = new Thickness(0.0F, 0.0F, 5.0F, 0.0F)
                };
                border.Child = scroll;

                var grid = new Grid();
                var i = 1;
                if (!checks.Any())
                {
                    var check = new QAQuestion
                    {
                        Answer = QAAnswer.Comment,
                        Question = "There are no checks defined for this form",
                        Parameters = ""
                    };
                    borders[check] = CreateCommentPanel(grid, check, values, i);
                }
                else
                {
                    foreach (var check in checks)
                    {
                        Border element = null;
                        if (check.Answer == QAAnswer.Comment)
                            element = CreateCommentPanel(grid, check, values, i);
                        else if (check.Answer == QAAnswer.Choice)
                            element = CreateChoicePanel(grid, check, values, i++);
                        else if (check.Answer == QAAnswer.Number)
                            element = CreateNumberPanel(grid, check, values, i++);
                        else if (check.Answer == QAAnswer.Text)
                            element = CreateTextPanel(grid, check, values, i++);
                        else if (check.Answer == QAAnswer.Combo)
                            element = CreateComboPanel(grid, check, values, i++);
                        if (element != null)
                            borders[check] = element;
                    }
                }

                scroll.Content = grid;
            }
        }
    }
}