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

namespace InABox.DynamicGrid
{
    public class CoreTimeEditorControl : DynamicEnclosedEditorControl<CoreTime>
    {
        private Grid Grid;

        private DateTimeEdit Start;
        private TimeSpanEdit Duration;
        private DateTimeEdit Finish;

        private CoreTime Time = new();

        protected override FrameworkElement CreateEditor()
        {
            Grid = new Grid();

            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

            Start = CreateTimeEdit("Start:", 0, StartChanged, false);
            Duration = CreateTimeSpanEdit("Duration:", 4, DurationChanged, true);
            Finish = CreateTimeEdit("Finish:", 8, FinishChanged, true);
            
            return Grid;
        }

        private void StartChanged(TimeSpan start)
        {
            Time.Start = start;

            UpdateEditors();
        }

        private void DurationChanged(TimeSpan duration)
        {
            Time.Duration = duration;

            UpdateEditors();
        }

        private void FinishChanged(TimeSpan finish)
        {
            Time.Finish = finish;

            UpdateEditors();
        }

        private void UpdateEditors()
        {
            SetValue(Start, Time.Start);
            SetValue(Duration, Time.Duration);
            SetValue(Finish, Time.Finish);

            CheckChanged();
        }

        private TimeSpanEdit CreateTimeSpanEdit(string header, int column, Action<TimeSpan> onChanged, bool leftMargin)
        {
            var label = new Label
            {
                Content = header,
                Margin = new Thickness(leftMargin ? 5 : 0, 0, 0, 0)
            };
            label.SetValue(Grid.ColumnProperty, column);

            var edit = new TimeSpanEdit
            {
                Format = "h:mm",
                MinValue = new TimeSpan(),
                MaxValue = TimeSpan.MaxValue,
                VerticalAlignment = VerticalAlignment.Stretch,
                VerticalContentAlignment = VerticalAlignment.Center,
                HorizontalAlignment = HorizontalAlignment.Stretch,
                HorizontalContentAlignment = HorizontalAlignment.Center,
                ShowArrowButtons = false
            };
            edit.SetValue(Grid.ColumnProperty, column + 1);

            edit.PreviewKeyDown += (o, e) =>
            {
                var separator = edit.Text.IndexOf(":");

                if (e.Key == Key.OemPeriod)
                {
                    edit.Select(separator + 1, 2);
                    e.Handled = true;
                }
                else if (e.Key == Key.Back)
                {
                    if (string.Equals(edit.SelectedText, "00"))
                        edit.Select(0, separator);
                    else
                        edit.SelectedText = "00";
                    e.Handled = true;
                }
                else if (e.Key == Key.Tab)
                {
                    if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.LeftShift))
                    {
                        if (edit.SelectionStart > separator)
                        {
                            edit.Select(0, separator);
                            e.Handled = true;
                        }
                    }
                    else
                    {
                        if (edit.SelectionLength != edit.Text.Length && edit.SelectionStart < separator)
                        {
                            edit.Select(separator + 1, 2);
                            e.Handled = true;
                        }
                    }
                }
            };

            var changed = false;
            edit.ValueChanged += (o, e) =>
            {
                changed = true;
            };

            edit.LostFocus += (o, e) =>
            {
                if (changed)
                    onChanged(GetValue(edit));
            };

            var less = new Button
            {
                Content = "<",
                Width = 23,
                Margin = new Thickness(2, 0, 0, 0),
                Focusable = false
            };
            less.SetValue(Grid.ColumnProperty, column + 2);

            less.Click += (o, e) =>
            {
                edit.Value = edit.Value.HasValue && edit.Value >= new TimeSpan(0, 15, 0)
                    ? edit.Value.Value.Subtract(new TimeSpan(0, 15, 0))
                    : new TimeSpan(0);
                onChanged(GetValue(edit));
            };

            var more = new Button
            {
                Content = ">",
                Width = 23,
                Margin = new Thickness(2, 0, 0, 0),
                Focusable = false
            };
            more.SetValue(Grid.ColumnProperty, column + 3);

            more.Click += (o, e) =>
            {
                edit.Value = edit.Value.HasValue ? edit.Value.Value.Add(new TimeSpan(0, 15, 0)) : new TimeSpan(0, 15, 0);
                onChanged(GetValue(edit));
            };

            Grid.Children.Add(label);
            Grid.Children.Add(more);
            Grid.Children.Add(less);
            Grid.Children.Add(edit);

