﻿using System;
using System.Collections.Generic;

namespace JLGames.RocketDriver.Games.RpgMaterial.Common
{
    public class ElementSet : IElementSet
    {
        protected readonly List<IElement> m_DataList;
        private readonly List<IElement> m_Temp = new List<IElement>();

        public int Size => m_DataList.Count;

        public ElementSet()
        {
            m_DataList = new List<IElement>();
        }

        public ElementSet(int capacity)
        {
            m_DataList = new List<IElement>(capacity);
        }

        public bool Exist(string eKey)
        {
            return m_DataList.FindIndex(o => o.EKey == eKey) >= 0;
        }

        public IElement GetElement(string eKey)
        {
            return m_DataList.Find((element => eKey == element.EKey));
        }

        public TAs GetElement<TAs>(string eKey) where TAs : IElement
        {
            return ToAs<TAs>(GetElement(eKey));
        }

        public TAs GetElement<TAs>(Predicate<TAs> match) where TAs : IElement
        {
            var rs = m_DataList.Find(element => match.Invoke((TAs) element));
            return ToAs<TAs>(rs);
        }

        public TAs[] GetElements<TAs>(string[] eKeyArr) where TAs : IElement
        {
            if (null == eKeyArr || eKeyArr.Length == 0) return null;
            var count = eKeyArr.Length;
            var rs = new TAs[count];
            for (var index = 0; index < count; index++)
            {
                rs[index] = GetElement<TAs>(eKeyArr[index]);
            }

            return rs;
        }

        public TAs[] GetElements<TAs>(IEnumerable<string> eKeyArr) where TAs : IElement
        {
            if (null == eKeyArr) return null;
            var list = new List<TAs>();
            foreach (var eId in eKeyArr)
            {
                list.Add(GetElement<TAs>(eId));
            }

            return list.ToArray();
        }

        public TAs[] GetElements<TAs>(Predicate<TAs> match) where TAs : IElement
        {
            var list = new List<TAs>();
            m_DataList.ForEach(o =>
            {
                var e = (TAs) o;
                if (match.Invoke(e)) list.Add(e);
            });

            return list.ToArray();
        }

        public TAs[] GetElements<TAs>() where TAs : IElement
        {
            if (m_DataList.Count == 0) return null;
            var list = new List<TAs>();
            m_DataList.ForEach(element => { list.Add((TAs) element); });
            return list.ToArray();
        }

        public void ForeachElement<TAs>(Action<TAs> each) where TAs : IElement
        {
            if (m_DataList.Count == 0) return;
            m_DataList.ForEach((element => { each.Invoke((TAs) element); }));
        }

        public IElement RemoveElement(string eKey)
        {
            return RemoveEle<IElement>(eKey);
        }

        public IElement[] RemoveElements(string[] eKeyArr)
        {
            if (null == eKeyArr || eKeyArr.Length == 0 || m_DataList.Count == 0) return null;
            var count = eKeyArr.Length;
            var rs = new IElement[count];
            for (var index = 0; index < count; index++)
            {
                rs[index] = RemoveEle<IElement>(eKeyArr[index]);
            }

            return rs;
        }

        public IElement[] RemoveElements(IEnumerable<string> eKeyArr)
        {
            if (null == eKeyArr || m_DataList.Count == 0) return null;
            m_Temp.Clear();
            foreach (var eId in eKeyArr)
            {
                m_Temp.Add(RemoveEle<IElement>(eId));
            }

            return m_Temp.ToArray();
        }

        public IElement[] RemoveElements(Predicate<IElement> match)
        {
            if (m_DataList.Count == 0) return null;
            m_Temp.Clear();
            for (var index = m_DataList.Count - 1; index >= 0; index--)
            {
                if (match.Invoke(m_DataList[index]))
                {
                    var rs = m_DataList[index];
                    m_DataList.RemoveAt(index);
                    m_Temp.Add(rs);
                }
            }

            m_Temp.Reverse();
            return m_Temp.ToArray();
        }

        public IElement[] RemoveElements()
        {
            var rs = m_DataList.ToArray();
            m_DataList.Clear();
            return rs;
        }

        public void AddElement(IElement ele)
        {
            if (null == ele) return;
            if (Exist(ele.EKey)) return;
            m_DataList.Add(ele);
        }

