﻿using System;
using System.Collections.Generic;
using JLGames.RocketDriver.Games.RpgMaterial.Common;
using JLGames.RocketDriver.Games.RpgMaterial.Material;
using JLGames.RocketDriver.Games.RpgMaterial.Service;

namespace JLGames.RocketDriver.Games.RpgMaterial.User
{
    public class UserMaterialSet<TU, TM, TCfg> : IUserMaterialSet<TU, TM, TCfg>, IUserMaterialSetMod<TU, TM, TCfg>
        where TU : class, IUserMaterial<TM, TCfg> where TM : IMaterial<TCfg>
    {
        protected readonly IElementSet<TU> m_Elements;
        protected readonly List<TU> m_TempElements;

        public UserMaterialSet()
        {
            m_Elements = new ElementSet<TU>();
            m_TempElements = new List<TU>();
        }

        public UserMaterialSet(IElementSet<TU> elements)
        {
            m_Elements = elements;
            m_TempElements = new List<TU>();
        }

        // IUserMaterialSet

        public int UserMaterialSize => m_Elements.Size;

        public bool ExistUserMaterial(string eKey)
        {
            return m_Elements.Exist(eKey);
        }

        public bool ExistUserMaterial(int mId)
        {
            return m_Elements.Get(e => e.MId == mId) != null;
        }

        public bool ExistUserMaterial(int mId, int uId)
        {
            return m_Elements.Get(e => e.MId == mId && e.UId == uId) != null;
        }

        public int GetUserMaterialNum(string eKey)
        {
            return m_Elements.Get(eKey)?.Num ?? 0;
        }

        public int GetUserMaterialNum(int mid)
        {
            var rs = 0;
            m_Elements.ForeachElement<TU>(um =>
            {
                if (um.MId == mid && um.Num >= 0) rs += um.Num;
            });
            return rs;
        }

        public int GetUserMaterialNum(int mId, int uId)
        {
            return m_Elements.Get(e => e.MId == mId && e.UId == uId)?.Num ?? 0;
        }

        public TUAs GetUserMaterialAs<TUAs>(string eKey) where TUAs : IUserMaterial
        {
            return m_Elements.GetElement<TUAs>(eKey);
        }

        public TUAs GetUserMaterialAs<TUAs>(int mId, int uId = 0) where TUAs : IUserMaterial
        {
            return m_Elements.GetElement<TUAs>(tuas => tuas.MId == mId && tuas.UId == uId);
        }

        public TUAs[] GetUserMaterialsAs<TUAs>() where TUAs : IUserMaterial
        {
            return m_Elements.GetElements<TUAs>();
        }

        public TUAs[] GetUserMaterialsAs<TUAs>(int mId) where TUAs : IUserMaterial
        {
            return m_Elements.GetElements<TUAs>(o => o.MId == mId);
        }

        public void ForeachUserMaterialAs<TUAs>(Action<TUAs> each) where TUAs : IUserMaterial
        {
            m_Elements.ForeachElement(each);
        }

        // IUserMaterialSet<TU, TM, TCfg>

        public int GetSize(Predicate<TU> match)
        {
            var count = 0;
            for (var index = m_Elements.Size - 1; index >= 0; index--)
            {
                if (match.Invoke(m_Elements[index])) count += 1;
            }

            return count;
        }

        public TU GetUserMaterial(string eKey)
        {
            return m_Elements.Get(eKey);
        }

        public TU GetUserMaterial(int mId, int uId = 0)
        {
            return m_Elements.Get(u => u.MId == mId && u.UId == uId);
        }

        public TU[] GetUserMaterials()
        {
            return m_Elements.GetAll();
        }

        public TU[] GetUserMaterials(int mId)
        {
            return m_Elements.GetRange(u => u.MId == mId);
        }

        public TU[] GetUserMaterials(Predicate<TU> match)
        {
            return m_Elements.GetRange(match);
        }

        public void ForeachUserMaterial(Action<TU> each)
        {
            m_Elements.Foreach(each);
        }

        // IUserMaterialSetMod

        public TUMod GetUserMaterialMod<TUMod>(string eKey) where TUMod : class, IUserMaterial, IUserMaterialMod
        {
            return m_Elements.GetElement<TUMod>(eKey);
        }

        public TUMod GetUserMaterialMod<TUMod>(int mId, int uId = 0) where TUMod : class, IUserMaterial, IUserMaterialMod
        {
            return m_Elements.GetElement<TUMod>(mod => mod.MId == mId && mod.UId == uId);
        }

        public TUMod RemoveUserMaterialAs<TUMod>(string eKey) where TUMod : class, IUserMaterial
        {
            return m_Elements.RemoveElement(eKey) as TUMod;
        }

        public TUMod RemoveUserMaterialAs<TUMod>(int mId, int uId = 0) where TUMod : class, IUserMaterial
        {
            return m_Elements.RemoveElement(InternalUtil.Id2EKey(mId, uId)) as TUMod;
        }

        public TUMod[] RemoveUserMaterialsAs<TUMod>(string[] eKeyArr) where TUMod : class, IUserMaterial
        {
            if (null == eKeyArr || eKeyArr.Length == 0) return null;
            var rms = m_Elements.RemoveElements(eKeyArr);
            return CloneArray<TUMod>(rms);
        }

        public TUMod[] RemoveUserMaterialsAs<TUMod>(IEnumerable<string> eKeyArr) where TUMod : class, IUserMaterial
        {
            if (null == eKeyArr) return null;
            var rms = m_Elements.RemoveElements(eKeyArr);
            return CloneArray<TUMod>(rms);
        }

        public TUMod[] RemoveUserMaterialsAs<TUMod>(Predicate<TUMod> match) where TUMod : class, IUserMaterial
        {
            if (null == match) return null;
            var rms = m_Elements.RemoveElements(element => match.Invoke(element as TUMod));
            return CloneArray<TUMod>(rms);
        }

        public TUMod[] RemoveUserMaterialsAs<TUMod>() where TUMod : class, IUserMaterial
        {
            if (m_Elements.Size == 0) return new TUMod[0];
            var rms = m_Elements.RemoveElements();
            return CloneArray<TUMod>(rms);
        }

        public void AddUserMaterialAs(IUserMaterial um)
        {
            if (!(um is TU)) return;
            m_Elements.AddElement(um);
        }

        public void AddUserMaterialsAs<TUAs>(TUAs[] umArr) where TUAs : class, IUserMaterial
        {
            if (null == umArr || umArr.Length == 0) return;
            foreach (var o in umArr)
            {
                AddUserMaterialAs(o);
            }
        }

        public void AddUserMaterialsAs<TUAs>(IEnumerable<TUAs> umArr) where TUAs : class, IUserMaterial
        {
            if (null == umArr) return;
            foreach (var o in umArr)
            {
                AddUserMaterialAs(o);
            }
        }

        public void ReplaceUserMaterialAs(IUserMaterial um)
        {
            if (null == um || !(um is TU)) return;
            m_Elements.ReplaceElement(um);
        }

        public void ReplaceUserMaterialsAs<TUAs>(TUAs[] umArr) where TUAs : class, IUserMaterial
        {
            if (null == umArr || umArr.Length == 0) return;
            foreach (var o in umArr)
            {
                ReplaceUserMaterialAs(o);
            }
        }

        public void ReplaceUserMaterialsAs<TUAs>(IEnumerable<TUAs> umArr) where TUAs : class, IUserMaterial
        {
            if (null == umArr) return;
            foreach (var o in umArr)
            {
                ReplaceUserMaterialAs(o);
            }
        }

        private T1[] CloneArray<T1>(IElement[] arr) where T1 : class, IElement
        {
            if (null == arr || arr.Length == 0) return null;
            var rs = new T1[arr.Length];
            for (var index = arr.Length - 1; index >= 0; index--)
            {
                rs[index] = arr[index] as T1;
            }

            return rs;
        }

        // IUserMaterialModifySet<TU, TM, TCfg>

        public TU RemoveUserMaterial(string eleId)
        {
            return m_Elements.Remove(eleId);
        }

        public TU[] RemoveUserMaterials(string[] eleIdArr)
        {
            return m_Elements.RemoveRange(eleIdArr);
        }

        public TU[] RemoveUserMaterials(IEnumerable<string> eleIdArr)
        {
            return m_Elements.RemoveRange(eleIdArr);
        }

        public TU[] RemoveUserMaterials(Predicate<TU> match)
        {
            if (null == match) return null;
            var rms = m_Elements.RemoveElements(element => match.Invoke(element as TU));
            return CloneArray<TU>(rms);
        }

        public TU[] RemoveUserMaterials()
        {
            return m_Elements.RemoveAll();
        }

        public void AddUserMaterial(TU um)
        {
            m_Elements.Add(um);
        }

        public void AddUserMaterials(TU[] umArr)
        {
            m_Elements.AddRange(umArr);
        }

        public void AddUserMaterials(IEnumerable<TU> umArr)
        {
            m_Elements.AddRange(umArr);
        }

        public void ReplaceUserMaterial(TU um)
        {
            m_Elements.Replace(um);
        }

        public void ReplaceUserMaterials(TU[] umArr)
        {
            m_Elements.ReplaceRange(umArr);
        }

        public void ReplaceUserMaterials(IEnumerable<TU> umArr)
        {
            m_Elements.ReplaceRange(umArr);
        }
    }
}