            return edit;
        }

        private DateTimeEdit CreateTimeEdit(string header, int column, Action<TimeSpan> onChanged, bool leftMargin)
        {
            var label = new Label
            {
                Content = header,
                Margin = new Thickness(leftMargin ? 5 : 0, 0, 0, 0)
            };
            label.SetValue(Grid.ColumnProperty, column);

            var edit = new DateTimeEdit
            {
                Pattern = DateTimePattern.CustomPattern,
                CustomPattern = "HH:mm", //DateTimeFormat.Custom,
                //FormatString = "HH:mm",
                VerticalAlignment = VerticalAlignment.Stretch,
                VerticalContentAlignment = VerticalAlignment.Center,
                HorizontalAlignment = HorizontalAlignment.Stretch,
                HorizontalContentAlignment = HorizontalAlignment.Center,
                //TimeInterval = new TimeSpan(0, 15, 0),
                //ShowButtonSpinner = false
                DropDownView = DropDownViews.Clock,
                IsButtonPopUpEnabled = false
            };
            edit.SetValue(Grid.ColumnProperty, column + 1);

            edit.PreviewKeyDown += (o, e) =>
            {
                var separator = edit.Text.IndexOf(":");

                if (e.Key == Key.OemPeriod)
                {
                    edit.Select(separator + 1, 2);
                    e.Handled = true;
                }
                else if (e.Key == Key.Back)
                {
                    if (string.Equals(edit.SelectedText, "00"))
                        edit.Select(0, separator);
                    else
                        edit.SelectedText = "00";
                    e.Handled = true;
                }
                else if (e.Key == Key.Tab)
                {
                    if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.LeftShift))
                    {
                        if (edit.SelectionStart > separator)
                        {
                            edit.Select(0, separator);
                            e.Handled = true;
                        }
                    }
                    else
                    {
                        if (edit.SelectionLength != edit.Text.Length && edit.SelectionStart < separator)
                        {
                            edit.Select(separator + 1, 2);
                            e.Handled = true;
                        }
                    }
                }
            };

            var changed = false;
            edit.DateTimeChanged += (o, e) =>
            {
                changed = true;
            };

            edit.LostFocus += (o, e) =>
            {
                if (changed)
                {
                    onChanged(GetValue(edit));
                }
            };

            var less = new Button
            {
                Content = "<",
                Width = 23,
                Margin = new Thickness(2, 0, 0, 0),
                Focusable = false
            };
            less.SetValue(Grid.ColumnProperty, column + 2);

            less.Click += (o, e) =>
            {
                edit.DateTime = edit.DateTime.HasValue && edit.DateTime.Value.TimeOfDay >= new TimeSpan(0, 15, 0)
                    ? edit.DateTime.Value.Subtract(new TimeSpan(0, 15, 0))
                    : edit.DateTime;
                onChanged(GetValue(edit));
            };

            var more = new Button
            {
                Content = ">",
                Width = 23,
                Margin = new Thickness(2, 0, 0, 0),
                Focusable = false
            };
            more.SetValue(Grid.ColumnProperty, column + 3);

            more.Click += (o, e) =>
            {
                edit.DateTime = edit.DateTime.HasValue && edit.DateTime.Value.TimeOfDay < new TimeSpan(23, 45, 0)
                    ? edit.DateTime.Value.Add(new TimeSpan(0, 15, 0))
                    : edit.DateTime;
                onChanged(GetValue(edit));
            };

            Grid.Children.Add(label);
            Grid.Children.Add(edit);
            Grid.Children.Add(less);
            Grid.Children.Add(more);

            return edit;
        }

        private static TimeSpan GetValue(DateTimeEdit edit)
        {
            var result = new TimeSpan(0);

            if (edit.DateTime.HasValue)
                result = edit.DateTime.Value.TimeOfDay;

            result = new TimeSpan(result.Hours, result.Minutes, 0);

            return result;
        }
        private static void SetValue(DateTimeEdit edit, TimeSpan value)
        {
            if (value.Ticks > 0)
                edit.DateTime = DateTime.MinValue.Add(value);
            else
                edit.DateTime = null;
        }
        private static TimeSpan GetValue(TimeSpanEdit edit)
        {
            var result = new TimeSpan(0);

            if (edit.Value.HasValue)
                result = edit.Value.Value;

            return result;
        }
        private static void SetValue(TimeSpanEdit edit, TimeSpan value)
        {
            edit.Value = value;
        }

        public override int DesiredHeight()
        {
            return 25;
        }

        public override int DesiredWidth()
        {
            return int.MaxValue;
        }

        public override Dictionary<string, object?> GetValues()
        {
            return new Dictionary<string, object?>
            {
                { $"{ColumnName}.Start", Time.Start },
                { $"{ColumnName}.Duration", Time.Duration },
                { $"{ColumnName}.Finish", Time.Finish }
            };
        }

        public override object? GetValue(string property)
        {
            if (!property.StartsWith($"{ColumnName}.")) return null;
            property = property[(ColumnName.Length + 1)..];

            if (property == "Start") return Time.Start;
            if (property == "Duration") return Time.Duration;
            if (property == "Finish") return Time.Finish;

            return null;
        }

        public override void SetValue(string property, object? value)
        {
            if (!property.StartsWith($"{ColumnName}.")) return;
            property = property[(ColumnName.Length + 1)..];

            if (value is not TimeSpan timeSpan) return;

            if (property == "Start") Time.Start = timeSpan;
            else if (property == "Duration") Time.Duration = timeSpan;
            else if (property == "Finish") Time.Finish = timeSpan;

            UpdateEditors();
        }

        public override void SetColor(Color color)
        {
            Start.Background = new SolidColorBrush(color);
            Duration.Background = new SolidColorBrush(color);
            Finish.Background = new SolidColorBrush(color);
        }

        public override void SetFocus()
        {
            Start.Focus();
        }
    }
}
