var g_app = null;
var g_mainScene = null;
var g_lockImagePath = "img/s2/lock/";

/**
	* The lock class, a sprite frame-by-frame animation of a stroke
	* TODO : should generalize this into the engine and port it over
	*		  as the original S2SpriteAnimation.cpp
	*/
var g_lockFrameCoords = [
	/*01*/ new Sweaky2D.Vector2D(320, 289), /*02*/ new Sweaky2D.Vector2D(288, 289),
	/*03*/ new Sweaky2D.Vector2D(256, 289), /*04*/ new Sweaky2D.Vector2D(224, 289),
	/*05*/ new Sweaky2D.Vector2D(192, 289), /*06*/ new Sweaky2D.Vector2D(160, 289),
	/*07*/ new Sweaky2D.Vector2D(128, 272), /*08*/ new Sweaky2D.Vector2D(96, 272),
	/*09*/ new Sweaky2D.Vector2D(64, 256),	/*10*/ new Sweaky2D.Vector2D(32, 230),
	/*11*/ new Sweaky2D.Vector2D(0, 208),	/*12*/ new Sweaky2D.Vector2D(0, 176),
	/*13*/ new Sweaky2D.Vector2D(0, 144),	/*14*/ new Sweaky2D.Vector2D(0, 112),
	/*15*/ new Sweaky2D.Vector2D(0, 80),	/*16*/ new Sweaky2D.Vector2D(32, 53),
	/*17*/ new Sweaky2D.Vector2D(64, 37),	/*18*/ new Sweaky2D.Vector2D(96, 23),
	/*19*/ new Sweaky2D.Vector2D(128, 16),	/*20*/ new Sweaky2D.Vector2D(160, 16),
	/*21*/ new Sweaky2D.Vector2D(192, 16),	/*22*/ new Sweaky2D.Vector2D(224, 16),
	/*23*/ new Sweaky2D.Vector2D(256, 16),	/*24*/ new Sweaky2D.Vector2D(288, 27),
	/*25*/ new Sweaky2D.Vector2D(320, 43),	/*26*/ new Sweaky2D.Vector2D(352, 70),
	/*27*/ new Sweaky2D.Vector2D(368, 106),	/*28*/ new Sweaky2D.Vector2D(368, 136),
	/*29*/ new Sweaky2D.Vector2D(368, 168), /*30*/ new Sweaky2D.Vector2D(365, 201),
	/*31*/ new Sweaky2D.Vector2D(352, 235), /*32*/ new Sweaky2D.Vector2D(326, 253),
	/*33*/ new Sweaky2D.Vector2D(298, 277), /*34*/ new Sweaky2D.Vector2D(263, 296),
	/*35*/ new Sweaky2D.Vector2D(224, 308), /*36*/ new Sweaky2D.Vector2D(192, 311),
	/*37*/ new Sweaky2D.Vector2D(160, 311), /*38*/ new Sweaky2D.Vector2D(128, 311),
	/*39*/ new Sweaky2D.Vector2D(96, 311),	/*40*/ new Sweaky2D.Vector2D(64, 295)
];

Lock = Class.create(Sweaky2D.SceneDrawable, {
	initialize : function($super) {
		$super("Lock");

		this.setSize(395, 314);	// explicitly set the size
		this.frameCount = 40;	// total frame count
		this.frames = [];		// an array of sprites (this is create below, just hard-coded
		this.duration = 0;		// the duration (in total seconds) of this animation
		this.elapsed = 0;		// the amount of time (in seconds) that have elapsed since start
		this.fps = 0;			// the computed number of frames per second
		this.isRunning = false;	// true if this animation is running, otherwise false
		this.isReady = false;
		this.readyCallback = null;

		var mgr = Sweaky2D.SpriteManager.getInstance();
		var itemName;

		// construct the array of sprite frames
		for(var i = 0; i < this.frameCount; i++)
		{
			// reusable name
			itemName = (i + 1).toString();

			// create the texture (i.e. load it)
			mgr.createTexture(itemName, g_lockImagePath + itemName + ".png");

			// assign the sprite from the created texture to this frame
			this.frames[i] = mgr.getSprite(itemName, itemName, this.onItemReady.bind(this));
			this.frames[i].setPosition(g_lockFrameCoords[i].x, g_lockFrameCoords[i].y);
			this.frames[i].setIsVisible(false);
			this.add(this.frames[i]);
		}
	},

	onItemReady : function(obj) {
	
		for(var i = 0; i < this.frameCount; i++)
		{
			if(!this.frames[i].texture.isReady)
				return;
		}

		this.isReady = true;
		
		if(this.readyCallback != null)
			this.readyCallback(this);
	},

	setReadyCallback : function(cb) {
		this.readyCallback = cb;
	},

	reset : function() {
		for(var i = 0; i < this.frameCount; i++)
			this.frames[i].setIsVisible(false);
	},

	/**
		* starts this animation
		*/
	start : function() {
		if(this.duration <= 0)
			return;

		this.fps = this.duration / this.frameCount;
		this.isRunning = true;
	},

	/**
		* stops this animation, the last frame shown will
		* be the final frame visible
		*/
	stop : function() {
		this.isRunning = false;
	},

	/**
		* occurs during each frame update, will compute
		* the index of the frame (sprite) to display
		*/
	onUpdate : function($super, time) {
		$super(time);

		// either it has been stopped or the duration has passed
		if(!this.isRunning)
			return;

		// compute the frame index
		var idx = Math.ceil(this.elapsed / this.fps);

		// ensure we don't go over, if so, stall
		// on the last frame since it is done
		if(idx >= this.frameCount)
		{
			idx = this.frameCount;
			this.isRunning = false;
		}

		// increment the elapsed time (in total seconds)
		this.elapsed += time;

		for(var i = 0; i < idx; i++)
			this.frames[i].setIsVisible(true);
	}
});


