// COPYRIGHT (C) Tom. ALL RIGHTS RESERVED.
// THE AntdUI PROJECT IS AN WINFORM LIBRARY LICENSED UNDER THE Apache-2.0 License.
// LICENSED UNDER THE Apache License, VERSION 2.0 (THE "License")
// YOU MAY NOT USE THIS FILE EXCEPT IN COMPLIANCE WITH THE License.
// YOU MAY OBTAIN A COPY OF THE LICENSE AT
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, SOFTWARE
// DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
// SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS AND
// LIMITATIONS UNDER THE License.
// GITCODE: https://gitcode.com/AntdUI/AntdUI
// GITEE: https://gitee.com/AntdUI/AntdUI
// GITHUB: https://github.com/AntdUI/AntdUI
// CSDN: https://blog.csdn.net/v_132
// QQ: 17379620

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;

namespace AntdUI
{
    public class iCollection<T> : IList<T>, IList
    {
        #region 刷新UI

        public Action<bool>? action;
        public Action<T>? action_add;
        public Action<T, int>? action_del;
        void PropertyChanged(T value)
        {
            if (value is NotifyProperty notify)
            {
                notify.PropertyChanged += (a, b) =>
                {
                    action?.Invoke(false);
                };
            }
        }

        #endregion

        List<T> list;
        public iCollection() { list = new List<T>(); }
        public iCollection(int capacity) { list = new List<T>(capacity); }
        public iCollection(IEnumerable<T> collection) { list = new List<T>(collection); }

        public T this[int index]
        {
            get => list[index];
            set
            {
                if (action_add == null && action_del == null) list[index] = value;
                else
                {
                    action_del?.Invoke(list[index], index);
                    list[index] = value;
                    action_add?.Invoke(value);
                }
                PropertyChanged(value);

            }
        }
        object? IList.this[int index]
        {
            get => list[index];
            set
            {
                if (value is T item)
                {
                    if (action_add == null && action_del == null) list[index] = item;
                    else
                    {
                        action_del?.Invoke(list[index], index);
                        list[index] = item;
                        action_add?.Invoke(item);
                    }
                    PropertyChanged(item);
                }
            }
        }

        #region 原生

        #region 列表

        public IEnumerator<T> GetEnumerator()
        {
            for (int i = 0; i < list.Count; i++) yield return list[i];
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            for (int i = 0; i < list.Count; i++) yield return list[i];
        }
        public T[] ToArray() => list.ToArray();
        public void ForEach(Action<T> action) => list.ForEach(action);
        public bool TrueForAll(Predicate<T> match) => list.TrueForAll(match);
        public T[] Safe => list.ToArray();

        #endregion

        public int Count => list.Count;

        public bool IsReadOnly => false;

        public bool IsFixedSize => false;

        public bool IsSynchronized => true;

        public object SyncRoot => this;

        #region 添加

        public virtual void Add(T item)
        {
            list.Add(item);
            PropertyChanged(item);
            action_add?.Invoke(item);
            action?.Invoke(true);
        }
        public virtual int Add(object? value)
        {
            if (value is T item)
            {
                list.Add(item);
                PropertyChanged(item);
                action_add?.Invoke(item);
                action?.Invoke(true);
            }
            return list.Count;
        }

        public void AddRange(IEnumerable<T> collection)
        {
            list.AddRange(collection);
            foreach (var item in collection)
            {
                PropertyChanged(item);
                action_add?.Invoke(item);
            }
            action?.Invoke(true);
        }

        public void Insert(int index, T item)
        {
            list.Insert(index, item);
            PropertyChanged(item);
            action_add?.Invoke(item);
            action?.Invoke(true);
        }
        public void InsertAntRemove(int index, T item)
        {
            list.Remove(item);
            list.Insert(index, item);
            PropertyChanged(item);
            action?.Invoke(true);
        }
        public void Insert(int index, object? value)
        {
            if (value is T item)
            {
                list.Insert(index, item);
                action_add?.Invoke(item);
            }
        }
        public void InsertRange(int index, IEnumerable<T> collection)
        {
            list.InsertRange(index, collection);
            foreach (var item in collection)
            {
                PropertyChanged(item);
                action_add?.Invoke(item);
            }
            action?.Invoke(true);
        }

