using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Threading;

namespace eSuite.WorkBench.WpfHelper
{
    public class SelectableObservableCollection<T> : ObservableCollection<T>
    {
        private int _selectedIndex = -1;

        public T SelectedItem
        {
            get
            {
                if ((_selectedIndex > -1) && (this.Count > 0))
                {
                    return this[_selectedIndex];
                }

                return default(T);
            }
            set
            {
                if (((_selectedIndex == -1) && (value != null)) ||
                    ((this.Count > 0) && !EqualityComparer<T>.Default.Equals(this[_selectedIndex], value)))
                {
                    int index = IndexOf(value);
                    //if (index == -1)
                    //{
                    //    throw new InvalidOperationException("SelectedItem must be part of the collection");
                    //}
                    SetSelectedIndex(index);
                }
            }
        }

        public int SelectedIndex
        {
            get { return _selectedIndex; }
            set
            {
                if (_selectedIndex != value)
                {
                    if (value < -1 || value >= Count)
                    {
                        throw new ArgumentOutOfRangeException(string.Format("Selected index {0} can be in the range of -1 to {1}", _selectedIndex, Count - 1));
                    }
                    SetSelectedIndex(value);
                }
            }
        }

        private void SetSelectedIndex(int index)
        {
            if (_selectedIndex == index)
                return;

            int originalValue = _selectedIndex;
            bool cancelChange = OnSelectedItemChanging();
            _selectedIndex = index;

            if (cancelChange == false)
            {
                Application.Current.Dispatcher.BeginInvoke(
                    new Action(() =>
                    {
                        _selectedIndex = originalValue;
                        OnPropertyChanged(new PropertyChangedEventArgs("SelectedItem"));
                        OnPropertyChanged(new PropertyChangedEventArgs("SelectedIndex"));
                    }
                    ),
                    DispatcherPriority.ContextIdle,
                    null
                );
            }
            else
            {
                OnSelectedItemChanged();
                OnPropertyChanged(new PropertyChangedEventArgs("SelectedItem"));
                OnPropertyChanged(new PropertyChangedEventArgs("SelectedIndex"));
            }
        }


        public event CanChangeEventHandler SelectedItemChanging;
        public event EventHandler SelectedItemChanged;

        private bool OnSelectedItemChanging()
        {
            if (SelectedItemChanging != null)
            {
                CanChangeEventArgs canChangeEventArgs = new CanChangeEventArgs();
                SelectedItemChanging(this, canChangeEventArgs);
                return canChangeEventArgs.CanChange;
            }
            return true;
        }

        private void OnSelectedItemChanged()
        {
            if (SelectedItemChanged != null)
            {
                SelectedItemChanged(this, EventArgs.Empty);
            }
        }

        public new void Clear()
        {
            base.Clear();
            SelectedIndex = -1;
        }
    }
}