/**
	* The watch class, a watch that shows the current time
	* of the user, animated to look like a real watch
	*/
Watch = Class.create(Sweaky2D.SceneDrawable, {
	initialize: function ($super) {
		$super("Watch")

		// explicitly set the size
		this.setSize(114, 292);

		// get the sprites from our manager, the textures 
		// have already been loaded during application
		// creation.
		// FIXME : should put this into a sprite sheet instead
		//		   of having to load individual images
		var spriteManager = Sweaky2D.SpriteManager.getInstance();

		this.watch = spriteManager.getSprite("watch", "watch", this.onWatchSpriteReady.bind(this));
		this.hourHand = spriteManager.getSprite("watch-hour", "hourHand", this.onHourSpriteReady.bind(this));
		this.minHand = spriteManager.getSprite("watch-minute", "minHand", this.onMinuteSpriteReady.bind(this));
		this.secHand = spriteManager.getSprite("watch-second", "secHand", this.onSecondSpriteReady.bind(this));

		// add the sprites, rearrange the order if
		// one hand should be above or below the other, 
		// i think this is how a real watch lays out 
		// their hands
		this.add(this.watch);
		this.add(this.secHand);
		this.add(this.minHand);
		this.add(this.hourHand);
	},

	/**
	* occurs when the hour hand sprite is ready to
	* use, set the position and the point at
	* which to rotate.
	*/
	onHourSpriteReady: function (sprite) {
		sprite.setCenterOfRotation(3, 30);
		sprite.setPosition(44, 162);
	},

	/**
	* occurs when the minute hand sprite is ready to
	* use, set the position and the point at
	* which to rotate.
	*/
	onMinuteSpriteReady: function (sprite) {
		sprite.setCenterOfRotation(3.5, 40);
		sprite.setPosition(46, 150);
	},

	/**
	* occurs when the second hand sprite is ready to
	* use, set the position and the point at
	* which to rotate.
	*/
	onSecondSpriteReady: function (sprite) {
		sprite.setCenterOfRotation(2.5, 36.5);
		sprite.setPosition(46, 154);
	},

	/**
	* occurs when the watch sprite is ready to
	* use, fade the canvas element in
	*/
	onWatchSpriteReady: function (sprite) {
		jQuery("#stage_canvas").fadeIn("fast");
	},

	/**
	* occurs during each frame update, will get
	* the current time and compute the angles
	* required to display the individual hands
	*/
	onUpdate: function ($super, time) {
		$super(time);

		// NOTE : each hand has a +10 degree rotation applied to it
		//		  after the final calculation, this aligns it to best
		//		  align with the ticks on the watch face, adjust this
		//		  if the the hands are off.

		// get the time information
		var now = new Date();
		var hours = now.getHours();
		var mins = now.getMinutes();
		var secs = now.getSeconds();
		var angle = 0;

		// set the rotation for the hour hand
		this.hourHand.setRotation(
			(this.getAngleForHand(((hours % 12) * 5 + mins / 12.0))) + 10);

		// set the rotation for the minute hand
		this.minHand.setRotation(
			(this.getAngleForHand((mins + secs / 60.0))) + 10);

		// set the rotation for the second hand
		this.secHand.setRotation(
			(this.getAngleForHand(secs)) + 10);
	},

	/**
	* gets the angle of rotation for a hand, in degrees
	* @param t - the seconds to compute
	*/
	getAngleForHand: function (t) {
		return (Math.PI * (2.0 * (t / 60.0) - 0.065)) * Sweaky2D.RadianToDegree;
	}
});


/**
	* The main scene of the application, this is the primary container
	* for all the drawable items in the canvas
	*/