        public void AddElements<TAs>(IEnumerable<TAs> eleArr) where TAs : IElement
        {
            foreach (var o in eleArr)
            {
                AddElement(o);
            }
        }

        public void ReplaceElement(IElement ele)
        {
            if (null == ele) return;
            var index = m_DataList.FindIndex(o => o.EKey == ele.EKey);
            if (index >= 0)
            {
                m_DataList[index] = ele;
            }
            else
            {
                m_DataList.Add(ele);
            }
        }

        public void ReplaceElements<TAs>(IEnumerable<TAs> eleArr) where TAs : IElement
        {
            foreach (var o in eleArr)
            {
                ReplaceElement(o);
            }
        }

        public void SortElements(IComparer<IElement> comparer)
        {
            m_DataList.Sort(comparer);
        }

        public void SortElements(Comparison<IElement> comparison)
        {
            m_DataList.Sort(comparison);
        }

        private TAs ToAs<TAs>(IElement o) where TAs : IElement
        {
            if (o is TAs) return (TAs) o;
            return default(TAs);
        }

        private TAs RemoveEle<TAs>(string eKey) where TAs : IElement
        {
            var count = m_DataList.Count;
            for (var index = 0; index < count; index++)
            {
                if (m_DataList[index].EKey == eKey)
                {
                    var rs = m_DataList[index];
                    m_DataList.RemoveAt(index);
                    return (TAs) rs;
                }
            }

            return default(TAs);
        }
    }


    public class ElementSet<T> : IElementSet<T> where T : IElement
    {
        protected readonly List<T> m_DataList;
        private readonly List<T> m_Temp = new List<T>();
        private readonly List<IElement> m_TempBase = new List<IElement>();

        public int Size => m_DataList.Count;

        public ElementSet()
        {
            m_DataList = new List<T>();
        }

        public ElementSet(int capacity)
        {
            m_DataList = new List<T>(capacity);
        }

        //IElementSet

        public bool Exist(string eKey)
        {
            return m_DataList.FindIndex(o => o.EKey == eKey) >= 0;
        }

        public IElement GetElement(string eKey)
        {
            return Get(eKey);
        }

        public TAs GetElement<TAs>(string eKey) where TAs : IElement
        {
            var rs = Get(eKey);
            if (null == rs || !(rs is TAs)) return default(TAs);

            return ToAs<TAs>(rs);
        }

        public TAs GetElement<TAs>(Predicate<TAs> match) where TAs : IElement
        {
            if (null == match || m_DataList.Count == 0) return default(TAs);
            foreach (var o in m_DataList)
            {
                var oAs = ToAs<TAs>(o);
                if (match.Invoke(oAs)) return oAs;
            }

            return default(TAs);
        }

        public TAs[] GetElements<TAs>(string[] eKeyArr) where TAs : IElement
        {
            var rs = GetRange(eKeyArr);
            if (null == rs || rs.Length == 0) return null;
            var rsAs = new TAs[rs.Length];
            for (var index = rs.Length - 1; index >= 0; index--)
            {
                rsAs[index] = ToAs<TAs>(rs[index]);
            }

            return rsAs;
        }

        public TAs[] GetElements<TAs>(IEnumerable<string> eKeyArr) where TAs : IElement
        {
            var rs = GetRange(eKeyArr);
            if (null == rs || rs.Length == 0) return null;
            var rsAs = new TAs[rs.Length];
            for (var index = rs.Length - 1; index >= 0; index--)
            {
                rsAs[index] = ToAs<TAs>(rs[index]);
            }

            return rsAs;
        }

        public TAs[] GetElements<TAs>(Predicate<TAs> match) where TAs : IElement
        {
            if (null == match) return null;
            var list = new List<TAs>();
            m_DataList.ForEach(o =>
            {
                var oAs = ToAs<TAs>(o);
                if (null == oAs) return;
                if (match.Invoke(oAs)) list.Add(oAs);
            });

            return list.ToArray();
        }

        public TAs[] GetElements<TAs>() where TAs : IElement
        {
            if (0 == m_DataList.Count) return null;
            var rs = m_DataList.ToArray();
            var rsAs = new TAs[rs.Length];
            for (var index = rs.Length - 1; index >= 0; index--)
            {
                rsAs[index] = ToAs<TAs>(rs[index]);
            }

            return rsAs;
        }

