/*
	Copyright (c) 2010 Peter MacWhinnie

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
 */

/*!
 @class		AppController
 @abstract	The AppController singleton is responsible for most of the interface logic of Essays.
 */
var AppController = {
	
	//MARK: Utilities
	
	/*!
	 @method	handleEssayManifestDownloadFailure
	 @abstract	Present an error message to the user explaining that the essay manifest could not be downloaded.
	 */
	handleEssayManifestDownloadFailure: function(/*String*/textStatus, /*Object*/error) {
		alert("Could not download the essay manifest. Sorry.\n\nStatus: " + textStatus);
	},
	
	/*!
	 @method	handleEssayDownloadFailure
	 @abstract	Present an error message to the user explaining that an essay could not be downloaded.
	 */
	handleEssayDownloadFailure: function(/*String*/essayName, /*String*/textStatus, /*Object*/error) {
		alert("Could not download the essay named «" + essayName + "»\n\nStatus: " + textStatus);
		
		$('#loading_indicator').hide();
	},
	
	//MARK: -
	
	sectionTitles: [],
	
	/*!
	 @method	currentEssay
	 @abstract	Get the selected essay from the source list.
	 */
	currentEssay: function() {
		return this.essayData[this.sourceList.selectedRow()];
	},
	
	/*!
	 @method	presentableStringForDateString
	 @abstract	Returns a human readable date for a date-string.
	 */
	presentableStringForDateString: function(/*String*/dateString) {
		var date = new Date(dateString);
		return "" + date.getFullYear() + "/" + date.getMonth() + "/" + date.getDay();
	},
	
	//MARK: -
	//MARK: Setup
	
	/*!
	 @method	awaken
	 @abstract	The callback invoked after the DOM-ready event has been raised that
				sets up the internal state of the AppController object.
	 */
	awaken: function() {
		Defaults.load();
		
		//Setup the font chooser
		this.fontMenu = new Menu($('#menu_container'));
		
		var action = bind(this, AppController.changeFont);
		this.fontMenu.addItems([
			new Menu.Item("Baskerville", action).setStyle({'font-family': "Baskerville", 'font-size': "14px"}),
			new Menu.Item("Times", action).setStyle({'font-family': "Times", 'font-size': "14px"}),
			new Menu.Item("Times New Roman", action).setStyle({'font-family': "Baskerville", 'font-size': "14px"}),
			new Menu.Item("Courier", action).setStyle({'font-family': "Courier", 'font-size': "12px"}),
			new Menu.Item("Helvetica", action).setStyle({'font-family': "Helvetica", 'font-size': "12px"})
		]);
		
		var selectedFont = Defaults.get('font', 'Baskerville');
		$('#essay_toolbar #font_selector')
			.text(selectedFont)
			.mouseup(function(event) {
				AppController.fontMenu.popUpAt($(event.currentTarget).offset());
			});
		
		//Setup the font size
		$('#essay_toolbar #zoom_out').click(function(event) { AppController.zoomOut() });
		$('#essay_toolbar #zoom_in').click(function(event) { AppController.zoomIn() });
		
		$('#reading_area')
			//Use the users preferred font size
			.css('font-size', Defaults.get('font-size', 16) + 'px')
			.css('font-family', selectedFont)
			//We track scrolling in the reading area so we
			//can persist it for each essay the user views.
			.scroll(function(event) {
				var target = $(event.currentTarget);
				var essayName = AppController.currentEssay().filename;
				if(essayName && target.css('display') != 'hidden')
				{
					Defaults
						.set(essayName + '-scroll-left', target.scrollLeft())
						.set(essayName + '-scroll-top', target.scrollTop())
						.synchronize();
				}
			});
		
		//Setup the section chooser
		$('#essay_toolbar #section_chooser').mouseup(bind(this, AppController.showSectionChooser));
		
		//Setup the fullscreen button
		$('#essay_toolbar #full_screen_button').mouseup(function(event) {
			AppController.toggleFullScreen($(event.currentTarget));
		});
		
		if(Defaults.get('full-screen?', false))
			this.toggleFullScreen($('#essay_toolbar #full_screen_button'));
		
		//Create our source list
		this.sourceList = new ListView($('#essay_list'));
		this.sourceList.delegate = this;
		
		//This allows deselection in the source list
		$('#left_frame').mouseup(function(event) { AppController.sourceList.deselectAll() });
		
		//Load the introductory text
		$('#introduction').load('Resources/Introduction.html');
		
		//Load the essay manifest
		var essaysDownloadedHandler = function(data) {
			this.essayData = data;
			this.sourceList.reloadData();
			this.sourceList.setSelectedRow(Defaults.get('selected-item', -1));
		};
		Essays.downloadAvailableEssays(this, essaysDownloadedHandler, function(request, status, error) { this.handleEssayManifestDownloadFailure(status, error) });
	},
	
	//MARK: -
	//MARK: List Delegate
	
	numberOfItemsInList: function(/*List*/list) {
		return this.essayData.length;
	},
	
	listWillDisplayItem: function(/*List*/list, /*int*/itemIndex, /*jQuery(<li>)*/element) {
		var item = this.essayData[itemIndex];
		
		element.append('<span>' + item.title + '</span>\n<small>' + this.presentableStringForDateString(item.creationDate) + '</small>');
	},
	
	selectionDidChangeInList: function(/*List*/list, /*int*/itemIndex) {
		Defaults.set('selected-item', itemIndex).synchronize();
		if(itemIndex == -1)
		{
			this.hideEssayViewer();
			
			return;
		}
		
		var item = this.essayData[itemIndex];
		
		this.hideEssayViewer();
		$('#loading_indicator').show();
		
		Essays.downloadEssay(item.filename, this, this.presentEssay, function(request, status, error) { this.handleEssayDownloadFailure(item.filename, status, error) });
	},
	
	//MARK: -
	//MARK: Presenting Essays
	
	/*!
	 @method	hideEssayViewer
	 @abstract	Hide the essay viewer.
	 */
	hideEssayViewer: function() {
		$('#reading_area').hide();
		$('#introduction').show();
	},
	
	/*!
	 @method	showEssayViewer
	 @abstract	Show the essay viewer.
	 */
	showEssayViewer: function() {
		$('#introduction').hide();
		$('#reading_area').show();
	},
	
	/*!
	 @method	presentEssay
	 @abstract	Present an essay described as a JSON object.
	 @param		essay	A JSON object which contains a 'title' property, 'creationDate' property, 'author' property, and a 'sections' property.
	 @result	void
	 */
	presentEssay: function(/*JSON*/essay) {
		/*!
		 @function	makeSectionElement
		 @abstract	Make a section element for use in the essay viewer with a specified
					markdown converter and a specified section in the form of a JSON object.
		 @param		markdownConverter
						A Showdown.converter object to process essay section-bodies with.
		 @param		section
						A JSON object which contains a 'title' property and a 'markdown' property.
		 @result	jQuery(<div class="section">)
		 */
		var makeSectionElement = function(/*Showdown.converter*/markdownConverter, /*JSON*/section) {
			var container = $(document.createElement('div'));
			container.addClass('section');
			
			container.append($(document.createElement('div')).addClass('heading').text(section.title));
			
			var sectionBody = markdownConverter.makeHtml(section.markdown);
			container.append($(document.createElement('div')).addClass('body').html(sectionBody));
			
			return container;
		};
		
		
		//The content area is where we show the essay's sections
		var contentArea = $('#reading_area #contents');
		contentArea.empty();
		
		//Update the essay info
		$('#essay_info #title').text(essay.title);
		$('#essay_info #creation_date').text(this.presentableStringForDateString(essay.creationDate));
		$('#essay_info #author').text(essay.author);
		
		//Show the essay's sections
		var markdownConverter = new Showdown.converter();
		this.sectionTitles.splice(0, this.sectionTitles.length);
		essay.sections.forEach(function(section) {
			contentArea.append(makeSectionElement(markdownConverter, section));
			this.sectionTitles.push(section.title);
		}, this);
		
		//Display the end result
		$('#loading_indicator').hide();
		this.showEssayViewer();
		
		//Update the scroll position
		var essayName = this.currentEssay().filename;
		$('#reading_area')
			.scrollLeft(Defaults.get(essayName + '-scroll-left', 0))
			.scrollTop(Defaults.get(essayName + '-scroll-top', 0));
	},
	
	//MARK: -
	//MARK: Actions
	
	/*!
	 @method	zoomOut
	 @abstract	Zoom out the text of the essay reading area one size.
	 */
	zoomOut: function() {
		var readingArea = $('#reading_area');
		
		var fontSize = readingArea.css('font-size');
		var newFontSize = parseInt(fontSize.substr(0, fontSize.length - 2), 10) - 1;
		
		readingArea.css('font-size', newFontSize + 'px');
		
		Defaults.set('font-size', newFontSize).synchronize();
	},
	
	/*!
	 @method	zoomIn
	 @abstract	Zoom in the text of the essay reading area one size.
	 */
	zoomIn: function() {
		var readingArea = $('#reading_area');
		
		var fontSize = readingArea.css('font-size');
		var newFontSize = parseInt(fontSize.substr(0, fontSize.length - 2), 10) + 1;
		
		readingArea.css('font-size', newFontSize + 'px');
		
		Defaults.set('font-size', newFontSize).synchronize();
	},
	
	/*!
	 @method	changeFont
	 @abstract	The callback for the font chooser menu.
	 @param		sender	A Menu.Item object.
	 */
	changeFont: function(/*Menu.Item*/sender) {
		$('#reading_area').css('font-family', sender.title());
		Defaults.set('font', sender.title()).synchronize();
		
		$('#essay_toolbar #font_selector').text(sender.title());
	},
	
	//MARK: -
	
	/*!
	 @method		showSectionChooser
	 @abstract		Change the visible section using the tag of a specified Menu.Item object.
	 @discussion	The tag corresponds to a section in the children of $('#reading_area #children').children()
	 */
	changeSection: function(/*Menu.Item*/sender) {
		var sectionIndex = sender.tag();
		var section = $($('#reading_area #contents').children().get(sectionIndex));
		
		var readingArea = $('#reading_area');
		var newScrollTop = readingArea.scrollTop() + section.position().top;
		readingArea.animate({ scrollTop: newScrollTop }, 500);
	},
	
	/*!
	 @method	showSectionChooser
	 @abstract	Show the section chooser menu in response to the section chooser menu button being clicked by the user.
	 */
	showSectionChooser: function(event) {
		if(!this.currentEssay())
		{
			return;
		}
		
		var action = bind(this, AppController.changeSection);
		
		var sectionMenu = new Menu($('#menu_container'));
		sectionMenu.addItem(new Menu.Item(this.currentEssay().title, function() {
			$('#reading_area').animate({ scrollTop: 0 }, 500);
		}).setStyle({'font-weight': "bold"}));
		sectionMenu.addItem(Menu.Item.separatorItem());
		this.sectionTitles.forEach(function(title, index) {
			var item = new Menu.Item(title, action);
			item.setTag(index);
			sectionMenu.addItem(item);
		}, this);
		
		sectionMenu.popUpAt($(event.currentTarget).offset());
	},
	
	//MARK: -
	
	/*!
	 @method	toggleFullScreen
	 @abstract	Toggle full screen mode.
	 */
	toggleFullScreen: function(/*jQuery(<div>)*/sender) {
		var rightFrame = $('#right_frame');
		if(rightFrame.css('left') == '201px')
		{
			rightFrame.animate({'left': 0}, 200);
			sender.html("&rarr;");
			Defaults.set('full-screen?', true).synchronize();
		}
		else
		{
			rightFrame.animate({'left': 201}, 200);
			sender.html("&larr;");
			Defaults.set('full-screen?', false).synchronize();
		}
	},
};

$(document).ready(function() { AppController.awaken() });

