using System;
using System.Collections.Generic;
using UnityEngine;
using XGame.Framework;
using XGame.Framework.Asyncs;
using XGame.Framework.Components;
using XGame.Framework.Interfaces;
using XGame.Framework.Map;
using XGame.Framework.Time;

namespace FL.Battle.Components
{
    /// <summary>
    /// 特效播放上下文
    /// </summary>
    public interface IVfxContext
    {
        IMapAssetModule Asset { get; }
        ITimeModule Time { get; }
        /// <summary>
        /// 世界节点，不是则返回空
        /// 只有世界节点才能播放VfxFollowType.World的特效
        /// 每个地图只能有一个世界节点
        /// </summary>
        Transform WorldTr { get; }
        Transform CenterTr { get; }
        /// <summary>
        /// 施法点,没有则返回空
        /// </summary>
        Transform CastTr { get; }
    }
    /// <summary>
    /// 特效播放组件
    /// TODO 世界跟随类型需要将特效转到地图播放，以防止角色死亡时特效播放被打断
    /// </summary>
    public class VfxComponent : Component<IVfxContext>
    {
        class VfxPlayingDto : IReference
        {
            public IAsync loadAsync;
            public ITimer timer;
            public GameObject vfxGo;

            void IReference.Clear()
            {
                loadAsync = null;
                timer = null;
                vfxGo = null;
            }
        }

        private Dictionary<VfxPlayingDto, VfxArgs> _playingVfxMap = new Dictionary<VfxPlayingDto, VfxArgs>();
        protected override void OnEnable(object intent)
        {
            if (Context.WorldTr != null)
            {
                EventSingle.Instance.AddListener(EventDefine.VfxComponentWorldVfx, OnWorldVfx);
            }
        }
        protected override void OnDisable()
        {
            if (Context.WorldTr != null)
            {
                EventSingle.Instance.RemoveListener(EventDefine.VfxComponentWorldVfx, OnWorldVfx);
            }
        }
        protected override void OnDispose()
        {
            Stop();
        }
        public void Play(VfxArgs args)
        {
            Assert.IsTrue(args != null, "特效播放参数不能为空.");
            Assert.IsFalse(string.IsNullOrEmpty(args.vfxName), "特效名字不能为空.");
            Assert.IsTrue(args.duration > 0, "特效播放时长必须大于零.");
            if (args.followType == EVfxFollowType.World && Context.WorldTr == null)
            {
                EventSingle.Instance.Notify(EventDefine.VfxComponentWorldVfx, args);
                return;
            }
            var dto = AcquireDto(args);
            void DelayPlay()
            {
                var loadAsync = Context.Asset.LoadAsync<GameObject>(args.vfxName);
                dto.loadAsync = loadAsync;
                loadAsync.On(_ =>
                {
                    dto.loadAsync = null;
                    var vfxGo = loadAsync.Result;
                    if (vfxGo == null)
                    {
                        RecycleDto(dto);
                        args.onFinish.SafeInvoke();
                        RecycleArgs(args);
                        return;
                    }
                    dto.vfxGo = vfxGo;
                    SetParent(vfxGo.transform, args.followType);
                    vfxGo.SetActive(true);
                    args.action?.Start(vfxGo.transform);
                    dto.timer = Context.Time.AddDelayTimer(args.duration, () =>
                    {
                        dto.timer = null;
                        dto.vfxGo = null;
                        //回收GameObject
                        Context.Asset.Recycle(vfxGo);
                        //回收dto
                        RecycleDto(dto);
                        args.onFinish.SafeInvoke();
                        var next = args.next;
                        if (next != null)
                        {
                            args.next = null;
                            //播放下一个
                            Play(next);
                        }
                        RecycleArgs(args);
                    });
                });
            }
            if (args.delay > 0)
            {
                dto.timer = Context.Time.AddDelayTimer(args.delay, () =>
                {
                    dto.timer = null;
                    DelayPlay();
                });
            }
            else
            {
                DelayPlay();
            }
        }
        public void Stop()
        {
            foreach (var item in _playingVfxMap)
            {
                RecycleDto(item.Key, false);
                RecycleArgs(item.Value);
            }
            _playingVfxMap.Clear();
        }

        private void SetParent(Transform vfxTr, EVfxFollowType followType)
        {
            Transform parent;
            switch (followType)
            {
                case EVfxFollowType.World:
                    parent = Context.WorldTr;
                    break;
                case EVfxFollowType.CastPoint:
                    parent = Context.CastTr != null ? Context.CastTr : Context.CenterTr;
                    break;
                default:
                    parent = Context.CenterTr;
                    break;
            }
            vfxTr.SetParent(parent, false);
            if (followType != EVfxFollowType.World)
            {
                vfxTr.localPosition = Vector3.zero;
            }
            //TODO 技能特效需要缩小尺寸
            vfxTr.localScale = Vector3.one * 0.7f;
        }

        //public VfxArgs AcquireArgs()
        //{
        //    return ObjectPool.Acquire<VfxArgs>();
        //}
        public void RecycleArgs(VfxArgs args)
        {
            if (args == null)
                return;
            if (args.next != null)
            {
                RecycleArgs(args.next);
            }
            if (args.action != null)
            {
                ObjectPool.Recycle(args.action);
                args.action = null;
            }
            ObjectPool.Recycle(args);
        }
        private VfxPlayingDto AcquireDto(VfxArgs args)
        {
            var dto = ObjectPool.Acquire<VfxPlayingDto>();
            _playingVfxMap.Add(dto, args);
            return dto;
        }
        private void RecycleDto(VfxPlayingDto dto, bool removeMap = true)
        {
            dto.loadAsync?.RemoveAll();
            dto.timer?.Cancel();
            if (dto.vfxGo != null)
            {
                Context.Asset.Recycle(dto.vfxGo);
            }
            if (removeMap)
            {
                _playingVfxMap.Remove(dto);
            }
            ObjectPool.Recycle(dto);
        }

        #region 事件监听
        private void OnWorldVfx(int eventId, object args)
        {
            var vfxArgs = args as VfxArgs;
            Play(vfxArgs);
        }
        #endregion
    }
}
