﻿using JLGames.RocketDriver.CSharp.Event;
using JLGames.RocketDriver.Games.RpgMaterial.Common;
using JLGames.RocketDriver.Games.RpgMaterial.Material;
using JLGames.RocketDriver.Games.RpgMaterial.User;

namespace JLGames.RocketDriver.Games.RpgMaterial.Service
{
    public class MaterialServiceProxy<TU, TM, TCfg> : IServiceUpdateProxy, IServiceTriggerProxy
        where TU : class, IUserMaterial<TM, TCfg>
        where TM : IMaterial<TCfg>
    {
        protected readonly int m_Type;
        protected UserMaterialSet<TU, TM, TCfg> m_UserMaterials;
        protected IEventDispatcher m_EventDispatcher;
        protected IUserMaterialGenerator m_Generator;
        protected TriggerQueue m_TriggerQueue;

        public MaterialServiceProxy(int type)
        {
            m_Type = type;
        }

        public void ResetProxy()
        {
            m_TriggerQueue = null;
            m_EventDispatcher = null;
            m_Generator = null;
            m_UserMaterials = null;
        }

        public void InitProxy(UserMaterialSet<TU, TM, TCfg> userMaterials, IUserMaterialGenerator generator,
            IEventDispatcher eventDispatcher)
        {
            m_UserMaterials = userMaterials;
            m_Generator = generator;
            m_EventDispatcher = eventDispatcher;
            m_TriggerQueue = new TriggerQueue(m_Type);
        }

        // IServiceTriggerProxy

        public void RegisterTrigger(ITriggerEntity trigger)
        {
            m_TriggerQueue.RegisterTrigger(trigger);
        }

        public int RegisterTrigger(Trigger.TrackingType tType, int mType, int eId, Trigger.CompareType cType, int cValue,
            Trigger.TriggerInvoke invokeFunc, bool post = true, int times = -1)
        {
            return m_TriggerQueue.RegisterTrigger(tType, mType, eId, cType, cValue, invokeFunc, post, times);
        }

        public int RegisterOffsetTrigger(int mType, int eId, Trigger.CompareType cType, int cValue, Trigger.TriggerInvoke invokeFunc,
            bool post = true, int times = -1)
        {
            return m_TriggerQueue.RegisterOffsetTrigger(mType, eId, cType, cValue, invokeFunc, post, times);
        }

        public int RegisterValueTrigger(int mType, int eId, Trigger.CompareType cType, int cValue, Trigger.TriggerInvoke invokeFunc, bool post = true,
            int times = -1)
        {
            return m_TriggerQueue.RegisterValueTrigger(mType, eId, cType, cValue, invokeFunc, post, times);
        }

        public void UnregisterTrigger(ITriggerEntity trigger)
        {
            m_TriggerQueue.UnregisterTrigger(trigger);
        }

        public void UnregisterTrigger(int triggerId)
        {
            m_TriggerQueue.UnregisterTrigger(triggerId);
        }
        // IServiceUpdateProxy

        public bool UpdateUserMaterial(UserNotifyData no)
        {
            var result = UpdateToSet(m_UserMaterials, no);
            if (!result.Succ) return false;
            NotifyMaterialUpdate(result);
            return true;
        }

        public int UpdateUserMaterials(UserNotifyData[] nos)
        {
            var results = UpdateToSet(m_UserMaterials, nos);
            if (null == results || results.Length == 0) return 0;
            var rs = 0;
            foreach (var result in results)
            {
                if (!result.Succ) continue;
                NotifyMaterialUpdate(result);
                rs += 1;
            }

            m_EventDispatcher.DispatchEvent(MaterialServiceEvents.OnNotifyMultiUpdate, nos);
            return rs;
        }

        public bool UpdateUserMaterial(IUserMaterial um)
        {
            var result = UpdateToSet(m_UserMaterials, um);
            if (!result.Succ) return false;
            NotifyMaterialUpdate(result);
            m_EventDispatcher.DispatchEvent(MaterialServiceEvents.OnNotifyMultiInstanceUpdate, um);
            return true;
        }

        public int UpdateUserMaterials(IUserMaterial[] ums)
        {
            var results = UpdateToSet(m_UserMaterials, ums);
            if (null == results || results.Length == 0) return 0;
            var rs = 0;
            foreach (var result in results)
            {
                if (!result.Succ) continue;
                NotifyMaterialUpdate(result);
                m_EventDispatcher.DispatchEvent(MaterialServiceEvents.OnNotifyInstanceUpdate, result.Notify);
                rs += 1;
            }

            m_EventDispatcher.DispatchEvent(MaterialServiceEvents.OnNotifyMultiInstanceUpdate, ums);
            return rs;
        }

        // Protected------------------------------------------------------------

        protected void NotifyMaterialUpdate(MaterialServiceEventData notify)
        {
//            DebugUtil.Log("Base.NotifyMaterialUpdate:", notify);
            m_EventDispatcher.DispatchEvent(MaterialServiceEvents.OnNotifyUpdate, notify);
            if (notify.IsIncrease)
            {
                m_EventDispatcher.DispatchEvent(MaterialServiceEvents.OnNotifyIncrease, notify);
                if (notify.IsNew) m_EventDispatcher.DispatchEvent(MaterialServiceEvents.OnNotifyNew, notify);
                return;
            }

            if (notify.IsDecrease)
            {
                m_EventDispatcher.DispatchEvent(MaterialServiceEvents.OnNotifyDecrease, notify);
                if (notify.IsDel) m_EventDispatcher.DispatchEvent(MaterialServiceEvents.OnNotifyDel, notify);
                return;
            }
        }

        protected MaterialServiceEventData[] UpdateToSet(IUserMaterialSetMod set, UserNotifyData[] values)
        {
            if (null == values || values.Length == 0) return null;
            var rs = new MaterialServiceEventData[values.Length];
            for (var index = 0; index < values.Length; index++)
            {
                rs[index] = UpdateToSet(set, values[index]);
            }

            return rs;
        }

        protected MaterialServiceEventData UpdateToSet(IUserMaterialSetMod set, UserNotifyData notify)
        {
            m_TriggerQueue.ForeachBefore(notify);
            if (0 == notify.Num) // 删除, Id为EKey
            {
                var removed = set.RemoveUserMaterialAs<IUserMaterial>(notify.Id); //Id为EKey
                m_TriggerQueue.ForeachPost(notify);
                return new MaterialServiceEventData(null != removed, removed, notify);
            }

            var old = set.GetUserMaterialMod<IUserMaterialMod>(notify.Id); //Id为EKey
            if (null == old) // 新增
            {
                var newData = m_Generator.GenUserMaterial(notify.Id, notify.Num); //Id为MId
                set.AddUserMaterialAs(newData);
                m_TriggerQueue.ForeachPost(notify);
                return new MaterialServiceEventData(true, null, notify);
            }

            // 更新
            old.SetNum(notify.Num);
            m_TriggerQueue.ForeachPost(notify);
            return new MaterialServiceEventData(true, old, notify);
        }

        protected MaterialServiceEventData[] UpdateToSet(IUserMaterialSetMod set, IUserMaterial[] values)
        {
            if (null == values || values.Length == 0) return null;
            var rs = new MaterialServiceEventData[values.Length];
            for (var index = 0; index < values.Length; index++)
            {
                rs[index] = UpdateToSet(set, values[index]);
            }

            return rs;
        }

        protected MaterialServiceEventData UpdateToSet(IUserMaterialSetMod set, IUserMaterial um)
        {
            if (null == um) return new MaterialServiceEventData(false, null, default(UserNotifyData));
            IUserMaterial old;
            var notify = GetNotifyFromSet0(set, um, out old);
            m_TriggerQueue.ForeachPost(notify);
            if (um.Num == 0)
            {
                var removed = set.RemoveUserMaterialAs<IUserMaterial>(notify.Id); //Id为EKey
                m_TriggerQueue.ForeachPost(notify);
                return new MaterialServiceEventData(null != removed, removed, notify);
            }

            if (null == old) // 新增
            {
                var newData = m_Generator.GenUserMaterial(notify.Id, notify.Num); //Id为MId
                set.AddUserMaterialAs(newData);
                m_TriggerQueue.ForeachPost(notify);
                return new MaterialServiceEventData(true, null, notify);
            }

            // 更新
            ((IUserMaterialMod) old).SetNum(notify.Num);
            m_TriggerQueue.ForeachPost(notify);
            return new MaterialServiceEventData(true, old, notify);
        }

        private UserNotifyData GetNotifyFromSet0(IUserMaterialSetMod set, IUserMaterial um, out IUserMaterial old)
        {
            old = set.GetUserMaterialAs<IUserMaterial>(um.MId);
            var oldValue = old?.Num ?? 0;
            return new UserNotifyData(um.Type, um.MId, um.UId, um.Num, um.Num - oldValue);
        }
    }
}