using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Terminal.Gui;

namespace UICatalog.Scenarios;

[ScenarioMetadata ("Dynamic StatusBar", "Demonstrates how to add and remove a StatusBar and change items dynamically.")]
[ScenarioCategory ("Top Level Windows")]
public class DynamicStatusBar : Scenario
{
    public override void Main ()
    {

        Application.Run<DynamicStatusBarSample> ().Dispose();
    }

    public class Binding
    {
        private readonly PropertyInfo _sourceBindingProperty;
        private readonly object _sourceDataContext;
        private readonly IValueConverter _valueConverter;

        public Binding (
            View source,
            string sourcePropertyName,
            View target,
            string targetPropertyName,
            IValueConverter valueConverter = null
        )
        {
            Target = target;
            Source = source;
            SourcePropertyName = sourcePropertyName;
            TargetPropertyName = targetPropertyName;
            _sourceDataContext = Source.GetType ().GetProperty ("DataContext").GetValue (Source);
            _sourceBindingProperty = _sourceDataContext.GetType ().GetProperty (SourcePropertyName);
            _valueConverter = valueConverter;
            UpdateTarget ();

            var notifier = (INotifyPropertyChanged)_sourceDataContext;

            if (notifier != null)
            {
                notifier.PropertyChanged += (s, e) =>
                                            {
                                                if (e.PropertyName == SourcePropertyName)
                                                {
                                                    UpdateTarget ();
                                                }
                                            };
            }
        }

        public View Source { get; }
        public string SourcePropertyName { get; }
        public View Target { get; }
        public string TargetPropertyName { get; }

        private void UpdateTarget ()
        {
            try
            {
                object sourceValue = _sourceBindingProperty.GetValue (_sourceDataContext);

                if (sourceValue == null)
                {
                    return;
                }

                object finalValue = _valueConverter?.Convert (sourceValue) ?? sourceValue;

                PropertyInfo targetProperty = Target.GetType ().GetProperty (TargetPropertyName);
                targetProperty.SetValue (Target, finalValue);
            }
            catch (Exception ex)
            {
                MessageBox.ErrorQuery ("Binding Error", $"Binding failed: {ex}.", "Ok");
            }
        }
    }

    public class DynamicStatusBarDetails : FrameView
    {
        private Shortcut _statusItem;

        public DynamicStatusBarDetails (Shortcut statusItem = null) : this ()
        {
            _statusItem = statusItem;
            Title = statusItem == null ? "Adding New StatusBar Item." : "Editing StatusBar Item.";
        }

        public DynamicStatusBarDetails ()
        {
            var _lblTitle = new Label { Y = 1, Text = "Title:" };
            Add (_lblTitle);

            TextTitle = new TextField { X = Pos.Right (_lblTitle) + 4, Y = Pos.Top (_lblTitle), Width = Dim.Fill () };
            Add (TextTitle);

            var _lblAction = new Label { X = Pos.Left (_lblTitle), Y = Pos.Bottom (_lblTitle) + 1, Text = "Action:" };
            Add (_lblAction);

            TextAction = new TextView
            {
                X = Pos.Left (TextTitle), Y = Pos.Top (_lblAction), Width = Dim.Fill (), Height = 5
            };
            Add (TextAction);

            var _lblShortcut = new Label
            {
                X = Pos.Left (_lblTitle), Y = Pos.Bottom (TextAction) + 1, Text = "Shortcut:"
            };
            Add (_lblShortcut);

            TextShortcut = new TextField
            {
                X = Pos.X (TextAction), Y = Pos.Y (_lblShortcut), Width = Dim.Fill (), ReadOnly = true
            };

            TextShortcut.KeyDown += (s, e) =>
                                    {
                                        if (!ProcessKey (e))
                                        {
                                            return;
                                        }

                                        if (CheckShortcut (e.KeyCode, true))
                                        {
                                            e.Handled = true;
                                        }
                                    };

            bool ProcessKey (Key ev)
            {
                switch (ev.KeyCode)
                {
                    case KeyCode.CursorUp:
                    case KeyCode.CursorDown:
                    case KeyCode.Tab:
                    case KeyCode.Tab | KeyCode.ShiftMask:
                        return false;
                }

                return true;
            }

            bool CheckShortcut (KeyCode k, bool pre)
            {
                Shortcut m = _statusItem != null ? _statusItem : new Shortcut (k, "", null);

                if (pre && !ShortcutHelper.PreShortcutValidation (k))
                {
                    TextShortcut.Text = "";

                    return false;
                }

                if (!pre)
                {
                    return true;
                }

                TextShortcut.Text = k.ToString();
                return true;
            }

            TextShortcut.KeyUp += (s, e) =>
                                  {
                                      if (CheckShortcut (e.KeyCode, true))
                                      {
                                          e.Handled = true;
                                      }
                                  };
            Add (TextShortcut);

            var _btnShortcut = new Button
            {
                X = Pos.X (_lblShortcut), Y = Pos.Bottom (TextShortcut) + 1, Text = "Clear Shortcut"
            };
            _btnShortcut.Accept += (s, e) => { TextShortcut.Text = ""; };
            Add (_btnShortcut);
        }

