using FL.Battle.Buffs;
using System;
using System.Collections.Generic;
using XGame.Framework;
using XGame.Framework.Interfaces;

namespace FL.Map.TestMainMap
{
    public partial class EntitiesComponent : ILateUpdate
    {
        private HashSet<long> _buffOwnerIds;
        private bool _isElementBuffDirty;
        private BuffPreviewDto _buffPreview;
        void ILateUpdate.LateUpdate(int millisecond)
        {
            Context.VM.UpdateMapMoveTime(millisecond);
            Context.VM.Camera.OnLateUpdate(millisecond);
            RefreshElementBuffs();
        }
        #region 元素buff
        private void RefreshElementBuffs()
        {
            if (!_isElementBuffDirty) return;
            _isElementBuffDirty = false;
            CollectElementBuffs((elementBuffs, partners) =>
            {
                var isMatch = false;
                if (elementBuffs != null)
                {
                    foreach (var buff in elementBuffs)
                    {
                        foreach (var partner in partners)
                        {
                            if (MatchTargetBuff(partner, buff))
                            {
                                isMatch = true;
                                break;
                            }
                        }
                        if (isMatch)
                        {
                            break;
                        }
                    }
                }
                if (!isMatch)
                { // 没有匹配的项
                    ResetBuffPreview();
                }
            });
        }
        private void CollectElementBuffs(Action<List<IBuff>, List<PartnerEntityView>> callback)
        {
            if (_buffOwnerIds == null || _buffOwnerIds.Count == 0)
            {
                callback.SafeInvoke(null, null);
                return;
            }
            var buffOwnerIds = ListPool.Acquire<long>();
            buffOwnerIds.AddRange(_buffOwnerIds);
            var elementBuffs = ListPool.Acquire<IBuff>();
            foreach (var ownerId in buffOwnerIds)
            {
                if (_monsterMap.TryGetValue(ownerId, out var monster) &&
                    monster.IsDead == false &&
                    monster.Buffs.GetElementBuffs(ref elementBuffs))
                {
                    continue;
                }
                //非怪物 || 没有满足要求的元素buff
                _buffOwnerIds.Remove(ownerId);
            }
            elementBuffs.Sort((a, b) =>
            { // 层数高的排前面
                var orderA = a.Layers * 1000 / a.LayerLimit;
                var orderB = b.Layers * 1000 / b.LayerLimit;
                return orderB - orderA;
            });
            // 随从 按照技能冷却时间排序
            var partners = ListPool.Acquire<PartnerEntityView>();
            partners.AddRange(_partnerMap.Values);
            var skillCdMap = new Dictionary<long, int>();
            partners.Sort((a, b) =>
            {
                var id_a = a.Entity.EntityId;
                if (!skillCdMap.TryGetValue(id_a, out var cd_a))
                {
                    cd_a = a.Skill.GetMinCooldown();
                    skillCdMap.Add(id_a, cd_a);
                }
                var id_b = b.Entity.EntityId;
                if (!skillCdMap.TryGetValue(id_b, out var cd_b))
                {
                    cd_b = a.Skill.GetMinCooldown();
                    skillCdMap.Add(id_b, cd_b);
                }
                // 冷却时间短的排前面
                return cd_a - cd_b;
            });
            //回调
            callback.SafeInvoke(elementBuffs, partners);
            //回收临时变量
            ListPool.Recycle(partners);
            ListPool.Recycle(buffOwnerIds);
            ListPool.Recycle(elementBuffs);
        }
        private bool MatchTargetBuff(PartnerEntityView partner, IBuff buff)
        {
            if (partner.Epigraphics.IsTargetBuff(buff.TableId, out var epigrapId))
            {
                NotifyBuffPreview(buff, partner.Entity.Attr.UID, false, epigrapId);
                return true;
            }
            if (partner.Skill.FindSkillByElementType(buff.ElementType, out var skillId))
            {
                NotifyBuffPreview(buff, partner.Entity.Attr.UID, true, skillId);
                return true;
            }
            return false;
        }
        private void NotifyBuffPreview(IBuff buff, long partnerUID, bool isSkill, int skillId)
        {
            _buffPreview = new BuffPreviewDto()
            {
                ownerId = buff.OwnerId,
                buffTableId = buff.TableId,
                partnerUID = partnerUID,
                isSkill = isSkill,
                skillId = skillId,
                layer = buff.Layers,
                layerLimit = buff.LayerLimit,
            };
            EventSingle.Instance.Notify(EventDefine.GameMainMapPreviewBuff, _buffPreview);
        }
        private void ResetBuffPreview()
        {
            _buffPreview = default;
            EventSingle.Instance.Notify(EventDefine.GameMainMapPreviewBuff, _buffPreview);
        }
        private void OnAddBuff(int eventId, object args)
        {
            var dto = (BuffEventDto)args;
            var buffOwners = _buffOwnerIds ??= new HashSet<long>();
            buffOwners.Add(dto.ownerId);
            _isElementBuffDirty = true;
        }
        private void OnRemoveBuff(int eventId, object args)
        {
            var dto = (BuffEventDto)args;
            if (_buffPreview.ownerId == dto.ownerId && _buffPreview.buffTableId == dto.buffTableId)
            {
                ResetBuffPreview();
                _isElementBuffDirty = true;
            }
        }
        private void OnRequestBuffList(int eventId, object args)
        {
            var buffPreviewDtos = new List<BuffPreviewDto>();
            CollectElementBuffs((elementBuffs, partners) =>
            {
                if (elementBuffs != null)
                {
                    foreach (var buff in elementBuffs)
                    {
                        foreach (var partner in partners)
                        {
                            var partnerUID = partner.Entity.Attr.UID;
                            if (partner.Epigraphics.IsTargetBuff(buff.TableId, out var epigrapId))
                            {
                                var dto = new BuffPreviewDto()
                                {
                                    ownerId = buff.OwnerId,
                                    buffTableId = buff.TableId,
                                    partnerUID = partnerUID,
                                    isSkill = false,
                                    skillId = epigrapId,
                                    layer = buff.Layers,
                                    layerLimit = buff.LayerLimit,
                                };
                                buffPreviewDtos.Add(dto);
                            }
                            if (partner.Skill.FindSkillByElementType(buff.ElementType, out var skillId))
                            {
                                var dto = new BuffPreviewDto()
                                {
                                    ownerId = buff.OwnerId,
                                    buffTableId = buff.TableId,
                                    partnerUID = partnerUID,
                                    isSkill = true,
                                    skillId = skillId,
                                    layer = buff.Layers,
                                    layerLimit = buff.LayerLimit,
                                };
                                buffPreviewDtos.Add(dto);
                            }
                        }
                    }
                }
                EventSingle.Instance.Notify(EventDefine.GameMainMapPushPreviewBuffs, buffPreviewDtos);
            });
        }
        private void RemoveElementBuff(long ownerId)
        {
            if (_buffPreview.ownerId == ownerId)
            {
                ResetBuffPreview();
            }
            if (_buffOwnerIds?.Remove(ownerId) ?? false)
            {
                _isElementBuffDirty = true;
            }
        }
        private void ClearElementBuffs()
        {
            _buffOwnerIds?.Clear();
            _isElementBuffDirty = false;
            _buffPreview = default;
        }
        #endregion
    }
}
