using ImpactCFX.Pooling;
using Unity.Mathematics;

namespace ImpactCFX.MasterAudio
{
    public struct MasterAudioEffect : IEffectData<MasterAudioEffectResult>
    {
        public int EffectID { get; set; }
        public ImpactTagMaskFilter IncludeTags { get; set; }
        public ImpactTagMaskFilter ExcludeTags { get; set; }

        public Range VelocityReferenceRange;

        public bool ScaleVolumeWithVelocity;
        public float CollisionNormalInfluence;

        public ArrayChunk CollisionSoundGroupArrayChunk;
        public CollisionSoundGroupSelectionMode CollisionSoundGroupSelectionMode;

        public int SlideSoundGroupIndex;
        public int RollSoundGroupIndex;

        public MasterAudioEffectResult GetResult(CollisionInputData collisionData, MaterialCompositionData materialCompositionData, ImpactVelocityData velocityData, ref Random random)
        {
            MasterAudioEffectResult result = new MasterAudioEffectResult();

            float intensity = EffectUtility.GetCollisionIntensity(velocityData.ImpactVelocity, collisionData.Normal, CollisionNormalInfluence, collisionData.CollisionType);

            if (intensity < VelocityReferenceRange.Min)
            {
#if IMPACTCFX_DEBUG
                ImpactCFXLogger.LogEffectInvalid(GetType(), EffectID, $"Intensity ({intensity}) is less than Velocity Reference Range Min ({VelocityReferenceRange.Min})");
#endif          
                return result;
            }

            float normalizedIntensity = VelocityReferenceRange.Normalize(intensity);

            result.TemplateID = 1;
            result.Priority = collisionData.Priority;
            result.ContactPointID = ContactPointIDGenerator.CalculateContactPointID(collisionData.TriggerObjectID, collisionData.HitObjectID, collisionData.CollisionType, materialCompositionData.MaterialData.MaterialTags.Value, EffectID);
            result.CheckContactPointID = collisionData.CollisionType.RequiresContactPointID();

            result.SoundGroupIndex = getSoundGroupIndex(collisionData.CollisionType, normalizedIntensity, ref random);
            result.VolumeScale = getVolume(normalizedIntensity) * materialCompositionData.Composition;

#if IMPACTCFX_DEBUG
            if (result.SoundGroupIndex == -1)
                ImpactCFXLogger.LogEffectInvalid(GetType(), EffectID, "SoundGroupIndex is -1");
            else if (result.VolumeScale <= 0.01f)
                ImpactCFXLogger.LogEffectInvalid(GetType(), EffectID, "Volume Scale is less than 0.01f");
#endif       

            if (result.TemplateID != 0 && result.SoundGroupIndex > -1 && result.VolumeScale > 0.01f)
            {
                result.IsEffectValid = result.IsObjectPoolRequestValid = true;
            }

            return result;
        }

        private int getSoundGroupIndex(CollisionType collisionType, float normalizedIntensity, ref Random random)
        {
            if (collisionType == CollisionType.Collision)
                return getCollisionSoundGroupIndex(normalizedIntensity, ref random);
            else if (collisionType == CollisionType.Slide)
                return SlideSoundGroupIndex;
            else if (collisionType == CollisionType.Roll)
                return RollSoundGroupIndex;

            return -1;
        }

        private int getCollisionSoundGroupIndex(float normalizedIntensity, ref Random random)
        {
            if (CollisionSoundGroupArrayChunk.Length == 0)
                return -1;

            if (CollisionSoundGroupSelectionMode == CollisionSoundGroupSelectionMode.Velocity)
            {
                int relativeIndex = (int)(math.clamp(normalizedIntensity, 0, 1) * (CollisionSoundGroupArrayChunk.Length - 1));
                return CollisionSoundGroupArrayChunk.Offset + relativeIndex;
            }
            else
            {
                return CollisionSoundGroupArrayChunk.Offset + random.NextInt(0, CollisionSoundGroupArrayChunk.Length);
            }
        }

        private float getVolume(float normalizedIntensity)
        {
            if (ScaleVolumeWithVelocity)
            {
                return normalizedIntensity;
            }
            else
            {
                return 1;
            }
        }
    }

    public struct MasterAudioEffectResult : IEffectResult, IObjectPoolRequest
    {
        //IEffectResult outputs
        public bool IsEffectValid { get; set; }
        public int CollisionIndex { get; set; }
        public int MaterialCompositionIndex { get; set; }

        //IObjectPoolRequest inputs
        public bool IsObjectPoolRequestValid { get; set; }
        public int TemplateID { get; set; }
        public float Priority { get; set; }
        public long ContactPointID { get; set; }
        public bool CheckContactPointID { get; set; }

        //IObjectPoolRequest outputs
        public int ObjectIndex { get; set; }
        public bool IsUpdate { get; set; }

        //Master Audio Effect Outputs
        public int SoundGroupIndex;
        public float VolumeScale;

        public override string ToString()
        {
            return $"[{GetType().Name.ToUpper()}] SoundGroupIndex = {SoundGroupIndex}, VolumeScale = {VolumeScale}";
        }
    }
}
