﻿using System;
using JLGames.RocketDriver.Actions.Utils;
using UnityEngine;

namespace JLGames.RocketDriver.Actions.Loaderx
{
    public sealed class BundleRef : IEquatable<BundleRef>
    {
        private readonly string m_BundleName;
        private AssetBundle m_Bundle;
        private AssetBundleStatus m_Status;

        private bool m_AutoRelease;
        private bool m_UnloadObjects;

        private int m_TempCount;
        private bool m_ReleaseAfterInvoke;

        private event LoaderDelegate.OnBundleLoaded AsyncEvent;

        //---------------------------------

        public string BundleName => m_BundleName;
        public AssetBundle Bundle => m_Bundle;
        public AssetBundleStatus Status => m_Status;

        internal bool IsLoading => m_Status == AssetBundleStatus.Loading;
        internal bool IsAutoRelease => m_AutoRelease;
        internal bool UnloadObjects => m_UnloadObjects;

        //---------------------------------

        public bool Equals(BundleRef other)
        {
            return other == this || (null != other && other.BundleName == BundleName);
        }

        //---------------------------------

        public BundleRef(string bundleName, bool autoRelease, bool unloadObjects)
        {
            m_BundleName = bundleName;
            m_AutoRelease = autoRelease;
            m_UnloadObjects = unloadObjects;
            m_Status = AssetBundleStatus.Init;
        }

        /// <summary>
        /// Force release of resources after callbacks are all executed
        /// 在回调全部执行后强制释放资源
        /// </summary>
        public void ReleaseAfterInvoke()
        {
            m_ReleaseAfterInvoke = true;
        }

        /// <summary>
        /// Force the release of resources
        /// 强制释放资源 
        /// </summary>
        public void Release()
        {
            DebugUtil.Log($"AssetBundleRef.Release:(Status={m_Status})");
            m_TempCount = 0;
            DoRelease();
        }

        //---------------------------------

        internal void StartLoading()
        {
//            DebugUtil.Log($"AssetBundleRef.StartLoading:(Status={_status})");
            if (m_Status == AssetBundleStatus.Init) m_Status = AssetBundleStatus.Loading;
        }

        internal void FinishLoading(AssetBundle bundle)
        {
//            DebugUtil.Log($"AssetBundleRef.FinishLoading:(Status={_status}, Bundle={bundle})");
            if (m_Status == AssetBundleStatus.Loading)
            {
                m_Status = AssetBundleStatus.Loaded;
                m_Bundle = bundle;
                Invoke();
                ReleaseAuto();
            }
        }

        /// <summary>
        /// Set as a temporary resource
        /// Here by increasing the calculation, it is used to record the temporary setting amount
        /// 设置为临时资源
        /// 这里通过增加计算，用来记录临时设置量
        /// </summary>
        internal void SetTempBundle()
        {
            m_TempCount += 1;
        }

        internal void UpgradeReleaseStatus(bool autoRelease, bool unloadObjects)
        {
//            DebugUtil.Log(
//                $"AssetBundleRef.UpgradeReleaseStatus:(Status={_status}, AutoRelease={autoRelease}, UnloadObjects={unloadObjects})");
            UpgradeReleaseStatus(autoRelease);
            UpgradedObjectUnloadStatus(unloadObjects);
        }


        /// <summary>
        /// Add a loaded callback
        /// 添加完成时行为
        /// </summary>
        /// <param name="callback"></param>
        internal void AddAction(LoaderDelegate.OnBundleLoaded callback)
        {
//            DebugUtil.Log($"AssetBundleRef.AddAction:(Status={_status})");
            if (null == callback || m_Status == AssetBundleStatus.Unload) return;

            if (m_Status == AssetBundleStatus.Loaded)
            {
                callback.Invoke(this, m_Bundle != null);
                return;
            }

            AsyncEvent += callback;
        }

        /// <summary>
        /// Remove callback
        /// 移除行为
        /// </summary>
        /// <param name="callback"></param>
        internal void RemoveAction(LoaderDelegate.OnBundleLoaded callback)
        {
//            DebugUtil.Log($"AssetBundleRef.RemoveAction:(Status={_status})");
            if (null == callback || null == AsyncEvent || m_Status == AssetBundleStatus.Unload ||
                m_Status == AssetBundleStatus.Loaded) return;

            AsyncEvent -= callback;
        }

        /// <summary>
        /// Reduce a temporary record amount
        /// Release resources, condition:
        ///  1. Automatic type resource
        ///  2. The number of temporary resource records <= 0
        /// 减少一临时记录量
        /// 释放资源，条件：
        ///  1. 自动类型资源
        ///  2. 临时资源记录数量 <= 0
        /// </summary>
        internal void ReleaseTemp()
        {
//            DebugUtil.Log(
//                $"AssetBundleRef.ReleaseTemp:(Status={_status},AutoRelease={_autoRelease},TempCount={_tempCount})");
            if (m_AutoRelease)
            {
                m_TempCount -= 1;
                if (m_TempCount <= 0)
                {
                    DoRelease();
                }
            }
        }

        //---------------------------------------

        /// <summary>
        /// Release resources, condition:
        ///  1. Automatic type resource
        ///  2. Non-temporary resources
        /// 释放资源，条件：
        ///  1.自动类型资源
        ///  2.非临时资源 
        /// </summary>
        private void ReleaseAuto()
        {
//            DebugUtil.Log(
//                $"AssetBundleRef.ReleaseAuto:(Status={_status},AutoRelease={_autoRelease},TempCount={_tempCount})");
            if (m_AutoRelease && m_TempCount <= 0)
            {
                DoRelease();
            }
        }

        /// <summary>
        /// Invoke all actions in sequence
        /// 依顺序执行全部行为
        /// </summary>
        private void Invoke()
        {
//            DebugUtil.Log($"AssetBundleRef.Invoke:(Status={_status})");
            if (m_Status == AssetBundleStatus.Loaded)
            {
                AsyncEvent?.Invoke(this, m_Bundle != null);
                AsyncEvent = null;
                if (m_ReleaseAfterInvoke) DoRelease();
            }
        }

        private void UpgradeReleaseStatus(bool autoRelease)
        {
            if (!m_AutoRelease)
            {
                return;
            }

            if (m_Status == AssetBundleStatus.Init || m_Status == AssetBundleStatus.Loading)
            {
                m_AutoRelease = autoRelease;
            }
        }

        private void UpgradedObjectUnloadStatus(bool unloadObjects)
        {
            if (m_UnloadObjects) return;

            if (m_Status == AssetBundleStatus.Init || m_Status == AssetBundleStatus.Loading)
            {
                m_UnloadObjects = unloadObjects;
            }
        }

        /// <summary>
        /// Release resources
        /// 释放资源
        /// </summary>
        private void DoRelease()
        {
//            DebugUtil.Log("AssetBundleRef.DoRelease:", _status);
            if (m_Status == AssetBundleStatus.Loaded)
            {
                if (m_Bundle != null)
                {
                    m_Bundle.Unload(m_UnloadObjects);
                    m_Bundle = null;
                    LoaderEventDispather.Shared.DispatchEvent(LoaderEvents.OnBundleRelease, null);
                }

                m_Status = AssetBundleStatus.Unload;
                BundleRefSet.Dispatcher.DispatchEvent(BundleRefSet.EventReleaseBundle, this);
            }
        }
    }
}