/**
 * @license Copyright (c) 2013, Viet Trinh All Rights Reserved.
 * Available via MIT license.
 */

// Cache the gatherer url.
var GATHERER_URL = 'http://gatherer.wizards.com/Pages/Search/Default.aspx?page={page}&sort=cn+&output=checklist&set=[%22{setName}%22]',
	ID_REGEX = /multiverseid=(\d+)/;

/**
 * A module to get the checklist of cards in a set.
 */
define([ 'framework/request/i_module', 
		 'framework/core/utils/clazz',
		 'framework/core/deferred/deferred',
		 'request',
		 'cheerio' ], 
function(IModule, 
		 ClazzUtils,
		 Deferred,
		 request,
		 cheerio)
{
	var GathererGetChecklistModule = function()
		{
			IModule.call(this);

			return this;
		}

	GathererGetChecklistModule.prototype = new IModule();
	ClazzUtils.generateProperties(GathererGetChecklistModule);
	
	/**
	 * Gets the cards by set name via the checklist page of the gatherer.
	 * @param {RequestContext} requestContext
	 *		The request context of the handler calling this function.
	 * @param {String} setName
	 * 		The name of the set to query for.
	 * @returns {Promise} A promise that is resolved with an object containing the 'name' and 'cards' fields, and 
	 *		rejected if the remote query fails.
	 */
	GathererGetChecklistModule.prototype.execute = function(requestContext, setName) 
	{
		var deferred = new Deferred(),
			baseUrl = GATHERER_URL.replace('{setName}', (encodeURIComponent(setName)));

		// Lets start the magic here and request for all of the cards.
		var set = 
			{
				name: setName,
				cards: []
			},
			page = 0;

		// Extract results from each page.
		var extractResults = function(error, response, body)
			{
				
				if (error != null) 
				{
					deferred.reject(error);
					return;
				}
				if (response.statusCode !== 200) 
				{
					deferred.reject('Cannot connect with the server.');
					return;
				}

				try
				{
					// Extract the useful information using cheerio which has similar syntax with jQuery.
					var $ = cheerio.load(body),
						cardNumbers = { 'c0': true };

					$('.cardItem').each(function() 
					{
						var $this = this,
							card = {},
							href = this.find('.name').find('a').attr('href');

						card.setName = setName;
						card.gathererId = parseInt(ID_REGEX.exec(href)[1]);

						$this.find('td').each(function() 
						{
							var $this = this;

							switch ($this.attr('class')) 
							{
								case 'number':
									// Earlier sets didn't have card numbers, therefore we'll just give them arbitrary ones
									// based on alpha order.
									card.number = parseInt($(this).text().trim()) || set.cards.length + 1;
									break;
								case 'name':
									card.name = $(this).text().trim();
									break;
								case 'color':
									card.color = $(this).text().trim().replace('White', 'W').replace('Blue', 'U').replace('Black', 'B')
										.replace('Red', 'R').replace('Green', 'G');
									break;
								case 'rarity':
									card.rarity = $(this).text().trim();
									break;
							}
						});

						// Set the card to rare if the rarity is not there.
						if (!card.rarity || card.rarity == 'S')
						{
							card.rarity = 'R'; 
						}

						// Only allow unique card numbers to be added, the double faced cards are listed as two cards 
						// with the same number. 
						if (!cardNumbers[ 'c' + card.number ])
						{
							set.cards.push(card);
							cardNumbers[ 'c' + card.number ] = card;
						}
						// If the card is already taken, then just append the name.
						// There's no way to tell which name goes first, therefore we'll have to edit it later.
						// So, flip cards are listed in order, however split cards are not.
						else
						{
							cardNumbers[ 'c' + card.number ].name = cardNumbers[ 'c' + card.number ].name + ' / ' + card.name;
						}
					});
					
					set.cards.sort(function(a, b)
					{
						return a.number - b.number;
					});
				} 
				catch (e) 
				{
					deferred.reject('Gatherer Server - Cannot parse the remote content.');
				}

				// If we have more than 1 page, then visit the rest of the pages. At this point we can tell how many 
				// pages are left.
				if (page === 0)
				{
					var pages = $('.termdisplay').text().trim(),
						totalPages = Math.ceil(+pages.substring(pages.indexOf('(') + 1, pages.indexOf(')')) / 100.0),
						pagePromises = [];

					// This is where JavaScript rules, aync requests all the way!!!
					for (var i = 1;i < totalPages;i++)
					{
						page++;
						(function(i)
						{
							var localDeferred = new Deferred();
							
							pagePromises.push(localDeferred.promise());

							request({ url: baseUrl.replace('{page}', page) }, function(error, response, body)
							{
								extractResults.call(this, error, response, body)

								// If we have any failures, then the extract results function would have already
								// called the reject on the master deferred.
								localDeferred.resolve();
							});
						})(i);
					}

					// When all of the other pages come back and are parsed, then resolve the master deferred.
					Deferred.when.apply(Deferred, pagePromises).done(function()
					{
						set.cards.length > 0 ? deferred.resolve(set) : deferred.reject('Gatherer Server - The set does not exist.');
					});
				}
			};

		// Start the results call.
		request({ url: baseUrl.replace('{page}', page) }, function(error, response, body)
		{
			extractResults(error, response, body);
		});

		return deferred.promise();
	};

	return GathererGetChecklistModule;
});