        public TextView TextAction { get; }
        public TextField TextShortcut { get; }
        public TextField TextTitle { get; }
        public Action CreateAction (DynamicStatusItem item) { return () => MessageBox.ErrorQuery (item.Title, item.Action, "Ok"); }

        public void EditStatusItem (Shortcut statusItem)
        {
            if (statusItem == null)
            {
                Enabled = false;
                CleanEditStatusItem ();

                return;
            }

            Enabled = true;
            _statusItem = statusItem;
            TextTitle.Text = statusItem?.Title ?? "";

            TextAction.Text = statusItem != null && statusItem.Action != null
                                  ? GetTargetAction (statusItem.Action)
                                  : string.Empty;

            TextShortcut.Text = statusItem.CommandView.Text;
        }

        public DynamicStatusItem EnterStatusItem ()
        {
            var valid = false;

            if (_statusItem == null)
            {
                var m = new DynamicStatusItem ();
                TextTitle.Text = m.Title;
                TextAction.Text = m.Action;
            }
            else
            {
                EditStatusItem (_statusItem);
            }

            var btnOk = new Button { IsDefault = true, Text = "OK" };

            btnOk.Accept += (s, e) =>
                              {
                                  if (string.IsNullOrEmpty (TextTitle.Text))
                                  {
                                      MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
                                  }
                                  else
                                  {
                                      if (!string.IsNullOrEmpty (TextShortcut.Text))
                                      {
                                          TextTitle.Text = DynamicStatusBarSample.SetTitleText (
                                                                                                TextTitle.Text,
                                                                                                TextShortcut.Text
                                                                                               );
                                      }

                                      valid = true;
                                      Application.RequestStop ();
                                  }
                              };
            var btnCancel = new Button { Text = "Cancel" };

            btnCancel.Accept += (s, e) =>
                                  {
                                      TextTitle.Text = string.Empty;
                                      Application.RequestStop ();
                                  };
            var dialog = new Dialog { Title = "Enter the menu details.", Buttons = [btnOk, btnCancel], Height = Dim.Auto (DimAutoStyle.Content, 17, Driver.Rows) };

            Width = Dim.Fill ();
            Height = Dim.Fill () - 1;
            dialog.Add (this);
            TextTitle.SetFocus ();
            TextTitle.CursorPosition = TextTitle.Text.Length;
            Application.Run (dialog);
            dialog.Dispose ();

            return valid
                       ? new DynamicStatusItem
                       {
                           Title = TextTitle.Text, Action = TextAction.Text, Shortcut = TextShortcut.Text
                       }
                       : null;
        }

        private void CleanEditStatusItem ()
        {
            TextTitle.Text = "";
            TextAction.Text = "";
            TextShortcut.Text = "";
        }

        private string GetTargetAction (Action action)
        {
            object me = action.Target;

            if (me == null)
            {
                throw new ArgumentException ();
            }

            var v = new object ();

            foreach (FieldInfo field in me.GetType ().GetFields ())
            {
                if (field.Name == "item")
                {
                    v = field.GetValue (me);
                }
            }

            return v == null || !(v is DynamicStatusItem item) ? string.Empty : item.Action;
        }
    }

    public class DynamicStatusBarSample : Window
    {
        private readonly ListView _lstItems;
        private Shortcut _currentEditStatusItem;
        private int _currentSelectedStatusBar = -1;
        private Shortcut _currentStatusItem;
        private StatusBar _statusBar;

