using ImpactCFX.Pooling;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

namespace ImpactCFX.MasterAudio
{
    [AddComponentMenu("Impact CFX/Master Audio/Impact Master Audio Effect Processor")]
    [DisallowMultipleComponent]
    public class ImpactMasterAudioEffectProcessor : ImpactSimpleEffectProcessor<ImpactMasterAudioEffectAuthoring, MasterAudioEffect, MasterAudioEffectResult>
    {
        [SerializeField]
        [Tooltip("Object pool configuration for the 'proxy' Game Objects used to communicate with MasterAudio.")]
        private ObjectPoolConfig proxyObjectPoolConfig;
        [SerializeField]
        [Tooltip("Behavior for attaching effects to one of the colliding objects that triggered the effect.")]
        private EffectAttachMode effectAttachMode;
        [SerializeField]
        [Tooltip("How long audio will fade out when sliding and rolling effects end.")]
        private float audioFadeOutTime = 0.2f;

        private MasterAudioEffectProxyPool audioPool;
        private List<string> soundGroups = new List<string>();

        protected override void OnEnable()
        {
            base.OnEnable();

            GameObject g = new GameObject("Master Audio Effect Proxy Object Pool");
            DontDestroyOnLoad(g);

            audioPool = g.AddComponent<MasterAudioEffectProxyPool>();
            audioPool.Setup(effectAttachMode, audioFadeOutTime);
            audioPool.Initialize(1, proxyObjectPoolConfig);
        }

        protected override void OnDisable()
        {
            base.OnDisable();

            if (audioPool != null && !audioPool.Equals(null))
            {
                audioPool.Destroy();
                audioPool = null;
            }
        }

        protected override MasterAudioEffect getEffect(ImpactMasterAudioEffectAuthoring effectAuthoring)
        {
            MasterAudioEffect audioEffect = effectAuthoring.GetMasterAudioEffect();

            ArrayChunk collisionClipsArrayChunk = new ArrayChunk();
            collisionClipsArrayChunk.Offset = soundGroups.Count;

            foreach (ImpactMasterAudioEffectAuthoring.CollisionSoundGroup collisionSoundGroup in effectAuthoring.CollisionSoundGroups)
            {
                if (!string.IsNullOrEmpty(collisionSoundGroup.SoundGroup) && !collisionSoundGroup.SoundGroup.Equals(DarkTonic.MasterAudio.MasterAudio.NoGroupName))
                {
                    soundGroups.Add(collisionSoundGroup.SoundGroup);
                    collisionClipsArrayChunk.Length += 1;
                }
            }
            audioEffect.CollisionSoundGroupArrayChunk = collisionClipsArrayChunk;

            if (!string.IsNullOrEmpty(effectAuthoring.SlideSoundGroup) && !effectAuthoring.SlideSoundGroup.Equals(DarkTonic.MasterAudio.MasterAudio.NoGroupName))
            {
                audioEffect.SlideSoundGroupIndex = soundGroups.Count;
                soundGroups.Add(effectAuthoring.SlideSoundGroup);
            }
            else
            {
                audioEffect.SlideSoundGroupIndex = -1;
            }

            if (!string.IsNullOrEmpty(effectAuthoring.RollSoundGroup) && !effectAuthoring.RollSoundGroup.Equals(DarkTonic.MasterAudio.MasterAudio.NoGroupName))
            {
                audioEffect.RollSoundGroupIndex = soundGroups.Count;
                soundGroups.Add(effectAuthoring.RollSoundGroup);
            }
            else
            {
                audioEffect.RollSoundGroupIndex = -1;
            }

            //Set here due to timing issues
            if (!queueCapacity.Override)
                queueCapacity.Value = proxyObjectPoolConfig.PoolSize;

            return audioEffect;
        }

        protected override ImpactEffectProcessorJob<MasterAudioEffect, MasterAudioEffectResult> getEffectProcessorJobBase()
        {
            return new ImpactEffectProcessorJob<MasterAudioEffect, MasterAudioEffectResult>();
        }

        public override void ClearAllRegistered()
        {
            base.ClearAllRegistered();

            soundGroups.Clear();
        }

        public override JobHandle ScheduleProcessorJobs(NativeArray<CollisionInputData> collisionData, int collisionDataCount,
            NativeArray<MaterialCompositionData> materialCompositionData,
            NativeArray<ImpactVelocityData> velocityData,
            JobHandle dependencies)
        {
            JobHandle baseJobHandle = base.ScheduleProcessorJobs(collisionData, collisionDataCount, materialCompositionData, velocityData, dependencies);

            //Get indices of pooled objects to use
            ObjectPoolJob<MasterAudioEffectResult> objectPoolJob = new ObjectPoolJob<MasterAudioEffectResult>()
            {
                TemplateID = 1,
                Stealing = audioPool.Stealing,
                PooledObjects = audioPool.GetPooledObjectDataArray(),
                ObjectRequests = effectResults,
                ObjectRequestCount = effectResultCount,
                CurrentFrame = Time.frameCount
            };

            JobHandle objectPoolJobHandle = objectPoolJob.Schedule(baseJobHandle);

            return objectPoolJobHandle;
        }

        public override void PlayEffect(MasterAudioEffectResult effectResult, CollisionResultData collisionResultData)
        {
            base.PlayEffect(effectResult, collisionResultData);

            MasterAudioEffectProxy a = audioPool.RetrieveObject(effectResult.ObjectIndex, effectResult.Priority, effectResult.ContactPointID);
            string soundGroup = soundGroups[effectResult.SoundGroupIndex];

            if (effectResult.IsUpdate)
            {
                a.UpdateMasterAudioEffectCollision(effectResult, collisionResultData);
            }
            else
            {
                a.PlayMasterAudioEffect(effectResult, soundGroup, collisionResultData);
            }
        }

        public override void FixedUpdateProcessor()
        {
            audioPool.UpdatePooledObjects();
        }

        public override void ResetProcessor()
        {
            audioPool.ReturnAllObjectsToPool();
        }

        public override bool HasEffects()
        {
            return effects.Length > 0;
        }
    }
}