MainScene = Class.create(Sweaky2D.Scene, {
	initialize : function($super, name) {
		$super(name);

		// create all the main textures we'll need, the lock textures are
		// created in the lock class during initialization
		var spriteManager = Sweaky2D.SpriteManager.getInstance();
		spriteManager.createTexture("watch", "img/s2/watch.png");
		spriteManager.createTexture("watch-second", "img/s2/seconds.png");
		spriteManager.createTexture("watch-hour", "img/s2/short_hand.png");
		spriteManager.createTexture("watch-minute", "img/s2/long_hand.png");

		// add the coffee steam
		var smoke = new Sweaky2D.ParticleEngine("smoke", null, 25);
		smoke.setSize(400, 400);		// this is meaningless
		smoke.setPosition(530, 365);	// the position of the smoke
		smoke.duration = -1; 			// the duration in seconds, use -1 for infinite
		smoke.gravity.x = 12;			// gravity in the x direction, negative values allowed
		smoke.gravity.y = 0;			// gravity in the y direction, negative values allowed
		smoke.angle.x = -60; 			// the direction of movement, in degrees
		smoke.angle.y = 0; 				// the angle variance, in degrees
		smoke.positionVariance.x = 25;	// position variance in the x direction
		smoke.positionVariance.y = 20;	// position variance in the y direction
		smoke.particleLife.x = 5.25;		// life of the particle
		smoke.particleLife.y = 0;		// particle life variance
		smoke.speed.x = 10;				// particle speed
		smoke.speed.y = 0;				// particle speed variance
		smoke.startSize.x = 125;		// particle starting size
		smoke.startSize.y = 0;			// particle starting size variance
		smoke.endSize.x = 25;			// particle ending size
		smoke.endSize.y = 0;			// particle ending size variance
		smoke.emissionRate = 5.5;		// the rate of emission
		smoke.startColor.r = 1;			// start color red component, float values only
		smoke.startColor.g = 1;			// start color green component, float values only
		smoke.startColor.b = 1;			// start color blue component, float values only
		smoke.startColor.a = 0;			// start color alpha component, float values only
		smoke.startColorVariance.r = 0;	// start color red component variance, float values only
		smoke.startColorVariance.g = 0;	// start color green component variance, float values only
		smoke.startColorVariance.b = 0;	// start color blue component variance, float values only
		smoke.startColorVariance.a = 0;	// start color alpha component variance, float values only
		smoke.endColor.r = 1;			// end color red component, float values only
		smoke.endColor.g = 1;			// end color green component, float values only
		smoke.endColor.b = 1;			// end color blue component, float values only
		smoke.endColor.a = 0.25;			// end color alpha component, float values only
		smoke.endColorVariance.r = 0;	// end color red component variance, float values only
		smoke.endColorVariance.g = 0;	// end color green component variance, float values only
		smoke.endColorVariance.b = 0;	// end color blue component variance, float values only
		smoke.endColorVariance.a = 0;	// end color alpha component variance, float values only

		this.add(smoke);

		// add the watch
		var watch = new Watch();
		watch.setPosition(794, 30);
		watch.setCenterOfRotation(57, 146);
		watch.setRotation(-15);
		this.add(watch);

		// add the lock
		var lock = new Lock();
		this.add(lock);
	}
});

function listenForLockReadyStatus(readyCallback)
{
	var lock = g_mainScene.getByName("Lock");
	
	if(readyCallback != null)
	{
		if(!lock.isReady)
			lock.setReadyCallback(readyCallback);
		else
			readyCallback(lock);
	}

	return lock.isReady;
}

function resetLock()
{
	var lock = g_mainScene.getByName("Lock");

	lock.reset();
}

function startLock(duration, x, y)
{
	if(duration == null)
		duration = 0.75;

	if(x == null)
		x = 22;

	if(y == null)
		y = 70;

	var lock = g_mainScene.getByName("Lock");
	lock.setPosition(x, y);
	lock.duration = duration;
	lock.start();
}

/**
	* handles mouse movements, use the mouse position
	* to modify the scene or scene drawables active
	* positions
	*/

function applySteamGravity(gx, gy)
{
	var smoke = g_mainScene.getByName("smoke");
	var watch = g_mainScene.getByName("Watch");

	/** set some gravity based on some delta **/
	smoke.gravity.x = gx * 20;
	smoke.gravity.y = gy * 20;
}

function toggleSteam(value)
{
	var smoke = g_mainScene.getByName("smoke");
	smoke.setIsVisible(value);
}

function toggleWatch(value)
{
	var watch = g_mainScene.getByName("Watch");
	watch.setIsVisible(value);
}

function toggleLock(value)
{
	var lock = g_mainScene.getByName("Lock");
	lock.setIsVisible(value);
}

function toggleAll(value)
{
	toggleLock(value);
	toggleSteam(value);
	toggleWatch(value);
}

/**
	* initializes and runs the main application, main scene and all
	* drawables within the scene.
	*/
function run(elCanvas, fps)
{
	if(fps == null)
		fps = 30;

	// create the main application, the size is omitted and will be
	// picked up by the actual size set on the canvas object
	g_app = new Sweaky2D.Application(elCanvas);
	g_mainScene = new MainScene("mainScene");

	// add the main scene
	g_app.add(g_mainScene);

	// ensure the main scene is the active scene
	g_app.goToScene("mainScene");

	g_app.getInputManager().disableMouseEvents();
	g_app.getInputManager().disableKeyboardEvents();

	// update fps
	g_app.setFrameRate(fps);

	// begin the render loop
	g_app.run();
}