        public void ForeachElement<TAs>(Action<TAs> each) where TAs : IElement
        {
            m_DataList.ForEach(o => { each.Invoke(ToAs<TAs>(o)); });
        }

        public IElement RemoveElement(string eKey)
        {
            return Remove(eKey);
        }

        public IElement[] RemoveElements(string[] eKeyArr)
        {
            if (null == eKeyArr || eKeyArr.Length == 0 || m_DataList.Count == 0) return null;
            var count = eKeyArr.Length;
            var rs = new IElement[count];
            for (var index = 0; index < count; index++)
            {
                rs[index] = RemoveEle(eKeyArr[index]);
            }

            return rs;
        }

        public IElement[] RemoveElements(IEnumerable<string> eKeyArr)
        {
            if (null == eKeyArr || m_DataList.Count == 0) return null;
            m_TempBase.Clear();
            foreach (var eId in eKeyArr)
            {
                m_TempBase.Add(RemoveEle(eId));
            }

            return m_TempBase.ToArray();
        }

        public IElement[] RemoveElements(Predicate<IElement> match)
        {
            if (null == match || m_DataList.Count == 0) return null;
            m_TempBase.Clear();
            for (var index = m_DataList.Count - 1; index >= 0; index--)
            {
                if (match.Invoke(m_DataList[index]))
                {
                    var rs = m_DataList[index];
                    m_DataList.RemoveAt(index);
                    m_TempBase.Add(rs);
                }
            }

            m_TempBase.Reverse();
            return m_TempBase.ToArray();
        }

        public IElement[] RemoveElements()
        {
            var old = m_DataList.ToArray();
            m_DataList.Clear();
            var rs = new IElement[old.Length];
            for (var index = old.Length - 1; index >= 0; index--)
            {
                rs[index] = old[index];
            }

            return rs;
        }

        public void AddElement(IElement ele)
        {
            if (ele is T) Add((T) ele);
        }

        public void AddElements<TAs>(IEnumerable<TAs> eleArr) where TAs : IElement
        {
            foreach (var o in eleArr)
            {
                AddElement(o);
            }
        }

        public void ReplaceElement(IElement ele)
        {
            if (!(ele is T)) return;
            var index = m_DataList.FindIndex(o => o.EKey == ele.EKey);
            if (index >= 0)
            {
                m_DataList[index] = (T) ele;
            }
            else
            {
                m_DataList.Add((T) ele);
            }
        }

        public void ReplaceElements<TAs>(IEnumerable<TAs> eleArr) where TAs : IElement
        {
            foreach (var e in eleArr)
            {
                ReplaceElement(e);
            }
        }

        public void SortElements(IComparer<IElement> comparer)
        {
            m_DataList.Sort((x, y) => comparer.Compare(x, y));
        }

        public void SortElements(Comparison<IElement> comparison)
        {
            m_DataList.Sort((x, y) => comparison.Invoke(x, y));
        }

        //IElementSet<T>

        public T this[int index]
        {
            get { return m_DataList[index]; }
            set { m_DataList[index] = value; }
        }

        public T Get(string eKey)
        {
            return m_DataList.Find(obj => obj.EKey == eKey);
        }

        public T Get(Predicate<T> match)
        {
            return m_DataList.Find(match);
        }

        public T[] GetRange(string[] eKeyArr)
        {
            if (null == eKeyArr || eKeyArr.Length == 0) return null;
            var count = eKeyArr.Length;
            var rs = new T[count];
            for (var index = 0; index < count; index++)
            {
                rs[index] = Get(eKeyArr[index]);
            }

            return rs;
        }

        public T[] GetRange(IEnumerable<string> eKeyArr)
        {
            if (null == eKeyArr) return null;
            m_Temp.Clear();
            foreach (var eId in eKeyArr)
            {
                m_Temp.Add(Get(eId));
            }

            return m_Temp.ToArray();
        }

        public T[] GetRange(Predicate<T> match)
        {
            if (null == match) return null;
            m_Temp.Clear();
            m_DataList.ForEach(o =>
            {
                if (match.Invoke(o)) m_Temp.Add(o);
            });

            return m_Temp.ToArray();
        }

