/*
 * Page Script for the ReadBook page.
 */
Seed.Page.ReadBook = Class.create(Seed.Page.GenericPage, {
	
	/*
	 * 1. This handles the table of contents behavior. By default, if there are a lot of sections then we will only
	 * show a fraction of them, relative to the current section being viewed. However, we respect the last state the 
	 * table of contents was in based on the user's actions. That is, we save whether the table of contents is expanded
	 * or collapsed in a cookie, and we restore it's previous state on page load.
	 * 
	 * This also handles hiding extra bookmarks in the bookmarks pnel
	 */
	initialize: function($super, data){
		$super(data);
		Seed.debug(this.data);
		
		/***Intialize Class Variables***/
		//Number of previous sections to show from the current section in the table of contents
		this.numPrevious = 4;
		
		//Maximum number of sections to show in the table of contents
		this.maxVisibleSections = 15;
		
		//Get a list of all sections in the table of contents
		this.sections = $$("#contentslist li");	
		
		//Total number of sections
		this.numSections = this.sections.length;
		
		//Whether we are done with initializing the table of contents. This is used because the first time we hide 
		//extra sections on page load, we don't want to animate it.
		this.initial = true;
		
		//Whether we are collapsing (hiding) any sections in the table of contents
		this.collapsed = false;
		
		//Keeps track of whether the content is set to be fluid or fixed
		this.constrainedState = "fluid";
		
		//The type of bookmark to create. (regular/lastread) THis is set depending on if the user clicks new bookmark or mark last read.
		this.bookmarkType = "regular";
		
		/***Initialize Page Behavior***/
		//hide extra sections in the table of contents
		this.hideExtraSections();
		
		//setup new bookmark link. This link allows users to add a new bookmark
		this.setupNewBookmarkLink();
		
		//setup the last read reusable bookmark
		this.setupLastReadLink();
		
		//If paragraph is specified in the page data, scroll to it
		if(this.data.paragraph !== null){
			var p = $(this.data.paragraph);
			if(p){
				new Effect.ScrollTo(p);
				p.pulsate();
			}
		}
		
		//setup width constrainer link
		this.initConstrainedContentState();
	},
		
	/*
	 * This handles hiding extra sections in the table of contents
	 */
	hideExtraSections: function(){
		//If we have more sections than we want to show, then hide the rest of them
		if(this.numSections > this.maxVisibleSections){
			//get whether the user last had the table of contents expanded or collapsed from a cookie
			var preference = Seed.Browser.Cookie.getData("toc_collapsed");
			
			//if the user did not have it expanded, hide any extra sections
			if(preference !== false){
				this.showHide();
			}
			else{ //else the user last had the table of contents expanded, so keep it that way
				this.initial = false;
			}
		
			//create link to toggle state of the table of contents
			this.createToggleLink();
		}
	},
	
	/*
	 * This creates a link to show more sections, or show fewer sections, based on the state
	 * of the table of contents. When clicked, the link will toggle the toc state.
	 */
	createToggleLink: function(){
		var text = (this.collapsed) ? "show more" : "show fewer";
		var link = new Element("a", {
			href: "#",
			"class": "moreless"
		}).insert(text);
				
		//onclick event handler
		link.observe("click", this.toggleCollapsed.bind(this));
	
		var li = new Element("li").insert(link);
		$("contentslist").insert(li);
	},
	
	/*
	 * This is the event handler for the toggleLink element.
	 * Toggles whether the table of contents is collapsed or fully expanded, changes the link's text
	 * and saves the current state to a cookie
	 */
	toggleCollapsed: function(e){
		e.stop();
		
		//toggle the table of contents state
		this.showHide();
		
		//update the text of the link
		var newlabel = (this.collapsed) ? "show more" : "show fewer";
		e.findElement().firstChild.nodeValue = newlabel;
		
		//save the state of the table of contents to the cookie
		Seed.Browser.Cookie.setData("toc_collapsed", this.collapsed);				
	},
	
	/*
	 * This handles toggling the state of the table of contents. It determines which sections to show, based
	 * on maxVisibleSections and the current section the user is viewing. 
	 */
	showHide: function(){
		//keeps track of how many sections are visible
		var numShown = 0; 
	
		//number of sections left before the last section
		var sectionsLeft = this.data.lastSection - this.data.currentSection;

		//remaining number of sections we can show before reaching max, after subtracting 1 for current section and the remaining number
		//sections.
		var remainder = this.maxVisibleSections - sectionsLeft - this.numPrevious - 1; 
		
		//extra previous sections to show (in addition to numPrevious). In other words, if we have a remainder, cause we are near the 
		//end of the book or something, then we can show more previous sections than normal before reaching the maxVisibleSections number.
		var extraPrevious = Math.max(remainder, 0); 

		for(var i=0; i<this.numSections; i++){
			//if this section is more than (numPrevious) before the current section, then hide it. 
			if(i < this.data.currentSection - (this.numPrevious + extraPrevious)){
				this.toggleSection(this.sections[i]);
			}
			//else if this section is greater than the current section and we reached the max sections visible, hide it
			else if(i > this.data.currentSection){
				if(numShown >= this.maxVisibleSections){
					this.toggleSection(this.sections[i]);					
				}
				else{ //if the section is greater than current but we haven't reached the max yet, we will show it
					numShown++;
				}
			}
			//else if we get here, that means this section is less than (numPrevious) sections before the current one, or it is
			//the current section itself
			else{
				numShown++;
			}
		}	
		
		this.collapsed = !this.collapsed;		
		this.initial = false;	
	},
	
	/*
	 * This shows/hides(toggles) an li element based the state of the table of contents.
	 */
	toggleSection: function(section){
		//if we are hiding stuff on page load, no animation
		if(this.initial){
			section.hide();
		}
		else if(this.collapsed){
			section.blindDown();
		}
		else{
			section.blindUp();
		}	
	},
	
	/*
	 * This sets a click event handler on the new bookmark link to turn bookmarking mode on. Bookmarking mode on 
	 * means the bookmarking banner is visible and the paragraphs are clickable.
	 */
	setupNewBookmarkLink: function(){
		var link = $("new_bookmark_link");
		if(!link) return;
		
		link.observe("click", this.newBookmarkModeOn.bind(this));
	},
	
	/*
	 * This sets a click event handler on the "Mark Last Read" link. The last read link is a updateable bookmark members 
	 * can use to mark where they left off.
	 */
	setupLastReadLink: function(){
		var link = $("last_read_link");
		if(!link) return;
		
		link.observe("click", this.newBookmarkModeOn.bind(this));	
	},
	
	/*
	 * Turns on bookmarking mode. This mode displays the bookmarking banner, and allows a user to click on any paragraph.
	 */
	newBookmarkModeOn: function(e){
		if(e) e.stop();
		
		//set the type of bookmark we are creating
		var elem = e.findElement();
		this.bookmarkType = (elem.id === "last_read_link") ? "lastread" : "regular";
				
		//show the banner
		$("new_bookmark_banner").slideDown().highlight();
		
		//setup the cancel link
		$("new_bookmark_cancel").observe("click", this.newBookmarkModeOff.bind(this));
		
		//go through all the paragraphs in the book and make them highlight on mouseover
		var paragraphs = $$("#content p[id^=p]");
		
		for(var i=0; paragraphs[i]; i++){
			paragraphs[i].observe("mouseenter", this.toggleParagraphHighlight.bind(this));
			paragraphs[i].observe("mouseleave", this.toggleParagraphHighlight.bind(this));
			paragraphs[i].observe("click", this.addNewBookmark.bind(this));			
		}
	},
	
	/*
	 * This turns bookmarking mode off.
	 */
	newBookmarkModeOff: function(e){
		if(e) e.stop(); //prevent default action if this is coming from a click event, (like when user clicks cancel)

		//remove the banner
		$("new_bookmark_banner").shrink();
		
		//remove observers on paragraphs
		var paragraphs = $$("#content p[id^=p]");
		for(var i=0; paragraphs[i]; i++){
			paragraphs[i].stopObserving();
		}
	},	

	/*
	 * This is an event handler for mousing over paragraphs when bookmarking mode is on. It toggles a css class name.
	 */
	toggleParagraphHighlight: function(e){
		e.stop();
		
		var elem = e.findElement("p");
		elem.toggleClassName("bookmark_paragraph_hover");
	},
	
	/*
	 * Add a new bookmark via an Ajax call. This is the event handler for clicking on a paragraph when bookmarking mode is on.
	 */
	addNewBookmark: function(e){
		e.stop();
	
		//get the paragraph that was clicked
		var p = e.findElement("p");
		
		//set the information needed to save a bookmark through ajax call
		var args = {
			section: this.data.currentSectionID,
			paragraph: p.id,
			type: this.bookmarkType
		};
				
		//make ajax request to save bookmark
		new Ajax.Request(this.data.addBookmarkUrl, {
			parameters: args,

			//on success turn bookmarking mode off, and add the new bookmark to the bookmark panel
		 	onSuccess: function(transport){
				this.newBookmarkModeOff();
				
				//remove class name (just in case user never moused-out)
				p.removeClassName("bookmark_paragraph_hover");

				//if we are adding a last read bookmark, and there is already one on the page, delete it
				if(this.bookmarkType === 'lastread'){
					var first = $("bookmarks_list").down("li");
					var firstLink = first.down("a");

					if(firstLink.firstChild.nodeValue == "Last Read"){
						first.remove();
					}
				}
				
				//add the new bookmark to the bookmarks lists
				var li = new Element("li").insert(transport.responseText);
				
				$("bookmarks_list").insert({
					top: li
				});
				
				li.highlight({
					endcolor: "#F3F3CC"
				});
				
				//hide the "no bookmarks set" if it's present
				var noneset = $$("#bookmarks_list .no_bookmarks_set");
				if(noneset || noneset.length > 0){
					noneset[0].style.display = "none";
				}
		   	}.bind(this),
		
			//on 400 error, give the user an alert with the problem encountered
			on400: function(transport){
				alert("There was a problem creating your bookmark: " + transport.responseText);
			},
			
			//on all other failures (like 500) just inform the user an error occured
		 	onFailure: function(){
			 	alert("There was a problem creating your bookmark. Please try again or contact us for help.");
			}
		}); //end ajax request			
	},

	/*
	 * Set the content constrained state based on value from cookie, and add a click event handler to the constrainer link
	 */
	initConstrainedContentState: function(){
		var link = $("width_constrainer_link");
		
		//set state based on cookie
		var preference = Seed.Browser.Cookie.getData("content_constrained_state");
		if(preference){
			this.setContentConstrained(preference, true);
		}
		
		//add click event handler
		link.observe("click", function(e){
			e.stop();
			
			//check for ie6 (not supported)
			if(Prado && Prado.Browser().ie6){
				var msg = "This feature does not support your browser, Internet Explorer 6. Please upgrade to a newer version,"+
				" or another browser such as Firefox or Safari.";
				alert(msg);
				return;
			}
			
			var changeTo = (this.constrainedState === "fluid") ? "fixed" : "fluid";
			this.setContentConstrained(changeTo);
		}.bind(this));
	},
	
	/*
	 * Set whether the content is fixed or fluid
	 * @param {String} constrained Whether the content should be [fluid|fixed]
	 * @param {Boolean} initial Whether this is the initial set done on page load
	 */
	setContentConstrained: function(constrained, initial){	
		var constrainer = $("width_constrainer");
		var link = $("width_constrainer_link");
		var image = $$("#width_constrainer_link img")[0];
			
		if(constrained === "fixed"){
			constrainer.addClassName("constrain_width");
			
			link.firstChild.nodeValue = "Fluid Width";
			image.className = "width_constrainer_fluid";
			
			this.constrainedState = "fixed";
		}
		else{ //make width fluid
			constrainer.removeClassName("constrain_width");
			
			link.firstChild.nodeValue = "Fixed Width";
			image.className = "width_constrainer_fixed";
			
			this.constrainedState = "fluid";
		}
		
		//show a little animation
		if(!initial){
			constrainer.shake({
				distance: 9
			});	
		}

		//save the state of the table of contents to the cookie
		Seed.Browser.Cookie.setData("content_constrained_state", this.constrainedState);
	}
});