// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using HoloToolkit.Unity;
using UnityEngine;

namespace HoloToolkit.Unity.UX
{
    [UseWith(typeof(LineBase))]
    public class LineParticles : LineRendererBase
    {
        const int GlobalMaxParticles = 2048;
        const float GlobalParticleStartLifetime = 0.5f;

        [Header("Particle Settings")]
        public Material LineMaterial;
        [Range(128, GlobalMaxParticles)]
        public int MaxParticles = GlobalMaxParticles;
        [Range(0.001f, 5f)]
        public float ParticleStartLifetime = GlobalParticleStartLifetime;

        [Header("Noise settings")]
        public bool ParticleNoiseOnDisabled = true;
        public Vector3 NoiseStrength = Vector3.one;
        public float NoiseFrequency = 1.2f;
        [Range(1, 10)]
        public int NoiseOcatives = 3;
        [Range(-10f, 10f)]
        public float NoiseSpeed = 1f;
        [Range(0.01f, 0.5f)]
        public float LifetimeAfterDisabled = 0.25f;
        public Gradient DecayGradient;

        [SerializeField]
        private ParticleSystem particles;
        private ParticleSystem.Particle[] mainParticleArray = new ParticleSystem.Particle[GlobalMaxParticles];
        private ParticleSystemRenderer mainParticleRenderer;
        private ParticleSystem.NoiseModule mainNoise;
        private float decayStartTime = 0f;

        protected void OnEnable()
        {
            particles = gameObject.EnsureComponent<ParticleSystem>();

            mainNoise = particles.noise;

            ParticleSystem.EmissionModule emission = particles.emission;
            emission.rateOverTime = new ParticleSystem.MinMaxCurve(0);
            emission.rateOverDistance = new ParticleSystem.MinMaxCurve(0);
            emission.enabled = true;

            ParticleSystem.MainModule main = particles.main;
            main.loop = false;
            main.playOnAwake = false;
            main.maxParticles = Mathf.Min(MaxParticles, GlobalMaxParticles);
            main.simulationSpace = ParticleSystemSimulationSpace.World;

            ParticleSystem.ShapeModule shape = particles.shape;
            shape.enabled = false;

            mainParticleRenderer = particles.GetComponent<ParticleSystemRenderer>();
            mainParticleRenderer.sharedMaterial = LineMaterial;

            // Initialize our particles
            for (int i = 0; i < mainParticleArray.Length; i++)
            {
                ParticleSystem.Particle particle = mainParticleArray[i];
                particle.startColor = Color.white;
                particle.startSize = 1f;
                particle.startLifetime = float.MaxValue;
                particle.remainingLifetime = float.MaxValue;
                particle.velocity = Vector3.zero;
                particle.angularVelocity = 0;
                mainParticleArray[i] = particle;
            }
        }

        private void OnDisable()
        {
            if (mainParticleRenderer != null)
            {
                mainParticleRenderer.enabled = false;
            }
        }

        private void Update()
        {
            if (!Source.enabled)
            {
                mainNoise.enabled = ParticleNoiseOnDisabled;
                mainNoise.strengthX = NoiseStrength.x;
                mainNoise.strengthY = NoiseStrength.y;
                mainNoise.strengthZ = NoiseStrength.z;
                mainNoise.octaveCount = NoiseOcatives;
                mainNoise.scrollSpeed = NoiseSpeed;
                mainNoise.frequency = NoiseFrequency;

                if (decayStartTime < 0)
                {
                    decayStartTime = Time.unscaledTime;
                }
            }
            else
            {
                mainNoise.enabled = false;
                decayStartTime = -1;
            }

            if (Source.enabled)
            {
                for (int i = 0; i < NumLineSteps; i++)
                {
                    float normalizedDistance = (1f / (NumLineSteps - 1)) * i;
                    ParticleSystem.Particle particle = mainParticleArray[i];
                    particle.position = Source.GetPoint(normalizedDistance);
                    particle.startColor = GetColor(normalizedDistance);
                    particle.startSize = GetWidth(normalizedDistance);
                    mainParticleArray[i] = particle;
                }
            }
            else
            {
                int numDecayingParticles = particles.GetParticles(mainParticleArray);
                for (int i = 0; i < numDecayingParticles; i++)
                {
                    float normalizedDistance = (1f / (NumLineSteps - 1)) * i;
                    mainParticleArray[i].startColor = DecayGradient.Evaluate((Time.unscaledTime - decayStartTime) / LifetimeAfterDisabled) * GetColor(normalizedDistance);
                }
            }
            particles.SetParticles(mainParticleArray, NumLineSteps);
        }
    }
}