        public DynamicStatusBarSample ()
        {
            DataContext = new DynamicStatusItemModel ();

            Title = $"{Application.QuitKey} to Quit";

            var _frmStatusBar = new FrameView
            {
                Y = 5, Width = Dim.Percent (50), Height = Dim.Fill (2), Title = "Items:"
            };

            var _btnAddStatusBar = new Button { Y = 1, Text = "Add a StatusBar" };
            _frmStatusBar.Add (_btnAddStatusBar);

            var _btnRemoveStatusBar = new Button { Y = 1, Text = "Remove a StatusBar" };

            _btnRemoveStatusBar.X = Pos.AnchorEnd ();
            _frmStatusBar.Add (_btnRemoveStatusBar);

            var _btnAdd = new Button { Y = Pos.Top (_btnRemoveStatusBar) + 2, Text = " Add  " };
            _btnAdd.X = Pos.AnchorEnd ();
            _frmStatusBar.Add (_btnAdd);

            _lstItems = new ListView
            {
                ColorScheme = Colors.ColorSchemes ["Dialog"],
                Y = Pos.Top (_btnAddStatusBar) + 2,
                Width = Dim.Fill () - Dim.Width (_btnAdd) - 1,
                Height = Dim.Fill (),
                Source = new ListWrapper<DynamicStatusItemList> ([])
            };
            _frmStatusBar.Add (_lstItems);

            var _btnRemove = new Button { X = Pos.Left (_btnAdd), Y = Pos.Top (_btnAdd) + 1, Text = "Remove" };
            _frmStatusBar.Add (_btnRemove);

            var _btnUp = new Button { X = Pos.Right (_lstItems) + 2, Y = Pos.Top (_btnRemove) + 2, Text = CM.Glyphs.UpArrow.ToString () };
            _frmStatusBar.Add (_btnUp);

            var _btnDown = new Button { X = Pos.Right (_lstItems) + 2, Y = Pos.Top (_btnUp) + 1, Text = CM.Glyphs.DownArrow.ToString () };
            _frmStatusBar.Add (_btnDown);

            Add (_frmStatusBar);

            var _frmStatusBarDetails = new DynamicStatusBarDetails
            {
                X = Pos.Right (_frmStatusBar),
                Y = Pos.Top (_frmStatusBar),
                Width = Dim.Fill (),
                Height = Dim.Fill (4),
                Title = "Shortcut Details:"
            };
            Add (_frmStatusBarDetails);

            _btnUp.Accept += (s, e) =>
                              {
                                  int i = _lstItems.SelectedItem;
                                  Shortcut statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].Shortcut : null;

                                  if (statusItem != null)
                                  {
                                      Shortcut [] items = _statusBar.Subviews.Cast<Shortcut> ().ToArray ();

                                      if (i > 0)
                                      {
                                          items [i] = items [i - 1];
                                          items [i - 1] = statusItem;
                                          DataContext.Items [i] = DataContext.Items [i - 1];

                                          DataContext.Items [i - 1] =
                                              new DynamicStatusItemList (statusItem.Title, statusItem);
                                          _lstItems.SelectedItem = i - 1;
                                          _statusBar.SetNeedsDisplay ();
                                      }
                                  }
                              };

            _btnDown.Accept += (s, e) =>
                                {
                                    int i = _lstItems.SelectedItem;
                                    Shortcut statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].Shortcut : null;

                                    if (statusItem != null)
                                    {
                                        Shortcut [] items = _statusBar.Subviews.Cast<Shortcut> ().ToArray ();

                                        if (i < items.Length - 1)
                                        {
                                            items [i] = items [i + 1];
                                            items [i + 1] = statusItem;
                                            DataContext.Items [i] = DataContext.Items [i + 1];

                                            DataContext.Items [i + 1] =
                                                new DynamicStatusItemList (statusItem.Title, statusItem);
                                            _lstItems.SelectedItem = i + 1;
                                            _statusBar.SetNeedsDisplay ();
                                        }
                                    }
                                };

            var _btnOk = new Button
            {
                X = Pos.Right (_frmStatusBar) + 20, Y = Pos.Bottom (_frmStatusBarDetails), Text = "Ok"
            };
            Add (_btnOk);

            var _btnCancel = new Button { X = Pos.Right (_btnOk) + 3, Y = Pos.Top (_btnOk), Text = "Cancel" };
            _btnCancel.Accept += (s, e) => { SetFrameDetails (_currentEditStatusItem); };
            Add (_btnCancel);

            _lstItems.SelectedItemChanged += (s, e) => { SetFrameDetails (); };

