﻿using System.Collections.Generic;
using JLGames.RocketDriver.Actions.ExcelExporter;
using JLGames.RocketDriver.CSharp.Event;
using JLGames.RocketDriver.Games.NetManager;
using JLGames.RocketDriver.Games.NetManager.Virtual;
using JLGames.RocketDriver.Games.RpgMaterial.Common;
using JLGames.RocketDriver.Games.RpgMaterial.Material;
using JLGames.RocketDriver.Games.RpgMaterial.Service;
using JLGames.RocketDriver.Games.RpgMaterial.User;

namespace JLGames.RocketDriver.Samples.RpgMaterialDemo.Service.Material
{
    public abstract class GameMaterialServiceBase<TU, TM, TCfg> : MaterialService<TU, TM, TCfg>
        where TU : class, IUserMaterial<TM, TCfg>
        where TM : IMaterial<TCfg>, IInitMaterial<TCfg>
    {
        protected IVirtualClient ClientProxy => NetManager.Shared.DefaultClient as IVirtualClient;

        public GameMaterialServiceBase(int type, bool unique) : base(type, unique)
        {
        }

        protected IElementSet<TM> InitMaterialData<TMImpl>(string fileName) where TMImpl : TM, new()
        {
            var text = ServiceCenter.ConfigDataService.ExtractJsonContext(fileName);

            var cfg = DataParsingUtil.ParseConfig<TCfg>(text);
            var set = new ElementSet<TM>();
            for (var i = 0; i < cfg.count; i++)
            {
                var impl = new TMImpl();
                impl.InitMaterial(cfg.data[i]);
                set.Add(impl);
            }

            return set;
        }

        public IGameMaterial GetGameMaterial(int mid)
        {
            return m_Materials.GetMaterial(mid) as IGameMaterial;
        }

        // IGameMaterialServiceMod

        protected void AddNotifyListeners()
        {
            var client = NetManager.Shared.DefaultClient;
            client.AddEventListener(VirtualClientEvents.NotifyUserData, OnUserData);
            client.AddEventListener(VirtualClientEvents.NotifyUserNum, OnUserDataNum);
            client.AddEventListener(VirtualClientEvents.NotifyUserOffset, OnUserDataOffset);
            client.AddEventListener(VirtualClientEvents.NotifyUserDatas, OnUserDatas);
            client.AddEventListener(VirtualClientEvents.NotifyUserNums, OnUserDataNums);
            client.AddEventListener(VirtualClientEvents.NotifyUserOffsets, OnUserDataOffsets);
        }

        protected void RemoveNotifyListeners()
        {
            var client = NetManager.Shared.DefaultClient;
            client.RemoveEventListener(VirtualClientEvents.NotifyUserOffsets, OnUserDataOffsets);
            client.RemoveEventListener(VirtualClientEvents.NotifyUserNums, OnUserDataNums);
            client.RemoveEventListener(VirtualClientEvents.NotifyUserDatas, OnUserDatas);
            client.RemoveEventListener(VirtualClientEvents.NotifyUserOffset, OnUserDataOffset);
            client.RemoveEventListener(VirtualClientEvents.NotifyUserNum, OnUserDataNum);
            client.RemoveEventListener(VirtualClientEvents.NotifyUserData, OnUserData);
        }

        protected void OnUserData(EventData evd)
        {
            var data = (UserNotifyData) evd.Data;
            if (data.Type != MaterialType) return;
            m_Proxy.UpdateUserMaterial(data);
        }

        protected void OnUserDataNum(EventData evd)
        {
            var num = (DataNum) evd.Data;
            if (num.Type != MaterialType) return;
            m_Proxy.UpdateUserMaterial(FromNum(num));
        }

        protected void OnUserDataOffset(EventData evd)
        {
            var offset = (DataOffset) evd.Data;
            if (offset.Type != MaterialType) return;
            m_Proxy.UpdateUserMaterial(FromOffset(offset));
        }

        protected void OnUserDatas(EventData evd)
        {
            var datas = (UserNotifyData[]) evd.Data;
            if (null == datas || datas.Length == 0) return;
            var myDatas = datas.ClearByFilter(data => data.Type != MaterialType);
            if (null == myDatas || myDatas.Length == 0) return;
            m_Proxy.UpdateUserMaterials(myDatas);
        }

        protected void OnUserDataNums(EventData evd)
        {
            var nums = (DataNum[]) evd.Data;
            if (null == nums || nums.Length == 0) return;
            var myDatas = FromNums(nums);
            if (null == myDatas || myDatas.Length == 0) return;
            m_Proxy.UpdateUserMaterials(myDatas);
        }

        protected void OnUserDataOffsets(EventData evd)
        {
            var offsets = (DataOffset[]) evd.Data;
            if (null == offsets || offsets.Length == 0) return;
            var myDatas = FromOffsets(offsets);
            if (null == myDatas || myDatas.Length == 0) return;
            m_Proxy.UpdateUserMaterials(myDatas);
        }

        private UserNotifyData FromOffset(DataOffset offset)
        {
            var oldNum = m_UserMaterials.GetUserMaterialNum(offset.Id, offset.UId);
            return new UserNotifyData(MaterialType, offset.Id, offset.UId, oldNum + offset.Offset, offset.Offset);
        }

        private readonly List<UserNotifyData> m_TempNotifyData = new List<UserNotifyData>();

        private UserNotifyData[] FromOffsets(DataOffset[] offsets)
        {
            m_TempNotifyData.Clear();
            foreach (var offset in offsets)
            {
                if (offset.Type != MaterialType) continue;
                m_TempNotifyData.Add(FromOffset(offset));
            }

            return m_TempNotifyData.Count <= 0 ? null : m_TempNotifyData.ToArray();
        }

        private UserNotifyData FromNum(DataNum num)
        {
            var oldNum = m_UserMaterials.GetUserMaterialNum(num.Id, num.UId);
            return new UserNotifyData(MaterialType, num.Id, num.UId, num.Num, num.Num - oldNum);
        }

        private UserNotifyData[] FromNums(DataNum[] nums)
        {
            m_TempNotifyData.Clear();
            foreach (var offset in nums)
            {
                if (offset.Type != MaterialType) continue;
                m_TempNotifyData.Add(FromNum(offset));
            }

            return m_TempNotifyData.Count <= 0 ? null : m_TempNotifyData.ToArray();
        }
    }
}