using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace UniMarc
{
    public class SortableBindingList<T> : BindingList<T>, IBindingListView
    {
        private bool _isSorted;
        private ListSortDirection _sortDirection;
        private PropertyDescriptor _sortProperty;
        private string _filter;
        private readonly List<T> _originalItems = new List<T>();

        public SortableBindingList() : base() { }
        public SortableBindingList(IList<T> list) : base(list) 
        {
            foreach (var item in list)
                _originalItems.Add(item);
        }

        protected override void OnListChanged(ListChangedEventArgs e)
        {
            if (e.ListChangedType == ListChangedType.ItemAdded)
            {
                _originalItems.Insert(e.NewIndex, this[e.NewIndex]);
            }
            else if (e.ListChangedType == ListChangedType.ItemDeleted)
            {
                // This is slightly tricky if filtered, but we assume basic usage for now
                // Ideally we'd track IDs
            }
            base.OnListChanged(e);
        }

        #region Sorting
        protected override bool SupportsSortingCore => true;
        protected override bool IsSortedCore => _isSorted;
        protected override ListSortDirection SortDirectionCore => _sortDirection;
        protected override PropertyDescriptor SortPropertyCore => _sortProperty;

        protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
        {
            var items = this.Items as List<T>;
            if (items != null)
            {
                PropertyComparer<T> pc = new PropertyComparer<T>(prop, direction);
                items.Sort(pc);
                _isSorted = true;
                _sortDirection = direction;
                _sortProperty = prop;
            }
            else
            {
                _isSorted = false;
            }
            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        protected override void RemoveSortCore()
        {
            _isSorted = false;
            _sortProperty = null;
            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }
        #endregion

        #region Filtering (IBindingListView)
        public string Filter
        {
            get => _filter;
            set
            {
                _filter = value;
                if (string.IsNullOrEmpty(_filter))
                    RemoveFilter();
                else
                    ApplyFilter(_filter);
            }
        }

        public bool SupportsFiltering => true;

        public void ApplyFilter(string filter)
        {
            _filter = filter;
            if (_originalItems.Count == 0 && this.Count > 0)
            {
                foreach (var item in this) _originalItems.Add(item);
            }

            // Simple parser for "Property=Value" or "Property='Value'"
            var parts = filter.Split('=');
            if (parts.Length != 2) return;

            string propName = parts[0].Trim();
            string valStr = parts[1].Trim().Trim('\'');

            var prop = TypeDescriptor.GetProperties(typeof(T))[propName];
            if (prop == null) return;

            this.RaiseListChangedEvents = false;
            this.Clear();
            foreach (var item in _originalItems)
            {
                var val = prop.GetValue(item);
                if (val != null && val.ToString().Equals(valStr, StringComparison.OrdinalIgnoreCase))
                {
                    this.Add(item);
                }
            }
            this.RaiseListChangedEvents = true;
            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        public void RemoveFilter()
        {
            _filter = null;
            if (_originalItems.Count > 0)
            {
                this.RaiseListChangedEvents = false;
                this.Clear();
                foreach (var item in _originalItems) this.Add(item);
                this.RaiseListChangedEvents = true;
                OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
            }
        }

        public ListSortDescriptionCollection SortDescriptions => null;
        public bool SupportsAdvancedSorting => false;
        public void ApplySort(ListSortDescriptionCollection sorts) => throw new NotSupportedException();
        #endregion

        private class PropertyComparer<TItem> : IComparer<TItem>
        {
            private readonly PropertyDescriptor _prop;
            private readonly ListSortDirection _direction;

            public PropertyComparer(PropertyDescriptor prop, ListSortDirection direction)
            {
                _prop = prop;
                _direction = direction;
            }

            public int Compare(TItem x, TItem y)
            {
                object xVal = _prop.GetValue(x);
                object yVal = _prop.GetValue(y);

                int result;

                if (xVal == null && yVal == null) result = 0;
                else if (xVal == null) result = -1;
                else if (yVal == null) result = 1;
                else if (xVal is IComparable comparable)
                    result = comparable.CompareTo(yVal);
                else if (xVal.Equals(yVal))
                    result = 0;
                else
                    result = xVal.ToString().CompareTo(yVal.ToString());

                return _direction == ListSortDirection.Ascending ? result : -result;
            }
        }
    }
}
