using System;
using System.Collections.Generic;
using System.Text;
using Vortex.Utils;

namespace Vortex.Drawing.Particles {

    ///<summary>Particle system is collection of particles which are generated by built-in emitter</summary>
    public class ParticleSystem {
        private IParticleProvider m_Provider;
        private EmitterParameters m_Emitter;
        private ParticleCollection m_Collection = new ParticleCollection();

        private float m_UnemittedParticles = 0.0f;
        private bool m_Continous = true;

        ///<summary>Parameter of emitter</summary>
        public EmitterParameters Emitter {
            get { return m_Emitter; }
        }

        ///<summary>Continous emitting on Update</summary>
        public bool Continous {
            get { return m_Continous; }
            set { m_Continous = value; }
        }

        ///<summary>Create default particle system</summary>
        public ParticleSystem(IParticleProvider provider) {
            m_Provider = provider;
            m_Emitter = new EmitterParameters();
        }

        ///<summary>Create particle system from prepared parameters</summary>
        public ParticleSystem(EmitterParameters parameters, IParticleProvider provider) {
            if (null == parameters) throw new ArgumentNullException("parameters");

            m_Provider = provider;
            m_Emitter = parameters;
        }

        ///<summary>Collection of particles</summary>
        public ParticleCollection Collection {
            get { return m_Collection; }
            set {
                if (null == m_Collection) throw new ArgumentNullException();
                m_Collection = value;
            }
        }

        public void Emit(int particleCount) {
            for (int n = 0; n < particleCount; ++n) {
                BaseParticle particle = m_Provider.CreateParticle();

                switch (Emitter.RegionType) {
                    case EmitterRegionType.Radial:
                        float emitSphereDirection = RandomUtils.NextFloat((float)Math.PI * 2);
                        float emitRadius = Emitter.DistributionRange.From + Emitter.DistributionRange.Difference * RandomUtils.NextFloat();
                        particle.XY = Emitter.Location + new Vector2((float)Math.Cos(emitSphereDirection) * emitRadius, (float)Math.Sin(emitSphereDirection) * emitRadius);
                        break;
                    case EmitterRegionType.Rectangular:
                        float emitOffsetX = Emitter.DistributionRange.From * RandomUtils.NextFloat(-0.5f, 0.5f);
                        float emitOffsetY = Emitter.DistributionRange.To * RandomUtils.NextFloat(-0.5f, 0.5f);
                        float regionDirection = Emitter.Direction;
                        float cosDir = (float)Math.Cos(regionDirection), sinDir = (float)Math.Sin(regionDirection);
                        particle.XY = Emitter.Location + new Vector2(cosDir, sinDir) * emitOffsetX + new Vector2(sinDir, -cosDir) * emitOffsetY;
                        break;
                    default:
                        particle.XY = Emitter.Location;
                        break;
                }

                m_Collection.Add(particle);
            }
        }

        ///<summary>Update particle system: generate new particles from provider and process existing particles</summary>
        public void Update(float timeDelta) {
            if (Continous) {
                m_UnemittedParticles += m_Emitter.Rate * timeDelta;

                float pulseAmount = m_Emitter.PulseAmount > 0 ? m_Emitter.PulseAmount : 1;
                if (m_UnemittedParticles > pulseAmount) {
                    float unemittedLeft = m_UnemittedParticles % pulseAmount;
                    int toEmit = (int)(m_UnemittedParticles - unemittedLeft);

                    Emit(toEmit);

                    m_UnemittedParticles = unemittedLeft;
                }
            }

            m_Collection.Update(timeDelta);
        }

        ///<summary>Draw particles of this system</summary>
        public void Draw(Vortex.Drawing.Canvas2D canvas) {
            Draw(canvas, true);
        }

        ///<summary>Draw particles of this system</summary>
        public void Draw(Vortex.Drawing.Canvas2D canvas, bool reverseOrder) {
            m_Collection.Draw(canvas, reverseOrder);
        }

        ///<summary>Move emitter location</summary>
        public void MoveTo(float x, float y) {
            Emitter.Location = new Vector2(x, y);
        }

        ///<summary>Move emitter location</summary>
        public void MoveTo(Vector2 vector) {
            Emitter.Location = vector;
        }
    }
}