            _btnOk.Accept += (s, e) =>
                              {
                                  if (string.IsNullOrEmpty (_frmStatusBarDetails.TextTitle.Text) && _currentEditStatusItem != null)
                                  {
                                      MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
                                  }
                                  else if (_currentEditStatusItem != null)
                                  {
                                      _frmStatusBarDetails.TextTitle.Text = SetTitleText (
                                                                                          _frmStatusBarDetails.TextTitle.Text,
                                                                                          _frmStatusBarDetails.TextShortcut.Text
                                                                                         );

                                      var statusItem = new DynamicStatusItem
                                      {
                                          Title = _frmStatusBarDetails.TextTitle.Text,
                                          Action = _frmStatusBarDetails.TextAction.Text,
                                          Shortcut = _frmStatusBarDetails.TextShortcut.Text
                                      };
                                      UpdateStatusItem (_currentEditStatusItem, statusItem, _lstItems.SelectedItem);
                                  }
                              };

            _btnAdd.Accept += (s, e) =>
                               {
                                   if (StatusBar == null)
                                   {
                                       MessageBox.ErrorQuery (
                                                              "StatusBar Bar Error",
                                                              "Must add a StatusBar first!",
                                                              "Ok"
                                                             );
                                       _btnAddStatusBar.SetFocus ();

                                       return;
                                   }

                                   var frameDetails = new DynamicStatusBarDetails ();
                                   DynamicStatusItem item = frameDetails.EnterStatusItem ();

                                   if (item == null)
                                   {
                                       return;
                                   }

                                   Shortcut newStatusItem = CreateNewStatusBar (item);
                                   _currentSelectedStatusBar++;
                                   _statusBar.AddShortcutAt (_currentSelectedStatusBar, newStatusItem);
                                   DataContext.Items.Add (new DynamicStatusItemList (newStatusItem.Title, newStatusItem));
                                   _lstItems.MoveDown ();
                                   SetFrameDetails ();
                               };

            _btnRemove.Accept += (s, e) =>
                                  {
                                      Shortcut statusItem = DataContext.Items.Count > 0
                                                                  ? DataContext.Items [_lstItems.SelectedItem].Shortcut
                                                                  : null;

                                      if (statusItem != null)
                                      {
                                          _statusBar.RemoveShortcut (_currentSelectedStatusBar);
                                          DataContext.Items.RemoveAt (_lstItems.SelectedItem);

                                          if (_lstItems.Source.Count > 0 && _lstItems.SelectedItem > _lstItems.Source.Count - 1)
                                          {
                                              _lstItems.SelectedItem = _lstItems.Source.Count - 1;
                                          }

                                          _lstItems.SetNeedsDisplay ();
                                          SetFrameDetails ();
                                      }
                                  };

            _lstItems.Enter += (s, e) =>
                               {
                                   Shortcut statusItem = DataContext.Items.Count > 0
                                                               ? DataContext.Items [_lstItems.SelectedItem].Shortcut
                                                               : null;
                                   SetFrameDetails (statusItem);
                               };

            _btnAddStatusBar.Accept += (s, e) =>
                                        {
                                            if (_statusBar != null)
                                            {
                                                return;
                                            }

                                            _statusBar = new StatusBar ();
                                            Add (_statusBar);
                                        };

            _btnRemoveStatusBar.Accept += (s, e) =>
                                           {
                                               if (_statusBar == null)
                                               {
                                                   return;
                                               }

                                               Remove (_statusBar);
                                               _statusBar = null;
                                               DataContext.Items = [];
                                               _currentStatusItem = null;
                                               _currentSelectedStatusBar = -1;
                                               SetListViewSource (_currentStatusItem, true);
                                               SetFrameDetails ();
                                           };

            SetFrameDetails ();

            var ustringConverter = new UStringValueConverter ();
            var listWrapperConverter = new ListWrapperConverter<DynamicStatusItemList> ();

            var lstItems = new Binding (this, "Items", _lstItems, "Source", listWrapperConverter);

