﻿
function Particle(emitter, posx, posy, velx, vely, accx, accy, life, className, element)
{
	this._emitter = emitter;
	this._posx = posx;
	this._posy = posy;
	this._velx = velx;
	this._vely = vely;
	this._accx = accx;
	this._accy = accy;
	this._age = 0;
	this._life = life;
	this._element = element;
	this._element.className = className;
	this._element.style.position = 'absolute';
	this._element.style.left = parseInt(this._posx) + 'px';
	this._element.style.top = parseInt(this._posy) + 'px';
	this._element.style.width = '12px';
	this._element.style.height = '12px';
	this._element.style.backgroundPosition = '0px 0px';
	this._element.style.display = 'block';
	this._element.style.zIndex = -1000;

	this.set_zIndex = function(zIndex)
	{
		if (this._element) this._element.style.zIndex = zIndex;
	}

	this.Process = Particle_Process;
	this.Dispose = Particle_Dispose;
}

function Particle_Process()
{
	if (this)
	{
		if (this._age >= this._life)
		{
			this.Dispose();
		}
		else
		{
			this._velx += this._accx;
			this._vely += this._accy;
			this._posx += this._velx;
			this._posy += this._vely;
			this._element.style.left = parseInt(this._posx) + 'px';
			this._element.style.top = parseInt(this._posy) + 'px';
			
			var k = this._age / this._life;
			var bgOffset = -12 * parseInt(7 * k);
			this._element.style.opacity = 1.0 - k;
			this._element.style.filter = 'alpha(opacity=' + (100 - k * 100) + ')';
			this._element.style.backgroundPosition = '0px ' + bgOffset + 'px ';
		}
		
		this._age += 1;
	}
}

function Particle_Dispose()
{
	if (this && this._element)
	{
		this._element.style.display = 'none';
		this._element = null;
	}
}

function Emitter(maxParticles, createChance, posx, posy, partvelxlo, partvelxhi, partvelylo, partvelyhi, partaccx, partaccy, partlife, className)
{
	this._maxParticles = maxParticles;
	this._createChance = createChance;
	this._posx = posx;
	this._posy = posy;
	this._offx = 0;
	this._offy = 0;
	this._hotx = 0;
	this._hoty = 0;
	this._partvelxlo = partvelxlo;
	this._partvelxhi = partvelxhi;
	this._partvelylo = partvelylo;
	this._partvelyhi = partvelyhi;
	this._partaccx = partaccx;
	this._partaccy = partaccy;
	this._partlife = partlife;
	this._className = className;
	this._nextNum = 0;
	this._particles = [];
	this._particles.length = maxParticles;
	this._elements = [];
	this._elements.length = maxParticles;
	this._active = true;

	// since creating elements is expensive, do it all now
	for (var i = 0; i < maxParticles; i++)
	{
		var element = document.createElement('div');
		this._elements[i] = element;
		document.body.appendChild(element);
	}

	this.set_active = function(active)
	{
		this._active = active;
	}

	this.set_createchance = function(chance)
	{
		this._createChance = chance;
	}

	this.set_hotspot = function(hotx, hoty)
	{
		this._hotx = hotx;
		this._hoty = hoty;
		this.HandleHotSpot();
	}

	this.set_offset = function(offx, offy)
	{
		this._offx = offx;
		this._offy = offy;
	}

	this.Process = Emitter_Process;
	this.SetPosition = Emitter_SetPosition;
	this.HandleHotSpot = Emitter_HandleHotSpot;
}

function Emitter_Process()
{
	if (this)
	{
		var particle = null;
		for (var i = 0, k = this._maxParticles; i < k; i++)
		{
			particle = this._particles[i];
			if (particle)
			{
				// apply movement and animation
				particle.Process();
			}
		}

		if (this._active && (Math.random() < this._createChance))
		{
			// determine initial position
			var px = this._offx + this._posx + 10 * Math.random() - 5;
			var py = this._offy + this._posy + 10 * Math.random() - 5;

			// determine initial velocity
			var vx = this._partvelxlo + Math.random() * (this._partvelxhi - this._partvelxlo);
			var vy = this._partvelylo + Math.random() * (this._partvelyhi - this._partvelylo);

			this._particles[this._nextNum] = new Particle(this, px, py, vx, vy, this._partaccx, this._partaccy, this._partlife, this._className, this._elements[this._nextNum]);
			this._nextNum = (this._nextNum + 1) % this._maxParticles;
		}

		this.HandleHotSpot();
	}
}

function Emitter_HandleHotSpot()
{
	if (this)
	{
		var particle = null;
		for (var i = 0, k = this._maxParticles; i < k; i++)
		{
			particle = this._particles[i];
			if (particle)
			{
				// if a particle is near the hotspot have it go behind elements to allow clicking
				if (this._hotx >= particle._posx - 4 && this._hotx <= particle._posx + 16 && this._hoty >= particle._posy - 4 && this._hoty <= particle._posy + 16)
				{
					particle.set_zIndex(-1000);
				}
				else
				{
					particle.set_zIndex(2001);
				}
			}
		}
	}
}

function Emitter_SetPosition(posx, posy)
{
	if (this)
	{
		this._posx = posx;
		this._posy = posy;
	}
}

function System(period)
{
	this._period = period;
	this._active = false;
	this._emitters = [];

	// sets the offset of all emitters
	this.set_offset = function(offx, offy)
	{
		for (var i = 0, k = this._emitters.length; i < k; i++)
		{
			this._emitters[i].set_offset(offx, offy);
		}
	};

	// sets the hotspot of all emitters
	this.set_hotspot = function(hotx, hoty)
	{
		for (var i = 0, k = this._emitters.length; i < k; i++)
		{
			this._emitters[i].set_hotspot(hotx, hoty);
		}
	};

	this.Start = System_Start;
	this.Update = System_Update;
	this.Process = System_Process;
	this.AddEmitter = System_AddEmitter;
}

function System_AddEmitter(emitter)
{
	if (this)
	{
		this._emitters.push(emitter);
	}
}

function System_Start()
{
	if (this)
	{
		// flag as active
		this._active = true;
		
		// schedule for next time
		var instance = this;
		window.setTimeout(function() { instance.Update.call(instance); }, this._period);
	}
}

function System_Update()
{
	if (this)
	{
		if (this._active == true)
		{
			// do updates
			this.Process();

			// schedule for next time
			var instance = this;
			window.setTimeout(function() { instance.Update.call(instance); }, this._period);
		}
	}
}

function System_Process()
{
	if (this)
	{
		if (this._active == true)
		{
			for (var i = 0, k = this._emitters.length; i < k; i++)
			{
				this._emitters[i].Process();
			}
		}
	}
}
