using UnityEngine;
using System;
using System.Collections.Generic;

namespace mgaia.Utils.Pause
{
    /// <summary>
    /// Behaviour que representa um grupo que pode ter elementos a serem pausados
    /// </summary>
    /// <owner>Victor</owner>
    public class PausableBehaviourGroup : MonoBehaviour
    {
        private bool _mIsPaused;
        private Dictionary<Rigidbody, Vector3> mRigidBodyVelocityCache = new Dictionary<Rigidbody, Vector3>();
        private Dictionary<TrailRenderer, float> mTrailRendererTimeCache = new Dictionary<TrailRenderer, float>();

        private List<Rigidbody> mPausedRigidBodies = new List<Rigidbody>();
        private List<TrailRenderer> mTrailRenderers = new List<TrailRenderer>();
        private List<Animator> mPausedAnimators = new List<Animator>();
        private List<PausableBehaviour> mPausableBehaviours = new List<PausableBehaviour>();
        private List<ParticleSystem> mPausedParticleSystems = new List<ParticleSystem>();
        private Dictionary<Type, Component[]> mPausedGenericComponents = new Dictionary<Type, Component[]>();

        private static Dictionary<Type, BasePausableBehaviourGroupPlugin> sPlugins = new Dictionary<Type, BasePausableBehaviourGroupPlugin>();

        /// <summary>
        /// Adiciona um plugin para tratar o pausamento dos elementos de tipo passado
        /// </summary>
        public static void AddPlugin<T>(BasePausableBehaviourGroupPlugin plugin)
        {
            Type type = typeof(T);

            Assert.AssertTrue(!sPlugins.ContainsKey(type), "Já foi adicionado um plugin para tratar o tipo " + type.Name);

            sPlugins.Add(type, plugin);
        }

        public bool IsPaused
        {
            get { return _mIsPaused; }
        }

        public void SetPauseState(bool isPaused)
        {
            _mIsPaused = isPaused;

            if (isPaused)
            {
                mPausedRigidBodies.AddRange(GetComponentsInChildren<Rigidbody>());
                mTrailRenderers.AddRange(GetComponentsInChildren<TrailRenderer>());
                mPausedAnimators.AddRange(GetComponentsInChildren<Animator>());
                mPausableBehaviours.AddRange(GetComponentsInChildren<PausableBehaviour>());
                mPausedParticleSystems.AddRange(GetComponentsInChildren<ParticleSystem>());

                foreach (Type genericType in sPlugins.Keys)
                {
                    mPausedGenericComponents.Add(genericType, GetComponentsInChildren(genericType));
                }
            }

            SetPauseStateOnRigidbodies(isPaused);
            SetPauseStateOnTrailRenderers(isPaused);
            SetPauseStateOnAnimators(isPaused);
            SetPauseStateOnPausableBehaviours(isPaused);
            SetPauseStateOnParticleSystems(isPaused);
            SetPauseStateOnGenericComponents(isPaused);

            if (!isPaused)
            {
                mPausedRigidBodies.Clear();
                mRigidBodyVelocityCache.Clear();

                mTrailRenderers.Clear();
                mTrailRendererTimeCache.Clear();

                mPausedAnimators.Clear();
                mPausableBehaviours.Clear();
                mPausedParticleSystems.Clear();
                mPausedGenericComponents.Clear();
            }
        }

        private void SetPauseStateOnRigidbodies(bool isPaused)
        {
            foreach (Rigidbody rigidbody in mPausedRigidBodies)
            {
                if (isPaused)
                {
                    mRigidBodyVelocityCache.Add(rigidbody, rigidbody.velocity);
                }

                rigidbody.isKinematic = isPaused;

                if (!isPaused)
                {
                    rigidbody.velocity = mRigidBodyVelocityCache[rigidbody];
                }
            }
        }

        private void SetPauseStateOnTrailRenderers(bool isPaused)
        {
            foreach (TrailRenderer trailRenderer in mTrailRenderers)
            {
                if (isPaused)
                {
                    mTrailRendererTimeCache.Add(trailRenderer, trailRenderer.time);
                }

                trailRenderer.time = float.PositiveInfinity;

                if (!isPaused)
                {
                    trailRenderer.time = mTrailRendererTimeCache[trailRenderer];
                }
            }
        }

        private void SetPauseStateOnAnimators(bool isPaused)
        {
            foreach (Animator animator in mPausedAnimators)
            {
                animator.enabled = !isPaused;
            }
        }

        private void SetPauseStateOnPausableBehaviours(bool isPaused)
        {
            foreach (PausableBehaviour pausableBehaviour in mPausableBehaviours)
            {
                pausableBehaviour.Pause(isPaused);
            }
        }

        private void SetPauseStateOnParticleSystems(bool isPaused)
        {
            foreach (ParticleSystem particleSystem in mPausedParticleSystems)
            {
                if (isPaused)
                {
                    particleSystem.Pause();
                }
                else
                {
                    particleSystem.Play();
                }
            }
        }

        private void SetPauseStateOnGenericComponents(bool isPaused)
        {
            foreach (Type genericType in mPausedGenericComponents.Keys)
            {
                BasePausableBehaviourGroupPlugin plugin = sPlugins[genericType];

                foreach (Component component in mPausedGenericComponents[genericType])
                {
                    plugin.SetPauseState(component, isPaused);
                }
            }
        }
    }
}