            void SetFrameDetails (Shortcut statusItem = null)
            {
                Shortcut newStatusItem;

                if (statusItem == null)
                {
                    newStatusItem = DataContext.Items.Count > 0
                                        ? DataContext.Items [_lstItems.SelectedItem].Shortcut
                                        : null;
                }
                else
                {
                    newStatusItem = statusItem;
                }

                _currentEditStatusItem = newStatusItem;
                _frmStatusBarDetails.EditStatusItem (newStatusItem);
                bool f = _btnOk.Enabled == _frmStatusBarDetails.Enabled;

                if (!f)
                {
                    _btnOk.Enabled = _frmStatusBarDetails.Enabled;
                    _btnCancel.Enabled = _frmStatusBarDetails.Enabled;
                }
            }

            void SetListViewSource (Shortcut _currentStatusItem, bool fill = false)
            {
                DataContext.Items = [];
                Shortcut statusItem = _currentStatusItem;

                if (!fill)
                {
                    return;
                }

                if (statusItem != null)
                {
                    foreach (Shortcut si in _statusBar.Subviews.Cast<Shortcut> ())
                    {
                        DataContext.Items.Add (new DynamicStatusItemList (si.Title, si));
                    }
                }
            }

            Shortcut CreateNewStatusBar (DynamicStatusItem item)
            {
                var newStatusItem = new Shortcut (Key.Empty, item.Title, null);

                return newStatusItem;
            }

            void UpdateStatusItem (
                Shortcut _currentEditStatusItem,
                DynamicStatusItem statusItem,
                int index
            )
            {
                _currentEditStatusItem = CreateNewStatusBar (statusItem);
                //_statusBar.Items [index] = _currentEditStatusItem;

                if (DataContext.Items.Count == 0)
                {
                    DataContext.Items.Add (
                                           new DynamicStatusItemList (
                                                                      _currentEditStatusItem.Title,
                                                                      _currentEditStatusItem
                                                                     )
                                          );
                }

                DataContext.Items [index] = new DynamicStatusItemList (
                                                                       _currentEditStatusItem.Title,
                                                                       _currentEditStatusItem
                                                                      );
                SetFrameDetails (_currentEditStatusItem);
            }

            //_frmStatusBarDetails.Initialized += (s, e) => _frmStatusBarDetails.Enabled = false;
        }

        public DynamicStatusItemModel DataContext { get; set; }

        public static string SetTitleText (string title, string shortcut)
        {
            string txt = title;
            string [] split = title.Split ('~');

            if (split.Length > 1)
            {
                txt = split [2].Trim ();
            }

            if (string.IsNullOrEmpty (shortcut) || shortcut == "Null")
            {
                return txt;
            }

            return $"~{shortcut}~ {txt}";
        }
    }

    public class DynamicStatusItem
    {
        public string Action { get; set; } = "";
        public string Shortcut { get; set; }
        public string Title { get; set; } = "New";
    }

    public class DynamicStatusItemList
    {
        public DynamicStatusItemList () { }

        public DynamicStatusItemList (string title, Shortcut statusItem)
        {
            Title = title;
            Shortcut = statusItem;
        }

        public Shortcut Shortcut { get; set; }
        public string Title { get; set; }
        public override string ToString () { return $"{Title}, {Shortcut}"; }
    }

    public class DynamicStatusItemModel : INotifyPropertyChanged
    {
        private ObservableCollection<DynamicStatusItemList> _items;
        private string _statusBar;
        public DynamicStatusItemModel () { Items = []; }

        public ObservableCollection<DynamicStatusItemList> Items
        {
            get => _items;
            set
            {
                if (value == _items)
                {
                    return;
                }

                _items = value;

                PropertyChanged?.Invoke (
                                         this,
                                         new PropertyChangedEventArgs (GetPropertyName ())
                                        );
            }
        }

        public string StatusBar
        {
            get => _statusBar;
            set
            {
                if (value == _statusBar)
                {
                    return;
                }

                _statusBar = value;

                PropertyChanged?.Invoke (
                                         this,
                                         new PropertyChangedEventArgs (GetPropertyName ())
                                        );
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public string GetPropertyName ([CallerMemberName] string propertyName = null) { return propertyName; }
    }

    public interface IValueConverter
    {
        object Convert (object value, object parameter = null);
    }

    public class ListWrapperConverter<T> : IValueConverter
    {
        public object Convert (object value, object parameter = null) { return new ListWrapper<T> ((ObservableCollection<T>)value); }
    }

    public class UStringValueConverter : IValueConverter
    {
        public object Convert (object value, object parameter = null)
        {
            byte [] data = Encoding.ASCII.GetBytes (value.ToString () ?? string.Empty);

            return StringExtensions.ToString (data);
        }
    }
}
