var asUtils = {
	window_size : function(w) {
        w = w ? w : window;
        var width = w.innerWidth || (w.document.documentElement.clientWidth || w.document.body.clientWidth);
        var height = w.innerHeight || (w.document.documentElement.clientHeight || w.document.body.clientHeight);
        return [width, height]
	}
};

var Phodyssey = Class.create();

Phodyssey.prototype = {
	// parameters
	user_id : 'asad.sheth',
	debug : false,
	url : '/glibtv2/',
	photos_at_a_time : 4,
	log_container : 'instr_container',
	fly_fps : 30,
	z_pos_increment_multiplier : 0.5,
	
	// internally used
	requested_album : -1,
	displayed_album : -1,
	focussed_photo : -1,
	flying_direction : 'up',	
	fx : {},
	timers : {},
	intervals : {},
	scriptaculous_transitions : {
		elastic : function(pos) {
		    return -1*Math.pow(4,-8*pos) * Math.sin((pos*6-1)*(2*Math.PI)/2) + 1;
		}
	},
	
	initialize : function(params)	{
		Object.extend(this, params);
		this.container = $(this.container);
		this.container.setStyle({
			position : 'relative',
			marginTop : 0,
			marginBottom : '50px',
			padding : 0,
			height : (Math.min(document.viewport.getWidth(), document.viewport.getHeight()) * 0.75) + 'px',
			width : (Math.min(document.viewport.getWidth(), document.viewport.getHeight()) * 0.75) + 'px',
			overflow : 'hidden'
		});
		this.log_container = $(this.log_container);
		this.log_container.setStyle({
			width : (Math.min(document.viewport.getWidth(), document.viewport.getHeight()) * 0.75) + 'px'
		});
		while(this.container.hasChildNodes())
			this.container.removeChild(this.container.firstChild);
		
		// invoke the picasa api
		this.user = new PicasaUser({
			user_id : this.user_id,
			on_user_load : this.process_user.bind(this)
		});

	},
	
	process_user : function(g_user_data)	{
		if(this.user.user_loaded && !this.user.g_user_data)	{
			this.container.innerHTML = 'There was an error loading ' + this.user.user_id + '\'s picasa data.  Check out asad.sheth\'s instead!';
			this.timers.page_reload = setTimeout((function()	{
				// bound to url from glibt object
				window.location.href = this + '?picasa_id=asad.sheth';
			}).bind(this.url), 2000);
			return;
		}
		if(this.user.albums.length == 0)	{
			this.container.innerHTML = this.user.user_id + ' has no public albums!  Check out asad.sheth\'s albums instead.';
			this.timers.page_reload = setTimeout((function()	{
				// bound to url from glibt object
				window.location.href = this + '?picasa_id=asad.sheth';
			}).bind(this.url), 2000);
			return;
		}
		
		// album list is this.user.albums - array of JSON objects
		for(var i = 0; i < this.user.albums.length; i++)	{
			this.user.albums[i].on_album_load = this.process_albums.bind(this);
		}
		
		this.log_container.innerHTML = 'picasa album data for ' + this.user.user_id + ' is being loaded';
	},
	
	process_albums : function(g_album_data)	{
		// are all albums loaded?
		var albums_loaded = 0;
		for(var i = 0; i < this.user.albums.length; i++)	{
			albums_loaded += this.user.albums[i].album_loaded ? 1 : 0;
		}
		
		this.log_container.innerHTML = albums_loaded + ' of ' + this.user.albums.length + ' albums are loaded';
			
		// this block only fires when all albums were loaded
		if(albums_loaded == this.user.albums.length)	{
			this.photo_array = [];
			
			for(var i = 0; i < this.user.albums.length && this.photo_array.length < 1000; i++)	{
				this.photo_array = this.photo_array.concat(this.user.albums[i].photos);
			}
			
			this.log_container.innerHTML = 'ordering photos';
			setTimeout(this.order_photos.bind(this), 25);
		}
	},
	
	order_photos : function()	{
		for(var i = 0; i < this.photo_array.length; i++)	{
			for(var j = i + 1; j < this.photo_array.length; j++)	{
				if(parseInt(this.photo_array[i].timestamp()) < parseInt(this.photo_array[j].timestamp()))	{
					var tmp = this.photo_array[i];
					this.photo_array[i] = this.photo_array[j];
					this.photo_array[j] = tmp;
				}
			}
			
		}
		
		this.log_container.innerHTML = 'building gallery';
		setTimeout(this.show_album.bind(this), 50);
	},
	
	go_to_picasa : function(photo_index)	{
		window.open(this.photo_array[photo_index].picasa_link());
	},
	
	stop_flying : function()	{
		clearInterval(this.intervals.flying);
		this.log_container.lastChild.getElementsByTagName('a')[2].innerHTML = 'start flying';
	},
	
	start_flying : function()	{
		this.intervals.flying = setInterval(this.fly.bind(this), 1000 / this.fly_fps);
		this.log_container.lastChild.getElementsByTagName('a')[2].innerHTML = 'stop flying';
	},
	
	toggle_flying : function()	{
		if(this.log_container.lastChild.getElementsByTagName('a')[2].innerHTML == 'stop flying')
			this.stop_flying();
		else
			this.start_flying();
	},
	
	fly : function()	{
		this.move_user(this.flying_direction);
		
		if(this.user_z_position == 1 - 1 / this.photo_array.length)
			this.flying_direction = 'down';
		if(this.user_z_position == 0 - 1 / this.photo_array.length)
			this.flying_direction = 'up';
	},
	
	show_album : function()	{
		var origin = {
			x : this.container.offsetHeight / 2,
			y : this.container.offsetWidth / 2
		};
		
		var photo_array = this.photo_array;
		
		this.user_z_position = -1 * 1 / photo_array.length;;
		this.photo_z_positions = [];
		this.photo_angles = [];
		
		for(var i = 0; i < photo_array.length; i++)	{
			// compute a random angle
			this.photo_angles[i] = Math.random() * 2 * Math.PI;
			// note the z-position in space of this photo
			this.photo_z_positions[i] = i / photo_array.length;
			
			var img_info = photo_array[i].thumbnail_medium();			
			var img = document.createElement('img');			
			img.src = img_info.url;
			Element.setStyle(img, {
				position : 'absolute',
				zIndex : photo_array.length - i,
				display : 'none',
				opacity : 0,
				background : 'white',
				border: '1px solid white',
				cursor : 'pointer'
			});
			Event.observe(img, 'click', this.go_to_picasa.bind(this, i));
			Event.observe(img, 'mouseover', this.stop_flying.bind(this));
			this.container.appendChild(img);
		}

		// IE doesn't do window handlers?
		if(Prototype.Browser.IE)	{
			Event.observe(document.body, 'keydown', this.handle_keypress.bindAsEventListener(this));
		}
		else	{
			Event.observe(window, 'keydown', this.handle_keypress.bindAsEventListener(this));
		}
		
		// build the controls
		this.log_container.innerHTML = 'this is a starfield for picasa collections.  you can use the up and down arrows to slide through the photo set, and you can click on any image to view it in picasa. mousing over an image will stop your flight.  click the button to start flying again. <div style="margin-top: 10px"><a style="whitespace: no-wrap; background: #aaa; border: 1px solid #444; cursor: pointer; color: #222">more photos at a time</a>, <a style="whitespace: no-wrap; background: #aaa; border: 1px solid #444; cursor: pointer; color: #222">move faster</a>, <a style="whitespace: no-wrap; background: #aaa; border: 1px solid #444; cursor: pointer; color: #222">start flying</a>, <a style="whitespace: no-wrap; background: #aaa; border: 1px solid #444; cursor: pointer; color: #222">move slower</a>, <a style="whitespace: no-wrap; background: #aaa; border: 1px solid #444; cursor: pointer; color: #222">less photos at a time</a></div>';
		// these are all the event listeners to change parameters - speed and photos at a time
		Event.observe(this.log_container.lastChild.getElementsByTagName('a')[2], 'click', this.toggle_flying.bind(this));
		Event.observe(this.log_container.lastChild.getElementsByTagName('a')[1], 'click', this.change_speed.bind(this, 'faster', this.log_container.lastChild.getElementsByTagName('a')[1]));
		Event.observe(this.log_container.lastChild.getElementsByTagName('a')[3], 'click', this.change_speed.bind(this, 'slower', this.log_container.lastChild.getElementsByTagName('a')[3]));
		Event.observe(this.log_container.lastChild.getElementsByTagName('a')[0], 'click', this.change_photos_at_a_time.bind(this, 'more', this.log_container.lastChild.getElementsByTagName('a')[0]));
		Event.observe(this.log_container.lastChild.getElementsByTagName('a')[4], 'click', this.change_photos_at_a_time.bind(this, 'less', this.log_container.lastChild.getElementsByTagName('a')[4]));
		
		// arrange for the first time
		this.arrange_images();
	},
	
	// duh
	change_photos_at_a_time : function(direction, control)	{
		if(direction == 'more')	{
			this.photos_at_a_time = Math.min(15, parseInt(this.photos_at_a_time) + 1);
		}
		else if(direction == 'less')	{
			this.photos_at_a_time = Math.max(2, parseInt(this.photos_at_a_time) - 1);
		}
		
		control.innerHTML = this.photos_at_a_time + ' photos at a time';
		clearTimeout(this.timers['photos_at_a_time_' + direction]);
		this.timers['photos_at_a_time_' + direction] = setTimeout((function(direction)	{
			this.innerHTML = direction + ' photos at a time';
		}).bind(control, direction), 1000);
	},
	
	// handles speed both for auto-fly and keyboard fly
	change_speed : function(direction, control)	{
		if(direction == 'slower')	{
			this.z_pos_increment_multiplier = Math.round(Math.max(0.1, parseFloat(this.z_pos_increment_multiplier) - 0.1) * 10) / 10;
		}
		else if(direction == 'faster')	{
			this.z_pos_increment_multiplier = Math.round(
				Math.min(
					2, parseFloat(this.z_pos_increment_multiplier) + 0.1
				) * 10
			 ) / 10;
		}
		
		control.innerHTML = this.z_pos_increment_multiplier + 'x';
		clearTimeout(this.timers[direction]);
		this.timers[direction] = setTimeout((function(direction)	{
			this.innerHTML = 'move ' + direction;
		}).bind(control, direction), 1000);
	},
	
	handle_keypress : function(evt)	{
		if(evt.keyCode == Event.KEY_UP)	{
			this.move_user('up');
			Event.stop(evt);
			return false;
		}
		else if(evt.keyCode == Event.KEY_DOWN)
			this.move_user('down');
			Event.stop(evt);
			return false;			
	},
	
	// this steps the photo display - simulates moving up or down in z-space
	move_user : function(direction)	{
		var current_pos = this.user_z_position;
		var new_pos = current_pos;
		
		var pos_interval = Math.pow(1 / this.photo_array.length, 1.375) * this.z_pos_increment_multiplier;
		
		if(direction == 'up')	{
			new_pos = Math.min(this.user_z_position + pos_interval, 1 - 1 / this.photo_array.length);	
		}
		if(direction == 'down')	{
			new_pos = Math.max(this.user_z_position - pos_interval, 0 - 1 / this.photo_array.length);	
		}
		
		if(new_pos != current_pos)	{
			this.user_z_position = new_pos;
			this.arrange_images();
		}
	},
	
	// only working in IE for now
	handle_mousewheel : function(evt)	{
		if(evt.wheelDelta > 0)	{
			this.move_user('up');
		}
		else	{
			this.move_user('down');
		}
	},
	
	// puts all the images in the proper place, according to all parameters and user z-position
	arrange_images : function()	{
		var photo_array = this.photo_array;
		
		// this tells us the range of pictures to look at
		var start_index = Math.max(0, parseInt(this.user_z_position * photo_array.length - 2));
		var end_index = Math.min(photo_array.length, start_index + this.photos_at_a_time + 4);

		for(var i = start_index; i < end_index; i++)	{
			var img_info = photo_array[i].thumbnail_large();
			var aspect = img_info.width / img_info.height;
			
			var z_offset = this.photo_z_positions[i] - this.user_z_position;			
			
			// first check if it's within the display range - if it's not, hide it
			if(z_offset < 0 || z_offset > this.photos_at_a_time / this.photo_array.length)	{
				Element.setStyle(this.container.childNodes[i], {
					display : 'none'
				});
			}
			// if it is, move it and show it
			else	{
				var center_offset = Math.pow(1 - z_offset * photo_array.length / this.photos_at_a_time, 2) * Math.min(this.container.offsetWidth / 1.5, this.container.offsetHeight / 1.5);
				var adjusted_height = Math.pow(1 - z_offset * photo_array.length / this.photos_at_a_time, 1.5) * Math.min(this.container.offsetHeight / 2, this.container.offsetWidth / 2);
				var adjusted_width = adjusted_height * aspect;
				
				var center_origin_system_coordinates = {
					x : center_offset * Math.cos(this.photo_angles[i]),
					y : center_offset * Math.sin(this.photo_angles[i])
				};
				var top_left_origin_system_coordinates = this.center_origin_system_coordinates_to_top_left_origin_system_coordinates(center_origin_system_coordinates);
				var image_adjusted_top_left_origin_system_coordinates = {
					x : parseInt(top_left_origin_system_coordinates.x - adjusted_width / 2),
					y : parseInt(top_left_origin_system_coordinates.y - adjusted_height / 2)
				};
				
				Element.setStyle(this.container.childNodes[i], {
					top : image_adjusted_top_left_origin_system_coordinates.y + 'px',
					left : image_adjusted_top_left_origin_system_coordinates.x + 'px',
					height : adjusted_height + 'px',
					width : adjusted_width + 'px',
					display : 'block',
					opacity : 1 - Math.min(1, Math.pow(1 - z_offset * photo_array.length / this.photos_at_a_time, 2))
				});
			}
		}		
	},
	
	center_origin_system_coordinates_to_top_left_origin_system_coordinates : function(center_origin_system_coordinates)	{
		var origin = {
			x : this.container.offsetWidth / 2,
			y : this.container.offsetHeight / 2
		};
		
		return {
			x : parseInt(origin.x + center_origin_system_coordinates.x),
			y : parseInt(origin.y - center_origin_system_coordinates.y)
		};
	}
};

// handle resizes
Event.observe(window, 'resize', function()	{
	// window.glibt.refresh_album();
});

// global ajax handlers
var myGlobalHandlers = {
	ajax_loader : null,
	
	onCreate: function(){
		window.status = 'I\'m working in the background!';
	},

	onComplete: function() {
		if(Ajax.activeRequestCount == 0){
			window.status = 'Done';
		}
	}
};
Ajax.Responders.register(myGlobalHandlers);		
