// Facebook Card Builder
Util.namespace('Holiday2011.Components.FacebookCardBuilder', function(){
	
	// console.log("components.js FacebookCardBuilder");
	
	// Note: Facebook photo sizes available are: 720 180 130 75

	// template grid 4
	var template_g4 = {
		maxWidth: 720,
		photosRequired: 4,
		name: "g4"
	};
	// template grid 6
	var template_g6 = {
		maxWidth: 720,
		photosRequired: 6,
		name: "g6"
	};
	// template column 4
	var template_c4 = {
		maxWidth: 180,
		photosRequired: 30,
		name: "c4"
	}
	// template column 9
	var template_c9 = {
		maxWidth: 75,
		photosRequired: 100,
		name: "c9"
	}
	
	var view;
	var props = {
		appId: "188551017890681",
		permissions: "user_photos",
		authenticateRedirect: "/Card/FB",
		defaultTemplate: "g4"
	};
	
	// the Facebook client for making requests
	var fb = null;
	var canvas = null;
	// an array containing the user's album ids
	var albums = [];
	// a hash containing photos grouped by width and year accessible as photos['width']['year']
	var photos = {};
	// whether or not the initPhotoPools function has finished loading all photo metadata
	var poolsLoaded = false;
	var switchingTemplate = false;
	// the total number of photos the user has
	var totalPhotos = 0;
	// an array containing the photos that are displayed on the current template
	var activePhotos = {
		"g4": [],
		"g6": [],
		"c4": [],
		"c9": []
	};
	var uid = "";
	var authenticated = false;
	var currentYear = new Date().getFullYear();
	// the current template
	var template;
	
    // initialize the component
    var init = function ($view, $props) {
		try
		{
			// console.log("components.js FacebookCardBuilder.init");

	        // set the main view
	        view = $view;
			// set any passed in property values
	        if ($props) {
	            $.extend(props, $props);
	        }
		
			// initialize photos hash
			photos = { 
				'720': {},
				'180': {},
				'75' : {}
			};
			photos['720'][currentYear.toString()] = [];
			photos['180'][currentYear.toString()] = [];
			photos['75' ][currentYear.toString()] = [];
			// console.log("photos:");
			// console.log(photos);

			// initialize the Facebook client for making requests on behalf of the user
	        fb = FacebookClient(props.appId);
	        fb.init();
			// console.log("calling authenticate with permissions: " + props.permissions);
	        fb.authenticate(props.authenticateRedirect, props.permissions, function(user_id){
				try
				{
					uid = user_id;
					authenticated = true;
					initPhotoPools(); // initialize the user's photo metadata			
				}
				catch(err) {
					handleError();
				}
			});

			// randomize photos button
			$('#randomizePhotos', view).click(function () {
				try
				{
					switchTemplate($('.temp.active').attr('id'), true);
				}
				catch(err) {
					handleError();
				}
			});

			// switching templates
			$('.temp', view).click(function () {
				try
				{
					//other templates
					from = $('.temp.active');
					fromTemplate = from.attr('id');
					toTemplate = $(this).attr('id');
					if(fromTemplate != toTemplate) {
						if(switchTemplate(toTemplate, false)){
							from.removeClass("active");
							$(this).addClass("active");
						}
					}
				}
				catch(err) {
					handleError();
				}
			});

			// toggling postal style
			$('#postalStyleToggle', view).click(function () {
				$('#postal-style', view).toggleClass('hide');
				$(this).toggleClass('on');
			});
		
			// toggling happy holidays banner
			$('#happyHolidaysToggle', view).click(function () {
				$('#banner', view).toggleClass('hide');
				$(this).toggleClass('on');
			});
		
			$('#finish', view).click(function() {
				try
				{
					$("#card-builder").addClass("uploading");
					console.log("Beginning upload");
					var bPostalStyle = false;
					if ($('#postalStyleToggle').hasClass("on")) {
						bPostalStyle = true;
					}
					var bBanner = false;
					if ($('#happyHolidaysToggle').hasClass("on")) {
						bBanner = true;
					}
					console.log("Getting image from canvas");
					canvas.getImageToSave("image/png", bPostalStyle, bBanner, function(image){
						console.log("Got image");
						try
						{
							image = image.replace('data:image/png;base64,', '');
							console.log("Posting image");
							$.ajax({
								type: 'POST',
								url: '/Upload/Create',
								data: '{ "imageData" : "' + image + '" }',
								contentType: 'application/json; charset=utf-8',
								success: function(r){
									console.log("Upload succeeded");
									var response = $.parseJSON(r);
									// console.log(response);
									if(response.success) {
										// console.log("upload success: " + response.data.id);
										// console.log(response.data.path)
										// advance to print request form
										window.location = getDomain() + "/Card/Submit/" + response.data.id;
									}
									else {
										console.log("Something went wrong uploading image.");
										$("#card-builder").addClass("error");
										$("#card-builder").removeClass("uploading");
									}
								},
								error: function(r, status, err){
									console.log("Error uploading photo");
									console.log(r);
									console.log(status);
									console.log(err);
									$("#card-builder").addClass("error");
									$("#card-builder").removeClass("uploading");
								}
							});
						}
						catch(err) {
							handleError();
						}
					});
				}
				catch(err) {
					handleError();
				}
			});
		}
		catch(err) {
			handleError();
		}
    };

	function handleError(){
		window.location = getDomain() + "/Error";
	}

	// Queries Facebook to compile metadata about the user's photos.
	// This function populates the albums array and photos hash variables
	// with info about the user's photos. Upon returning from this function
	// the photos variable can be used to access photos by size and by year.
	function initPhotoPools(){
		try
		{
			// console.log("initPhotoPools");

			// Query Facebook for the user's photos using FQL multi-query.
			// This essentially queries for the user's albums first and then uses
			// the resulting albums to grab all the photos from those albums.
			q = {
				"albums": "SELECT aid FROM album WHERE owner=" + uid,
				"photos": "SELECT images,created FROM photo WHERE aid IN (SELECT aid FROM #albums) LIMIT 150"
			};
			fb.multiquery(q, function(response){
				try
				{
					// console.log("processing multiquery result");
					// console.log(response);
					// process the Facebook API multiquery response
					if(typeof response.error_code === "undefined" && response.length == 2) {
						// console.log("got photo metadata");
						// query succeeded and response should be an array with two result sets
						albums = response[0].fql_result_set;
						var allPhotos = response[1].fql_result_set;
						totalPhotos = allPhotos.length;
						for(i = 0; i < allPhotos.length; i++){
							yr = yearFromTimestamp(allPhotos[i].created);
							// allPhotos[i].images contains an array of objects containing width, height, and source
							// each object represents a particular size of the photo
							var hasLarge = false;
							var largeWidth = 0;
							// do a quick pass to determine which width to use as the large
							for(j = 0; j < allPhotos[i].images.length; j++){
								w = allPhotos[i].images[j].width;
								if(w <= 720 && w >= 441 && w > largeWidth) {
									largeWidth = w;
								}
							}
							for(j = 0; j < allPhotos[i].images.length; j++){
								w = allPhotos[i].images[j].width;
								if(w >= 441 && w <= 720) { // a large image
									if(w != largeWidth) {
										continue; // a large image not being used as the large image
									}
									else {
										pushPhoto(allPhotos[i].images[j], 720, yr); // push large image to 720 collection
									}
								}
								else {
									pushPhoto(allPhotos[i].images[j], w, yr);
								}
							}
						}
						// console.log(photos);
						// console.log("setting poolsLoaded to true");
						poolsLoaded = true;
						switchTemplate(props.defaultTemplate, true); // display the default template
					}
					else {
						// encountered an error
						// console.log("Facebook query failed");
						$("#card-builder").addClass("error");
					}
				}
				catch(err) {
					handleError();
				}
			});
		}
		catch(err) {
			handleError();
		}
	}
	
	// Adds the specified photo to the photos hash in the correct 
	// collection for accessing the photo by width and year.
	// The photos hash has the following key/value organization:
	// 
	// 		photos['width']['year']
	//
	// This returns an array of photos where 'width' is one of the standard
	// photo widths (as a string) and year is the year (as a string) the photo
	// was created. For example, you can access photos with width 720 created in 2011
	// using photos['720']['2011'].
	//	
	function pushPhoto(photo, width, year){
		try
		{
			var collection = photos[width.toString()];
			if(collection === "undefined" || collection == null) {
				return; // a non-standard width so skip
			}
			if(collection[year.toString()] === "undefined" || collection[year.toString()] == null) {
				collection[year.toString()] = []; // adding a new year
			}
			collection[year.toString()].push(photo);
		}
		catch(err) {
			handleError();
		}
	}
	
	// Randomly chooses photos of the specifed width for the specified year.
	// The number of photos returned is determined by the specified quantity.
	// If the number of photos available for the specified width and year is less than
	// the specified quantity, then all photos for that width and year are returned.
	function choosePhotos(width, year, quantity){
		try
		{
			var collection = photos[width.toString()];
			if(collection === "undefined" || collection == null) {
				return []; // no results for that width
			}
			collection = collection[year.toString()];
			if(collection === "undefined" || collection == null) {
				return []; // no results for that year
			}
			if(collection.length <= quantity) {
				// have fewer photos than requested so return all photos but in random order
				var temp = collection.slice(0); // start with a copy of all photos
				var random = [];
				while(temp.length > 0){
					idx = getRandomIndex(temp.length);
					random = random.concat(temp.splice(idx,1));
				}
				return random; 
			}
			// have enough photos to randomize
			var temp = collection.slice(0); // start with a copy of all photos
			var random = [];
			while(random.length < quantity){
				idx = getRandomIndex(temp.length);
				random = random.concat(temp.splice(idx,1));
			}
			return random;
		}
		catch(err) {
			handleError();
		}
	}

	// Switches to the specified template.
	//
	// t - may either be one of the defined template vars or one of the following strings:
	//
	//		"g4" - for the grid 4 template
	// 		"g6" - for the grid 6 template
	//		"c4" - for the 4 column template
	//		"c9" - for the 9 column template
	//
	//	randomize - specify true to randomize the photos or false to use the currently chosen photos
	//
	function switchTemplate(t, randomize)
	{
		try
		{
			if(!poolsLoaded){
				// console.log("Cannot switch template. Photo metadata must finish loading first.");
				return false;
			}

			// console.log("components.js switchTemplate: " + t);
			if(!authenticated)
				return false;

			if(switchingTemplate) {
				// console.log("Cannot switch template. Already switching.")
				return false;
			}
			switchingTemplate = true;

			// set the template var based on the specified t
			if(typeof(t)=='string') {
				switch (t) {
					case "g4": template = template_g4; break;
					case "g6": template = template_g6; break;
					case "c4": template = template_c4; break;
					case "c9": template = template_c9; break;
				}
			}
			else {
				template = t;
			}

			if(totalPhotos < 1){
				// console.log("You have no photos loaded.");
				return true;
			}
			// console.log("You have " + totalPhotos + " photos.");

			setTimeout(function(){
				// wait before displaying the processing image in case the rendering completes quickly
				// this keeps the processing image from flickering on and off
				if(switchingTemplate) { // if still switching templates after waiting, it is long running so display processing image
					$("#processing", view).addClass(template.name);
				}
			}, 500);

			// console.log("Randomly choosing <" + template.photosRequired + "> photos");
		
			if(randomize || activePhotos[template.name].length == 0) {
				// console.log("choosing new random photos");
				activePhotos[template.name] = []; // start over
				// randomly choose photos of the appropriate width, starting with the current year
				// and moving back a year at a time until enough photos have been chosen
				var year = currentYear;
				var remaining = template.photosRequired;
				while(remaining > 0){
					// console.log("getting " + remaining);
					chosen = choosePhotos(template.maxWidth, year, remaining);
					activePhotos[template.name] = activePhotos[template.name].concat(chosen);
					remaining -= chosen.length;
					year--;
					// if we've chosen photos from the last 5 years and still don't have enough
					// start again at the current year
					if(year < currentYear-5) {  
						year = currentYear;
					}
				}
			}
			else {
				// console.log("not choosing random photos");
			}
		
			// console.log("updating canvas");
			// update the canvas with the randomly chosen photos
			updateCanvas(activePhotos[template.name]);
			return true;
		}
		catch(err) {
			handleError();
		}
	}
	
	// Redraws the canvas using the current template and the specified photos.
	function updateCanvas(currentPhotos){
		try
		{
			// console.log("updateCanvas");
			if (canvas) {
				canvas.clear();
			}
			else {
				canvas = new Canvas();
				canvas.init('fullCanvas');
			}
			canvas.draw(template, currentPhotos, function() {
				try
				{
					// called once all drawing is complete
					switchingTemplate = false;
					$("#processing", view).removeClass();
				}
				catch(err) {
					handleError();
				}
			});
		}
		catch(err) {
			handleError();
		}
	}
	
	// return public functions to expose
    return {
        'init': init
    };

});