        #endregion

        #region 判断/查找

        public bool Contains(T item) => list.Contains(item);
        public bool Contains(object? value)
        {
            if (value is T item) return list.Contains(item);
            return false;
        }

        public int IndexOf(object? value)
        {
            if (value is T item) return list.IndexOf(item);
            return -1;
        }
        public int IndexOf(T item) => list.IndexOf(item);
        public int IndexOf(T item, int index) => list.IndexOf(item, index);
        public int IndexOf(T item, int index, int count) => list.IndexOf(item, index, count);

        public int LastIndexOf(T item) => list.LastIndexOf(item);
        public int LastIndexOf(T item, int index) => list.LastIndexOf(item, index);
        public int LastIndexOf(T item, int index, int count) => list.LastIndexOf(item, index, count);

        public bool Exists(Predicate<T> match) => list.Exists(match);

        public T? Find(Predicate<T> match) => list.Find(match);
        public List<T> FindAll(Predicate<T> match) => list.FindAll(match);
        public int FindIndex(Predicate<T> match) => list.FindIndex(match);
        public int FindIndex(int startIndex, Predicate<T> match) => list.FindIndex(startIndex, match);
        public int FindIndex(int startIndex, int count, Predicate<T> match) => list.FindIndex(startIndex, count, match);

        public T? FindLast(Predicate<T> match) => list.FindLast(match);
        public int FindLastIndex(Predicate<T> match) => list.FindLastIndex(match);
        public int FindLastIndex(int startIndex, Predicate<T> match) => list.FindLastIndex(startIndex, match);
        public int FindLastIndex(int startIndex, int count, Predicate<T> match) => list.FindLastIndex(startIndex, count, match);

        public List<T> GetRange(int index, int count) => list.GetRange(index, count);

        #endregion

        #region 删除

        public void Clear()
        {
            if (action_del != null)
            {
                foreach (var item in list) action_del?.Invoke(item, -1);
            }
            list.Clear();
            action?.Invoke(true);
        }

        public void Remove(object? value)
        {
            if (value is T item)
            {
                int i = IndexOf(item);
                list.Remove(item);
                action_del?.Invoke(item, i);
                action?.Invoke(true);
            }
        }
        public bool Remove(T item)
        {
            int i = IndexOf(item);
            bool flag = list.Remove(item);
            if (flag)
            {
                action_del?.Invoke(item, i);
                action?.Invoke(true);
            }
            return flag;
        }
        public void RemoveAt(int index)
        {
            if (action_del != null)
            {
                try
                {
                    action_del?.Invoke(list[index], index);
                }
                catch { }
            }
            list.RemoveAt(index);
            action?.Invoke(true);
        }
        public int RemoveAll(Predicate<T> match)
        {
            int i = list.RemoveAll(match);
            action?.Invoke(true);
            return i;
        }
        public void RemoveRange(int index, int count)
        {
            list.RemoveRange(index, count);
            action?.Invoke(true);
        }

        #endregion

        public void Reverse() => list.Reverse();
        public void Reverse(int index, int count) => list.Reverse(index, count);

        public void Sort() => list.Sort();
        public void Sort(IComparer<T> comparer) => list.Sort(comparer);
        public void Sort(Comparison<T> comparison) => list.Sort(comparison);
        public void Sort(int index, int count, IComparer<T> comparer) => list.Sort(index, count, comparer);

        public void CopyTo(T[] array) => list.CopyTo(array);
        public void CopyTo(T[] array, int arrayIndex) => list.CopyTo(array, arrayIndex);
        public void CopyTo(int index, T[] array, int arrayIndex, int count) => list.CopyTo(index, array, arrayIndex, count);

        public int BinarySearch(T item) => list.BinarySearch(item);
        public int BinarySearch(T item, IComparer<T> comparer) => list.BinarySearch(item, comparer);
        public int BinarySearch(int index, int count, T item, IComparer<T> comparer) => list.BinarySearch(index, count, item, comparer);


        public void CopyTo(Array array, int index)
        {
            //list.CopyTo(array, index);
        }

        #endregion
    }