        public T[] GetAll()
        {
            return 0 == m_DataList.Count ? null : m_DataList.ToArray();
        }

        public TAs[] GetAllAs<TAs>() where TAs : T
        {
            if (0 == m_DataList.Count) return null;
            var rs = m_DataList.ToArray();
            var rsAs = new TAs[rs.Length];
            for (var index = rs.Length - 1; index >= 0; index--)
            {
                rsAs[index] = ToAs<TAs>(rs[index]);
            }

            return rsAs;
        }

        public void Foreach(Action<T> each)
        {
            m_DataList.ForEach(each);
        }

        public void Foreach<TAs>(Action<TAs> each) where TAs : T
        {
            m_DataList.ForEach(o => { each.Invoke(ToAs<TAs>(o)); });
        }

        public T Remove(string eKey)
        {
            var count = m_DataList.Count;
            return 0 == count ? default(T) : RemoveEle(eKey);
        }

        public T[] RemoveRange(string[] eKeyArr)
        {
            if (null == eKeyArr || eKeyArr.Length == 0 || m_DataList.Count == 0) return null;
            var count = eKeyArr.Length;
            var rs = new T[count];
            for (var index = 0; index < count; index++)
            {
                rs[index] = RemoveEle(eKeyArr[index]);
            }

            return rs;
        }

        public T[] RemoveRange(IEnumerable<string> eKeyArr)
        {
            if (null == eKeyArr || m_DataList.Count == 0) return null;
            m_Temp.Clear();
            foreach (var eId in eKeyArr)
            {
                m_Temp.Add(RemoveEle(eId));
            }

            return m_Temp.ToArray();
        }

        public T[] RemoveRange(Predicate<T> match)
        {
            if (null == match || m_DataList.Count == 0) return null;
            m_Temp.Clear();
            for (var index = m_DataList.Count - 1; index >= 0; index--)
            {
                if (match.Invoke(m_DataList[index]))
                {
                    var rs = m_DataList[index];
                    m_DataList.RemoveAt(index);
                    m_Temp.Add(rs);
                }
            }

            m_Temp.Reverse();
            return m_Temp.ToArray();
        }

        public T[] RemoveAll()
        {
            var rs = m_DataList.ToArray();
            m_DataList.Clear();
            return rs;
        }

        public void Add(T ele)
        {
            if (null == ele) return;
            if (Exist(ele.EKey)) return;
            m_DataList.Add(ele);
        }

        public void AddRange(IEnumerable<T> eleArr)
        {
            foreach (var o in eleArr)
            {
                Add(o);
            }
        }

        public void Replace(T ele)
        {
            if (null == ele) return;
            var index = m_DataList.FindIndex(o => o.EKey == ele.EKey);
            if (index >= 0)
            {
                m_DataList[index] = ele;
            }
            else
            {
                m_DataList.Add(ele);
            }
        }

        public void ReplaceRange(IEnumerable<T> eleArr)
        {
            foreach (var o in eleArr)
            {
                Replace(o);
            }
        }

        public void Sort(IComparer<T> comparer)
        {
            m_DataList.Sort(comparer);
        }

        public void Sort<TAs>(IComparer<TAs> comparer) where TAs : T
        {
            m_DataList.Sort((x, y) => comparer.Compare((TAs) x, (TAs) y));
        }

        public void Sort(Comparison<T> comparison)
        {
            m_DataList.Sort(comparison);
        }

        public void Sort<TAs>(Comparison<TAs> comparison) where TAs : T
        {
            m_DataList.Sort((x, y) => comparison.Invoke((TAs) x, (TAs) y));
        }

        private TAs ToAs<TAs>(T o) where TAs : IElement
        {
            if (o is TAs) return (TAs) (IElement) o;
            return default(TAs);
        }

        private T RemoveEle(string eKey)
        {
            var count = m_DataList.Count;
            for (var index = 0; index < count; index++)
            {
                if (m_DataList[index].EKey == eKey)
                {
                    var rs = m_DataList[index];
                    m_DataList.RemoveAt(index);
                    return rs;
                }
            }

            return default(T);
        }
    }
}