/**
 *	Copyright (c) 2006 Frozen O Productions
	Written by Shawn Lauriat
	All rights reserved.
 
	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:
 
	* Redistributions of source code must retain the above copyright notice,
		this list of conditions and the following disclaimer.
	* Redistributions in binary form must reproduce the above copyright notice,
		this list of conditions and the following disclaimer in the
		documentation and/or other materials provided with the distribution.
	* Neither the name of Frozen O Productions nor the names of its
		contributors may be used to endorse or promote products derived from
		this software without specific prior written permission.
 
	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
	POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * The base class of all Effects in the library
 */
function Effect() { }
Effect.prototype = new EventDispatcher;

/**
 * Kind of useless by itself, this class exists to get extended for use with
 * text, backgrounds, borders, etc.
 */
function ColorFade() { }
ColorFade.prototype = new Effect;
// Triggers at the start and end of the effect
ColorFade.prototype.events = {
	start : new Array(),
	end : new Array()
};
// Default to changing from a white background to a red one
ColorFade.prototype.start = 'ffffff';
ColorFade.prototype.end = 'ff0000';
// Default to a second, in milliseconds
ColorFade.prototype.duration = 1000;
ColorFade.prototype.step_length = 20;
// The current step (used when running)
ColorFade.prototype.step = 0;
// Reference to the interval (used when running)
ColorFade.prototype.interval = null;
// Calculated values used in color transformation
ColorFade.prototype.steps = 0;
ColorFade.prototype.from = null;
ColorFade.prototype.to = null;
ColorFade.prototype.differences = null;
ColorFade.prototype.current = null;
/**
 * Parse a three or six-character hex string into an array of hex values
 */
ColorFade.prototype.explodeColor = function(color) {
	// Three or six, nothing else
	if (color.length != 6 && color.length != 3) {
		throw 'Unexpected color string lenth of ' + color.length;
	}
	
	var colors = new Array();
	if (color.length == 3) {
		colors[0] = parseInt('0x' + color.charAt(0) + color.charAt(0));
		colors[1] = parseInt('0x' + color.charAt(1) + color.charAt(1));
		colors[2] = parseInt('0x' + color.charAt(2) + color.charAt(2));
	} else {
		colors[0] = parseInt('0x' + color.charAt(0) + color.charAt(1));
		colors[1] = parseInt('0x' + color.charAt(2) + color.charAt(3));
		colors[2] = parseInt('0x' + color.charAt(3) + color.charAt(5));
	}
	return colors;
}
/**
 * Executes the fade from the start to end color
 */
ColorFade.prototype.run = function() {
	this.from = this.explodeColor(this.start);
	this.to = this.explodeColor(this.end);
	this.differences = new Array(
								 this.from[0] - this.to[0],
								 this.from[1] - this.to[1],
								 this.from[2] - this.to[2]
								 );
	
	// Steps in portions
	this.steps = Math.round(this.duration / this.step_length);
	// Reset this so we can run it several times on other objects
	this.step = 0;
	
	clearInterval(this.interval);
	this.interval = setInterval(this.runStep, this.step_length, this);
	
	// Success!
	return true;
}
/**
 * Called from an Interval, takes what this should resolve to and references it
 */
ColorFade.prototype.runStep = function(dit) {
	dit.step++;
	var state = (dit.step / dit.steps);
	dit.current = new Array(
							 dit.from[0] - Math.round(state * dit.differences[0]),
							 dit.from[1] - Math.round(state * dit.differences[1]),
							 dit.from[2] - Math.round(state * dit.differences[2])
							 );
	if (dit.step == dit.steps) {
		clearInterval(dit.interval);
	}
}

/**
 * An event to pass back a reference the the element involved
 */
function ElementEffectEvent() { }
ElementEffectEvent.prototype = new CustomEvent;
ElementEffectEvent.prototype.element = null;

/**
 * An event to pass back some color details as well
 */
function FadeEvent() { }
FadeEvent.prototype = new ElementEffectEvent;
FadeEvent.prototype.start = null;
FadeEvent.prototype.end = null;

function BackgroundFade() { }
BackgroundFade.prototype = new ColorFade;
// The element(s) in question
BackgroundFade.prototype.target = null;

/**
 * Overrides run() to allow optionally passing the element
 */
BackgroundFade.prototype.run = function() {
	// Argument wins over object variable
	if (arguments[0]) {
		this.target = arguments[0];
	}
	
	// This can only run on something that can have a background color
	if (!this.target.style) {
		return false;
	}
	
	if (ColorFade.prototype.run.call(this)) {
		// Override the interval to call the correct method
		clearInterval(this.interval);
		this.interval = setInterval(this.runStep, 10, this);
		var e = new FadeEvent();
		e.start = this.start;
		e.end = this.end;
		e.element = this.target;
		this.dispatchEvent('start', e);
		return true;
	} else {
		return false;
	}
}

/**
 * Overrides runStep to apply the changed color to the element background
 */
BackgroundFade.prototype.runStep = function(dit) {
	ColorFade.prototype.runStep.call(this, dit);
	dit.target.style.backgroundColor = 'rgb('+dit.current[0]+','+dit.current[1]+','+dit.current[2]+')';
	if (dit.step == dit.steps) {
		var e = new FadeEvent();
		e.start = this.start;
		e.end = this.end;
		e.element = this.target;
		this.dispatchEvent('end', e);
	}
}

function ForegroundFade() { }
ForegroundFade.prototype = new BackgroundFade;

/**
 * Overrides runStep to apply the changed color to the element background
 */
ForegroundFade.prototype.runStep = function(dit) {
	ColorFade.prototype.runStep.call(this, dit);
	dit.target.style.color = 'rgb('+dit.current[0]+','+dit.current[1]+','+dit.current[2]+')';
	if (dit.step == dit.steps) {
		var event = new FadeEvent();
		event.start = dit.start;
		event.end = dit.end;
		event.element = dit.target;
		dit.dispatchEvent('end', event);
	}
}

