using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;

namespace Valg.EVA.Skanning.UserInterfaceElements.FilterControls
{
    public class FilteredComboBox : ComboBox, INotifyPropertyChanged
    {
        public FilteredComboBox()
        {
            SetDefaultValues();

            Loaded += FilteredComboBox_Loaded;
            LostFocus += FilteredComboBox_LostFocus;
            GotFocus += FilteredComboBox_GotFocus;
            IsEnabledChanged += FilteredComboBox_IsEnabledChanged;
            DropDownClosed += FilteredComboBox_DropDownClosed;
        }

        void FilteredComboBox_DropDownClosed(object sender, System.EventArgs e)
        {
            if (!string.IsNullOrEmpty(Text))
            {
                SetSelectedSearchItem();
                if (SelectedSearchItem == null)
                {
                    Text = GetLastValidSelectionText();
                }
            }
        }

        public bool IsBound { get; set; }

        
        
        
        
        
        
        
        private TextBox EditableTextBox
        {
            get
            {
                return GetTemplateChild("PART_EditableTextBox") as TextBox;
            }
        }

        private ICollectionView FilterCollection { get; set; }

        
        
        
        private string LastFilterValue { get; set; }

        private ISearchItem LastSelectedItem { get; set; }

        private string OriginalTextValue { get; set; }

        protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
        {
            if (newValue != null)
            {
                CreateFilterCollection(newValue);
            }

            if (oldValue != null)
            {
                ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
                view.Filter -= this.FilterPredicate;
            }

            base.OnItemsSourceChanged(oldValue, newValue);
        }

        
        
        
        
        
        
        
        
        protected override void OnKeyUp(KeyEventArgs e)
        {
            if (this.Text != null)
            {
                if (this.Text != this.LastFilterValue)
                {
                    if (this.Text.Length == 0 || this.Text.Length >= this.MinimumSearchLength)
                    {
                        StoreCurrentCursorPosition();
                        this.RefreshFilterCollection();
                        this.IsDropDownOpen = true;
                        RestoreCurrentCursorPosition();
                    }
                }
            }

            base.OnKeyUp(e);

            OnPropertyChanged(null);
        }

        
        
        
        
        
        
        
        
        
        
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                
                this.IsDropDownOpen = false;
                this.SelectedIndex = -1;
                this.Text = GetLastValidSelectionText();
                this.RefreshFilterCollection();
            }
            else
            {
                if (e.Key == Key.Down)
                {
                    
                    this.IsDropDownOpen = true;
                }

                base.OnPreviewKeyDown(e);
            }