    public class BaseCollection : IList
    {
        public BaseCollection() { }
        public BaseCollection(int capacity) { EnsureSpace(capacity); }
        public BaseCollection(IEnumerable<object> collection)
        {
            Add(collection);
        }

        public Action<bool>? action;
        void PropertyChanged(object value)
        {
            if (value is NotifyProperty notify)
            {
                notify.PropertyChanged += (a, b) =>
                {
                    action?.Invoke(false);
                };
            }
        }

        public virtual object? this[int index]
        {
            get => get(index);
            set => set(index, value);
        }

        object? get(int index)
        {
            if (list == null || index < 0 || index >= count) return default;
            return list[index];
        }
        void set(int index, object? value)
        {
            if (value == null || list == null || index < 0 || index >= count) return;
            list[index] = value;
            PropertyChanged(value);
        }

        public int Add(object? value)
        {
            if (value == null) return -1;
            EnsureSpace(1)[count++] = value;
            PropertyChanged(value);
            action?.Invoke(true);
            return IndexOf(value);
        }

        public void AddRange(object[] items)
        {
            var m_arrItem = EnsureSpace(items.Length);
            foreach (var item in items)
            {
                m_arrItem[count++] = item;
                PropertyChanged(item);
            }
            action?.Invoke(true);
        }
        public void AddRange(IList<object> items)
        {
            var m_arrItem = EnsureSpace(items.Count);
            foreach (var item in items)
            {
                m_arrItem[count++] = item;
                PropertyChanged(item);
            }
            action?.Invoke(true);
        }

        public void Clear()
        {
            count = 0;
            list = null;
            action?.Invoke(true);
        }

        public bool Contains(object? value)
        {
            if (value == null) return false;
            return IndexOf(value) != -1;
        }

        public void CopyTo(Array array, int index)
        {
            if (list == null) return;
            Array.Copy(list, 0, array, index, count);
        }

        public IEnumerator GetEnumerator()
        {
            for (int i = 0, Len = count; i < Len; i++)
                yield return list![i];
        }

        public int IndexOf(object? value)
        {
            if (list == null || value == null) return -1;
            return Array.IndexOf(list, value);
        }

        public void Insert(int index, object? value)
        {
            if (value == null || index < 0 || (index >= count && count > 0)) return;
            var m_arrItem = EnsureSpace(1);
            for (int i = count; i > index; i--) m_arrItem[i] = m_arrItem[i - 1];
            m_arrItem[index] = value;
            count++;
            PropertyChanged(value);
            action?.Invoke(true);
        }

        public void Remove(object? value)
        {
            int index = IndexOf(value);
            if (index > -1) RemoveAt(index);
        }

        public void RemoveAt(int index)
        {
            if (list == null || index < 0 || index >= count) return;
            count--;
            for (int i = index, Len = count; i < Len; i++)
                list[i] = list[i + 1];
            action?.Invoke(true);
        }

        #region 核心

        object[]? list;
        object[] EnsureSpace(int elements)
        {
            if (list == null) list = new object[Math.Max(elements, 4)];
            else if (count + elements > list.Length)
            {
                var arrTemp = new object[Math.Max(count + elements, list.Length * 2)];
                list.CopyTo(arrTemp, 0);
                list = arrTemp;
            }
            return list;
        }

        public bool IsFixedSize => false;

        public bool IsReadOnly => false;

        public int Count => count;

        public bool IsSynchronized => true;

        public object SyncRoot => this;


        int count = 0;

        #endregion

        public object[] ToArray() => list ?? new object[0];

        public T[] ToArray<T>()
        {
            if (list == null || list.Length == 0) return new T[0];
            var result = new List<T>(count);
            foreach (var it in list)
            {
                if (it is T t) result.Add(t);
            }
            return result.ToArray();
        }

        public List<object> ToList()
        {
            if (list == null || list.Length == 0) return new List<object>(0);
            var result = new List<object>(count);
            result.AddRange(list);
            return result;
        }

        public List<T> ToList<T>()
        {
            if (list == null || list.Length == 0) return new List<T>(0);
            var result = new List<T>(count);
            foreach (var it in list)
            {
                if (it is T t) result.Add(t);
            }
            return result;
        }
    }

