using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using WindowsInput;
using WpfKb.LogicalKeys;

namespace WpfKb.Controls
{
    public class OnScreenWebKeyboard : Grid
    {
        public static readonly DependencyProperty AreAnimationsEnabledProperty = DependencyProperty.Register("AreAnimationsEnabled", typeof(bool), typeof(OnScreenWebKeyboard), new UIPropertyMetadata(true, OnAreAnimationsEnabledPropertyChanged));

        private ObservableCollection<OnScreenKeyboardSection> _sections;
        private List<ModifierKeyBase> _modifierKeys;
        private List<ILogicalKey> _allLogicalKeys;
        private List<OnScreenKey> _allOnScreenKeys;

        public bool AreAnimationsEnabled
        {
            get { return (bool)GetValue(AreAnimationsEnabledProperty); }
            set { SetValue(AreAnimationsEnabledProperty, value); }
        }

        private static void OnAreAnimationsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var keyboard = (OnScreenWebKeyboard)d;
            keyboard._allOnScreenKeys.ToList().ForEach(x => x.AreAnimationsEnabled = (bool)e.NewValue);
        }

        public override void BeginInit()
        {
            SetValue(FocusManager.IsFocusScopeProperty, true);
            _modifierKeys = new List<ModifierKeyBase>();
            _allLogicalKeys = new List<ILogicalKey>();
            _allOnScreenKeys = new List<OnScreenKey>();

            _sections = new ObservableCollection<OnScreenKeyboardSection>();

            var mainSection = new OnScreenKeyboardSection();
            var mainKeys = new ObservableCollection<OnScreenKey>
                               {
                                   new OnScreenKey { GridRow = 0, GridColumn = 0, Key =  new StringKey("1", "1")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 1, Key =  new StringKey("2", "2")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 2, Key =  new StringKey("3", "3")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 3, Key =  new StringKey("4", "4")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 4, Key =  new StringKey("5", "5")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 5, Key =  new StringKey("6", "6")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 6, Key =  new StringKey("7", "7")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 7, Key =  new StringKey("8", "8")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 8, Key =  new StringKey("9", "9")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 9, Key =  new StringKey("0", "0")},
                                   new OnScreenKey { GridRow = 0, GridColumn = 10, Key =  new VirtualKey(VirtualKeyCode.BACK, "Bksp"), GridWidth = new GridLength(2, GridUnitType.Star)},

                                   new OnScreenKey { GridRow = 1, GridColumn = 0, Key =  new StringKey("Q", "Q")},
                                   new OnScreenKey { GridRow = 1, GridColumn = 1, Key =  new StringKey("W", "W")},
                                   new OnScreenKey { GridRow = 1, GridColumn = 2, Key =  new StringKey("E", "E")},
                                   new OnScreenKey { GridRow = 1, GridColumn = 3, Key =  new StringKey("R", "R")},
                                   new OnScreenKey { GridRow = 1, GridColumn = 4, Key =  new StringKey("T", "T")},
                                   new OnScreenKey { GridRow = 1, GridColumn = 5, Key =  new StringKey("Y", "Y")},
                                   new OnScreenKey { GridRow = 1, GridColumn = 6, Key =  new StringKey("U", "U")},
                                   new OnScreenKey { GridRow = 1, GridColumn = 7, Key =  new StringKey("I", "I")},
                                   new OnScreenKey { GridRow = 1, GridColumn = 8, Key =  new StringKey("O", "O")},
                                   new OnScreenKey { GridRow = 1, GridColumn = 9, Key =  new StringKey("P", "P")},
                                   
                                   new OnScreenKey { GridRow = 2, GridColumn = 0, Key =  new StringKey("A", "A")},
                                   new OnScreenKey { GridRow = 2, GridColumn = 1, Key =  new StringKey("S", "S")},
                                   new OnScreenKey { GridRow = 2, GridColumn = 2, Key =  new StringKey("D", "D")},
                                   new OnScreenKey { GridRow = 2, GridColumn = 3, Key =  new StringKey("F", "F")},
                                   new OnScreenKey { GridRow = 2, GridColumn = 4, Key =  new StringKey("G", "G")},
                                   new OnScreenKey { GridRow = 2, GridColumn = 5, Key =  new StringKey("H", "H")},
                                   new OnScreenKey { GridRow = 2, GridColumn = 6, Key =  new StringKey("J", "J")},
                                   new OnScreenKey { GridRow = 2, GridColumn = 7, Key =  new StringKey("K", "K")},
                                   new OnScreenKey { GridRow = 2, GridColumn = 8, Key =  new StringKey("L", "L")},

                                   new OnScreenKey { GridRow = 3, GridColumn = 0, Key =  new StringKey("Z", "Z")},
                                   new OnScreenKey { GridRow = 3, GridColumn = 1, Key =  new StringKey("X", "X")},
                                   new OnScreenKey { GridRow = 3, GridColumn = 2, Key =  new StringKey("C", "C")},
                                   new OnScreenKey { GridRow = 3, GridColumn = 3, Key =  new StringKey("V", "V")},
                                   new OnScreenKey { GridRow = 3, GridColumn = 4, Key =  new StringKey("B", "B")},
                                   new OnScreenKey { GridRow = 3, GridColumn = 5, Key =  new StringKey("N", "N")},
                                   new OnScreenKey { GridRow = 3, GridColumn = 6, Key =  new StringKey("M", "M")},
                                   new OnScreenKey { GridRow = 3, GridColumn = 7, Key =  new StringKey(".", ".")},
                                   new OnScreenKey { GridRow = 3, GridColumn = 8, Key =  new StringKey("_", "_")},
                                   new OnScreenKey { GridRow = 3, GridColumn = 9, Key =  new StringKey("-", "-")},
                                   
                                   new OnScreenKey { GridRow = 4, GridColumn = 0, Key =  new VirtualKey(VirtualKeyCode.SPACE, " "), GridWidth = new GridLength(5, GridUnitType.Star)},
                               };

            mainSection.Keys = mainKeys;
            mainSection.SetValue(ColumnProperty, 0);
            _sections.Add(mainSection);
            ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(3, GridUnitType.Star) });
            Children.Add(mainSection);

            _allLogicalKeys.AddRange(mainKeys.Select(x => x.Key));
            _allOnScreenKeys.AddRange(mainSection.Keys);


            var specialSection = new OnScreenKeyboardSection();
            /*var specialKeys = new ObservableCollection<OnScreenKey>
                                  {
                                      new OnScreenKey { GridRow = 0, GridColumn = 0, Key = new ChordKey("Select All", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_A), GridWidth = new GridLength(2, GridUnitType.Star)},
                                      new OnScreenKey { GridRow = 0, GridColumn = 1, Key = new ChordKey("Undo", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_Z) },
                                      new OnScreenKey { GridRow = 1, GridColumn = 0, Key = new ChordKey("Copy", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_C) },
                                      new OnScreenKey { GridRow = 1, GridColumn = 1, Key = new ChordKey("Cut", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_X) },
                                      new OnScreenKey { GridRow = 1, GridColumn = 2, Key = new ChordKey("Paste", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_V) },
                                      new OnScreenKey { GridRow = 2, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.DELETE, "Del") },
                                      new OnScreenKey { GridRow = 2, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.HOME, "Home") },
                                      new OnScreenKey { GridRow = 2, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.END, "End") },
                                      //VALE
                                      new OnScreenKey { GridRow = 3, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.PRIOR, "PgUp") },
                                      new OnScreenKey { GridRow = 3, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.UP, "Up") },
                                      new OnScreenKey { GridRow = 3, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.NEXT, "PgDn") },
                                      new OnScreenKey { GridRow = 4, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.LEFT, "Left") },
                                      new OnScreenKey { GridRow = 4, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.DOWN, "Down") },
                                      new OnScreenKey { GridRow = 4, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.RIGHT, "Right") },
                                  };

            specialSection.Keys = specialKeys;
            specialSection.SetValue(ColumnProperty, 1);
            _sections.Add(specialSection);
            ColumnDefinitions.Add(new ColumnDefinition());
            Children.Add(specialSection);

            _allLogicalKeys.AddRange(specialKeys.Select(x => x.Key));
            _allOnScreenKeys.AddRange(specialSection.Keys);
            */


            _modifierKeys.AddRange(_allLogicalKeys.OfType<ModifierKeyBase>());
            _allOnScreenKeys.ForEach(x => x.OnScreenKeyPress += OnScreenKeyPress);

            SynchroniseModifierKeyState();

            base.BeginInit();
        }

        void OnScreenKeyPress(DependencyObject sender, OnScreenKeyEventArgs e)
        {
            if (e.OnScreenKey.Key is ModifierKeyBase)
            {
                var modifierKey = (ModifierKeyBase)e.OnScreenKey.Key;
                if (modifierKey.KeyCode == VirtualKeyCode.SHIFT)
                {
                    HandleShiftKeyPressed(modifierKey);
                }
                else if (modifierKey.KeyCode == VirtualKeyCode.CAPITAL)
                {
                    HandleCapsLockKeyPressed(modifierKey);
                }
                else if (modifierKey.KeyCode == VirtualKeyCode.NUMLOCK)
                {
                    HandleNumLockKeyPressed(modifierKey);
                }
            }
            else
            {
                ResetInstantaneousModifierKeys();
            }
            _modifierKeys.OfType<InstantaneousModifierKey>().ToList().ForEach(x => x.SynchroniseKeyState());
        }

        private void SynchroniseModifierKeyState()
        {
            _modifierKeys.ToList().ForEach(x => x.SynchroniseKeyState());
        }

        private void ResetInstantaneousModifierKeys()
        {
            _modifierKeys.OfType<InstantaneousModifierKey>().ToList().ForEach(x => { if (x.IsInEffect) x.Press(); });
        }

        void HandleShiftKeyPressed(ModifierKeyBase shiftKey)
        {
            _allLogicalKeys.OfType<CaseSensitiveKey>().ToList().ForEach(x => x.SelectedIndex =
                                                                             InputSimulator.IsTogglingKeyInEffect(VirtualKeyCode.CAPITAL) ^ shiftKey.IsInEffect ? 1 : 0);
            _allLogicalKeys.OfType<ShiftSensitiveKey>().ToList().ForEach(x => x.SelectedIndex = shiftKey.IsInEffect ? 1 : 0);
        }

        void HandleCapsLockKeyPressed(ModifierKeyBase capsLockKey)
        {
            _allLogicalKeys.OfType<CaseSensitiveKey>().ToList().ForEach(x => x.SelectedIndex =
                                                                             capsLockKey.IsInEffect ^ InputSimulator.IsKeyDownAsync(VirtualKeyCode.SHIFT) ? 1 : 0);
        }

        void HandleNumLockKeyPressed(ModifierKeyBase numLockKey)
        {
            _allLogicalKeys.OfType<NumLockSensitiveKey>().ToList().ForEach(x => x.SelectedIndex = numLockKey.IsInEffect ? 1 : 0);
        }
    }
}