            this.LastFilterValue = this.Text;
            OnPropertyChanged(null);
        }

        private void CreateFilterCollection(IEnumerable newValue)
        {
            FilterCollection = CollectionViewSource.GetDefaultView(this.ItemsSource);
            FilterCollection.Filter += this.FilterPredicate;
            FilterCollection.SortDescriptions.Add(new SortDescription("SearchValue", ListSortDirection.Ascending));
            FilterCollection.CollectionChanged += FilterCollection_CollectionChanged;
        }

        private void FilterCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            
            if (this.SelectedIndex != -1 && this.IsDropDownOpen)
            {
                this.IsDropDownOpen = false;
                this.SelectedIndex = -1;
                this.IsDropDownOpen = true;
            }
        }

        private void FilteredComboBox_GotFocus(object sender, RoutedEventArgs e)
        {
            
            this.RefreshFilterCollection();
            this.IsDropDownOpen = true;

            
            if (this.EditableTextBox != null)
            {
                if (this.Text != null && !this.Text.Contains('*'))
                {
                    this.EditableTextBox.SelectionStart = this.Text.Length;
                }
                else
                {
                    this.EditableTextBox.SelectionStart = 0;
                }
            }
        }

        private void FilteredComboBox_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.IsDropDownOpen = false;
            ToggleTextMaskOnEnabledStatus();
        }

        private void ToggleTextMaskOnEnabledStatus()
        {
            if (EditableTextBox != null)
            {
                
                if (this.IsEnabled)
                {
                    var textValue = string.IsNullOrWhiteSpace(LastFilterValue) ? OriginalTextValue : LastFilterValue;
                    EditableTextBox.Text = textValue;
                    this.Text = textValue;
                    SetSelectedSearchItem();
                }
                else
                {
                    ClearSelectedSeachItem();
                }
            }
        }

        private void FilteredComboBox_Loaded(object sender, RoutedEventArgs e)
        {
            OriginalTextValue = this.Text;
            SetSelectedSearchItem();
            RefreshFilterCollection();

            
            if (this.IsFocused && this.EditableTextBox != null)
            {
                this.EditableTextBox.Focus();
            }

            ToggleTextMaskOnEnabledStatus();
        }

        private void FilteredComboBox_LostFocus(object sender, RoutedEventArgs e)
        {
            SetSelectedSearchItem();
            if (SelectedSearchItem == null)
            {
                this.Text = GetLastValidSelectionText();
            }
        }

        
        
        
        
        
        private bool FilterPredicate(object value)
        {
            var searchItem = value as ISearchItem;
            if (searchItem == null)
            {
                return false;
            }
            else if (ShouldBeHidden(searchItem))
            {
                return false;
            }
            else if (string.IsNullOrEmpty(this.Text))
            {
                return true;
            }
            else
            {
                if (IsBound)
                {
                    return TextSearch.IsCaseInsensitiveMatch(this.Text, searchItem.SearchValue);
                }
                else
                {
                    return false;
                }
            }
        }

        private string GetLastValidSelectionText()
        {
            if (this.IsEnabled)
            {
                if (SelectedSearchItem != null)
                {
                    return SelectedSearchItem.DisplayValue;
                }
                else if (LastSelectedItem != null)
                {
                    return LastSelectedItem.DisplayValue;
                }
                else
                {
                    return OriginalTextValue;
                }
            }
            else
            {
                return string.Empty;
            }
        }

        private void RefreshFilterCollection()
        {
            if (this.FilterCollection != null)
            {
                StoreTextTemporarily();

                FilterCollection.Refresh();

                RestoreText();
            }
        }

        private void SetDefaultValues()
        {
            
            IsTextSearchEnabled = false;
            IsEditable = true;
            IsReadOnly = false;
            IsBound = false;
        }

        private bool ShouldBeHidden(ISearchItem searchItem)
        {
            if (searchItem.Selected && searchItem != SelectedSearchItem)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private int _tempCursorPosition;

        private string _tempText;

        private void RestoreCurrentCursorPosition()
        {
            this.EditableTextBox.SelectionStart = _tempCursorPosition;
        }

        private void RestoreText()
        {
            this.Text = _tempText;
        }

        private void StoreCurrentCursorPosition()
        {
            _tempCursorPosition = this.EditableTextBox.SelectionStart;
        }

        private void StoreTextTemporarily()
        {
            _tempText = this.Text;
        }

        public static readonly DependencyProperty SelectedSearchItemProperty =
            DependencyProperty.Register(
                "SelectedSearchItem",
                typeof(ISearchItem),
                typeof(FilteredComboBox),
                new UIPropertyMetadata(null));

        [Category("Filtered ComboBox")]
        [DefaultValue(null)]
        public ISearchItem SelectedSearchItem
        {
            get
            {
                return (ISearchItem)this.GetValue(SelectedSearchItemProperty);
            }
            set
            {
                this.SetValue(SelectedSearchItemProperty, value);
                IsBound = true;
            }
        }

        private void SetSelectedSearchItem()
        {
            if (this.ItemsSource != null && this.IsEnabled)
            {
                if (SelectedSearchItem == null || SelectedSearchItem.DisplayValue != this.Text)
                {
                    SelectedSearchItem = this.ItemsSource.OfType<ISearchItem>().FirstOrDefault(p => p.DisplayValue == this.Text);
                    if (SelectedSearchItem != null)
                    {
                        if (LastSelectedItem != null)
                        {
                            LastSelectedItem.Selected = false;
                        }

                        SelectedSearchItem.Selected = true;
                        LastSelectedItem = SelectedSearchItem;
                    }
                }
            }
        }

        private void ClearSelectedSeachItem()
        {
            EditableTextBox.Text = string.Empty;
            this.Text = string.Empty;
            LastFilterValue = string.Empty;
            LastSelectedItem = null;
            OriginalTextValue = null;

            if (SelectedSearchItem != null)
            {
                SelectedSearchItem.Selected = false;
                SelectedSearchItem = null;
            }
        }

        
        
        
        
        
        
        
        public static readonly DependencyProperty ValidSearchItemsProperty =
            DependencyProperty.Register(
                "ValidSearchItems",
                typeof(IEnumerable<ISearchItem>),
                typeof(FilteredComboBox),
                new UIPropertyMetadata(null, OnValidSearchItemsChanged));

        
        
        
        
        [Description("All the possible values to search through")]
        [Category("Filtered ComboBox")]
        public IEnumerable<object> ValidSearchItems
        {
            get
            {
                return (IEnumerable<ISearchItem>)this.GetValue(ValidSearchItemsProperty);
            }
            set
            {
                this.SetValue(ValidSearchItemsProperty, value);
            }
        }

        private static void OnValidSearchItemsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var element = dependencyObject as FilteredComboBox;

            if (element != null)
            {
                var items = e.NewValue as IEnumerable<ISearchItem>;
                if (items != null)
                {
                    element.ItemsSource = items.ToArray();
                }
            }
        }

        
        
        
        
        
        
        public static readonly DependencyProperty MinimumSearchLengthProperty =
            DependencyProperty.Register(
                "MinimumSearchLength",
                typeof(int),
                typeof(FilteredComboBox),
                new UIPropertyMetadata(3));

        
        
        
        
        [Description("Length of the search string that triggers filtering.")]
        [Category("Filtered ComboBox")]
        [DefaultValue(2)]
        public int MinimumSearchLength
        {
            get
            {
                return (int)this.GetValue(MinimumSearchLengthProperty);
            }
            set
            {
                this.SetValue(MinimumSearchLengthProperty, value);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}