    public class AntList<T> : IList<T>
    {
        public AntList() { }

        public AntList(int capacity)
        {
            EnsureSpace(capacity);
        }

        public AntList(IList<T> collection)
        {
            EnsureSpace(collection.Count);
            AddRange(collection);
        }

        #region 通知

        public Action<string, object>? action;

        #endregion

        public T this[int index]
        {
            get
            {
                if (list == null || index < 0 || index >= count) throw new Exception("Null List");
                return list[index];
            }
            set
            {
                if (value == null || list == null || index < 0 || index >= count) return;
                list[index] = value;
                action?.Invoke("edit", index);
            }
        }

        #region 添加

        public void Add(T item)
        {
            if (item == null) return;
            int index = count++;
            EnsureSpace(1)[index] = item;
            action?.Invoke("add", index);
        }

        public void AddRange(T[] items)
        {
            var m_arrItem = EnsureSpace(items.Length);
            var list = new List<int>(items.Length);
            foreach (var item in items)
            {
                int index = count++;
                list.Add(index);
                m_arrItem[index] = item;
            }
            action?.Invoke("add", list.ToArray());
        }

        public void AddRange(IList<T> items)
        {
            var m_arrItem = EnsureSpace(items.Count);
            var list = new List<int>(items.Count);
            foreach (var item in items)
            {
                int index = count++;
                list.Add(index);
                m_arrItem[index] = item;
            }
            action?.Invoke("add", list.ToArray());
        }

        #endregion

        public void Clear()
        {
            count = 0;
            list = null;
            action?.Invoke("del", "all");
        }

        public bool Contains(T item)
        {
            if (item == null) return false;
            return IndexOf(item) != -1;
        }

        public void CopyTo(T[] array, int index)
        {
            if (list == null) return;
            Array.Copy(list, 0, array, index, count);
        }

        public int IndexOf(T item)
        {
            if (list == null || item == null) return -1;
            return Array.IndexOf(list, item);
        }

        public void Insert(int index, T item)
        {
            if (item == null || index < 0 || (index >= count && count > 0)) return;
            var m_arrItem = EnsureSpace(1);
            for (int i = count; i > index; i--) m_arrItem[i] = m_arrItem[i - 1];
            m_arrItem[index] = item;
            count++;
            action?.Invoke("add", index);
        }

        public bool Remove(T item)
        {
            int index = IndexOf(item);
            if (index > -1) { RemoveAt(index); return true; }
            return false;
        }

        public void RemoveAt(int index)
        {
            if (list == null || index < 0 || index >= count) return;
            count--;
            for (int i = index, Len = count; i < Len; i++) list[i] = list[i + 1];
            action?.Invoke("del", index);
        }

        public IEnumerator<T> GetEnumerator()
        {
            for (int i = 0, Len = count; i < Len; i++)
                yield return list![i];
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            for (int i = 0, Len = count; i < Len; i++)
                yield return list![i];
        }

        public int Count => count;

        public bool IsReadOnly => false;

        #region 核心

        int count = 0;
        T[]? list;
        T[] EnsureSpace(int elements)
        {
            if (list == null) list = new T[Math.Max(elements, 4)];
            else if (count + elements > list.Length)
            {
                var arrTemp = new T[Math.Max(count + elements, list.Length * 2)];
                list.CopyTo(arrTemp, 0);
                list = arrTemp;
            }
            return list;
        }

        #endregion
    }

    public class AntItem : NotifyProperty
    {
        public AntItem(string k)
        {
            key = k;
        }
        public AntItem(string k, object? v)
        {
            key = k;
            value = v;
        }
        public string key { get; set; }

        object? _value;
        public object? value
        {
            get => _value;
            set
            {
                if (_value == value) return;
                _value = value;
                OnPropertyChanged(key);
            }
        }

#if NET40 || NET46 || NET48
        public bool Try<T>(out T? val)
#else
        public bool Try<T>([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T? val)
#endif
        {
            if (_value is T v) { val = v; return true; }
            val = default;
            return false;
        }

        public override string? ToString() => _value?.ToString();
    }

    public class NotifyProperty : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;
#if NET40
        public void OnPropertyChanged(string propertyName)
#else
        public void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
#endif
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}