// Instagram Card Builder
Util.namespace('Holiday2011.Components.InstagramCardBuilder', function(){

	// Note: Instagram available sizes: 150 306 612
	
	// console.log("components.js InstagramCardBuilder");

	// template grid 4
	var template_g4 = {
		maxWidth: 612,
		photosRequired: 4,
		name: "g4"
	};
	// template grid 6
	var template_g6 = {
		maxWidth: 612,
		photosRequired: 6,
		name: "g6"
	};
	// template column 4
	var template_c4 = {
		maxWidth: 306,
		photosRequired: 16,
		name: "c4"
	}
	// template column 9
	var template_c9 = {
		maxWidth: 150,
		photosRequired: 81,
		name: "c9"
	}
	
	var view;
	var props = {
		clientId: "3d27065c7f4a404aa057dfb8889f6861",
		authenticateRedirect: "/Card/Instagram",
		defaultTemplate: "g4"
	};
	
	// the Instagram client for making requests
	var instagram = null;
	var canvas = null;
	// an array containing the user's album ids
	var albums = [];
	// a hash containing photos grouped by width and year accessible as photos['width']['year']
	var photos = {};
	// whether or not the initPhotoPools function has finished loading all photo metadata
	var poolsLoaded = false;
	var switchingTemplate = false;
	// the total number of photos the user has
	var totalPhotos = 0;
	// an array containing the photos that are displayed on the current template
	var activePhotos = {
		"g4": [],
		"g6": [],
		"c4": [],
		"c9": []
	};
	var uid = "";
	var authenticated = false;
	var currentYear = new Date().getFullYear();
	// the current template
	var template;
	
    // initialize the component
    var init = function ($view, $props) {
		// console.log("components.js InstagramCardBuilder.init");

        // set the main view
        view = $view;
		// set any passed in property values
        if ($props) {
            $.extend(props, $props);
        }
		
		// initialize photoHash
		photos = { 
			'612': {},
			'306': {},
			'150' : {}
		};
		photos['612'][currentYear.toString()] = [];
		photos['306'][currentYear.toString()] = [];
		photos['150' ][currentYear.toString()] = [];
		// console.log("photos:");
		// console.log(photos);

		// initialize the Instagram client for making requests on behalf of the user
		instagram = InstagramClient(props.clientId);
		instagram.authenticate(props.authenticateRedirect, function(){
			authenticated = true;
			initPhotoPools(); // initialize the user's photo metadata
		});

		// randomize photos button
		$('#randomizePhotos', view).click(function () {
			switchTemplate($('.temp.active').attr('id'), true);
		});

		// switching templates
		$('.temp', view).click(function () {
			//other templates
			from = $('.temp.active');
			fromTemplate = from.attr('id');
			toTemplate = $(this).attr('id');
			if(fromTemplate != toTemplate) {
				if(switchTemplate(toTemplate, false)){
					from.removeClass("active");
					$(this).addClass("active");
				}
			}
		});

		// toggling postal style
		$('#postalStyleToggle', view).click(function () {
			$('#postal-style', view).toggleClass('hide');
			$(this).toggleClass('on');
		});
		
		// toggling happy holidays banner
		$('#happyHolidaysToggle', view).click(function () {
			$('#banner', view).toggleClass('hide');
			$(this).toggleClass('on');
		});
		
		$('#finish', view).click(function() {
			try
			{
				$("#card-builder").addClass("uploading");
				console.log("Beginning upload");
				var bPostalStyle = false;
				if ($('#postalStyleToggle').hasClass("on")) {
					bPostalStyle = true;
				}
				var bBanner = false;
				if ($('#happyHolidaysToggle').hasClass("on")) {
					bBanner = true;
				}
				console.log("Getting image from canvas");
				canvas.getImageToSave("image/png", bPostalStyle, bBanner, function(image){
					console.log("Got image");
					try
					{
						image = image.replace('data:image/png;base64,', '');
						console.log("Posting image");
						$.ajax({
							type: 'POST',
							url: '/Upload/Create',
							data: '{ "imageData" : "' + image + '" }',
							contentType: 'application/json; charset=utf-8',
							success: function(r){
								console.log("Upload succeeded");
								var response = $.parseJSON(r);
								// console.log(response);
								if(response.success) {
									// console.log("upload success: " + response.data.id);
									// console.log(response.data.path)
									// advance to print request form
									window.location = getDomain() + "/Card/Submit/" + response.data.id;
								}
								else {
									console.log("Something went wrong uploading image.");
									$("#card-builder").addClass("error");
									$("#card-builder").removeClass("uploading");
								}
							},
							error: function(r, status, err){
								console.log("Error uploading photo");
								console.log(r);
								console.log(status);
								console.log(err);
								$("#card-builder").addClass("error");
								$("#card-builder").removeClass("uploading");
							}
						});
					}
					catch(err) {
						handleError();
					}
				});
			}
			catch(err) {
				handleError();
			}
		});
    };

	// Queries Instagram to compile metadata about the user's photos.
	// This function populates the albums array and photos hash variables
	// with info about the user's photos. Upon returning from this function
	// the photos variable can be used to access photos by size and by year.
	function initPhotoPools(){
		// console.log("initPhotoPools");

		// Query Instagram for the user's photos
		instagram.request('/users/self/media/recent', 150, function(response){
			if(response.meta.code == 200) {
				var media = response.data;
				// console.log("got photo metadata");
				// console.log(response);
				totalPhotos = media.length;
				for(i = 0; i < media.length; i++) {
					yr = yearFromTimestamp(media[i].created_time);
					pushPhoto({
							source: media[i].images.low_resolution.url, 
							width: media[i].images.low_resolution.width,
							height: media[i].images.low_resolution.height
						}, 306, yr);
					pushPhoto({
							source: media[i].images.thumbnail.url, 
							width: media[i].images.thumbnail.width,
							height: media[i].images.thumbnail.height
						}, 150, yr);
					pushPhoto({
							source: media[i].images.standard_resolution.url, 
							width: media[i].images.standard_resolution.width,
							height: media[i].images.standard_resolution.height
						}, 612, yr);
				}
				// console.log(photos);
				// console.log("setting poolsLoaded to true");
				poolsLoaded = true;
				switchTemplate(props.defaultTemplate, true); // display the default template
			}
			else {
				// console.log("An error occurred requesting photos from Instagram: " + response.meta.error_type + " " + response.meta.code + ": " + response.meta.error_message);
				$("#card-builder").addClass("error");
			}
		});
	}
	
	// Adds the specified photo to the photos hash in the correct 
	// collection for accessing the photo by width and year.
	// The photos hash has the following key/value organization:
	// 
	// 		photos['width']['year']
	//
	// This returns an array of photos where 'width' is one of the standard
	// photo widths (as a string) and year is the year (as a string) the photo
	// was created. For example, you can access photos with width 720 created in 2011
	// using photos['720']['2011'].
	//	
	function pushPhoto(photo, width, year){
		var collection = photos[width.toString()];
		if(collection === "undefined" || collection == null) {
			return; // a non-standard width so skip
		}
		if(collection[year.toString()] === "undefined" || collection[year.toString()] == null) {
			collection[year.toString()] = []; // adding a new year
		}
		collection[year.toString()].push(photo);
	}
	
	// Randomly chooses photos of the specifed width for the specified year.
	// The number of photos returned is determined by the specified quantity.
	// If the number of photos available for the specified width and year is less than
	// the specified quantity, then all photos for that width and year are returned.
	function choosePhotos(width, year, quantity){
		var collection = photos[width.toString()];
		if(collection === "undefined" || collection == null) {
			return []; // no results for that width
		}
		collection = collection[year.toString()];
		if(collection === "undefined" || collection == null) {
			return []; // no results for that year
		}
		if(collection.length <= quantity) {
			// have fewer photos than requested so return all photos but in random order
			var temp = collection.slice(0); // start with a copy of all photos
			var random = [];
			while(temp.length > 0){
				idx = getRandomIndex(temp.length);
				random = random.concat(temp.splice(idx,1));
			}
			return random; 
		}
		// have enough photos to randomize
		var temp = collection.slice(0); // start with a copy of all photos
		var random = [];
		while(random.length < quantity){
			idx = getRandomIndex(temp.length);
			random = random.concat(temp.splice(idx,1));
		}
		return random;
	}

	// Switches to the specified template.
	//
	// t - may either be one of the defined template vars or one of the following strings:
	//
	//		"g4" - for the grid 4 template
	// 		"g6" - for the grid 6 template
	//		"c4" - for the 4 column template
	//		"c9" - for the 9 column template
	//
	//	randomize - specify true to randomize the photos or false to use the currently chosen photos
	//
	function switchTemplate(t, randomize)
	{
		if(!poolsLoaded){
			// console.log("Cannot switch template. Photo metadata must finish loading first.");
			return false;
		}

		// console.log("components.js switchTemplate: " + t);
		if(!authenticated)
			return false;		
		
		if(switchingTemplate) {
			// console.log("Cannot switch template. Already switching.")
			return false;
		}
		switchingTemplate = true;

		// set the template var based on the specified t
		if(typeof(t)=='string') {
			switch (t) {
				case "g4": template = template_g4; break;
				case "g6": template = template_g6; break;
				case "c4": template = template_c4; break;
				case "c9": template = template_c9; break;
			}
		}
		else {
			template = t;
		}
		
		if(totalPhotos < 1){
			// console.log("You have no photos loaded.");
			return true;
		}
		// console.log("You have " + totalPhotos + " photos.");
		
		setTimeout(function(){
			// wait before displaying the processing image in case the rendering completes quickly
			// this keeps the processing image from flickering on and off
			if(switchingTemplate) { // if still switching templates after waiting, it is long running so display processing image
				$("#processing", view).addClass(template.name);
			}
		}, 500);
		
		// console.log("Randomly choosing <" + template.photosRequired + "> photos");
		
		if(randomize || activePhotos[template.name].length == 0) {
			// console.log("choosing new random photos");
			activePhotos[template.name] = []; // start over
			// randomly choose photos of the appropriate width, starting with the current year
			// and moving back a year at a time until enough photos have been chosen
			var year = currentYear;
			var remaining = template.photosRequired;
			while(remaining > 0){
				// console.log("getting " + remaining);
				chosen = choosePhotos(template.maxWidth, year, remaining);
				activePhotos[template.name] = activePhotos[template.name].concat(chosen);
				remaining -= chosen.length;
				year--;
				// if we've chosen photos from the last 5 years and still don't have enough
				// start again at the current year
				if(year < currentYear-5) {  
					year = currentYear;
				}
			}
		}
		else {
			// console.log("not choosing random photos");
		}
		
		// console.log("updating canvas");
		// update the canvas with the randomly chosen photos
		updateCanvas(activePhotos[template.name]);
		return true;
	}
	
	// Redraws the canvas using the current template and the specified photos.
	function updateCanvas(currentPhotos){
		// console.log("updateCanvas");
		if (canvas) {
			canvas.clear();
		}
		else {
			canvas = new Canvas();
			canvas.init('fullCanvas');
		}
		canvas.draw(template, currentPhotos, function() {
			// called once all drawing is complete
			switchingTemplate = false;
			$("#processing", view).removeClass();
		});
	}
	
	// return public functions to expose
    return {
        'init': init
    };

});


