/**
 *  Manages the embedded LetterSetter.
 *  
 *  @author Brook Elgie
 *  @since  14.05.2008
 */

var LetterSetter = Class.create({
  
  initialize: function() {
    this.LAYOUTS_DATA = "/javascript/lettersetter/formatedlayouts.json";
    this.LETTERSETTER_BASE_URL = "http://lettersetter.net/cgi-bin/ls?foundry=house";
    this.BUTIT_BASE_URL = "/index.php?page=showfont&subpage=pricing";
    this.FONT_BASE_URL = "/font.html";
    this.FONT_BASE_URL = "/fonts/";
    this.DEFAULT_IMAGES_DIR = "/images/transition/lettersetter_defaults/";

    this.imageLoader = document.createElement('img');                                                                 // An image element for preloading the requested ls image.
    this.textHistory = [];																																														// Array of previously used user text.
    this.kit = null;          																																												// Kit object to hold a reference to the collection being displayed on the page (only set once).
    this.userOverriddenFeatures = false;                                                                              // A boolean to determine whether the user has overridden the default features.
    
  	this.houseSorts = new Sorts(this.LAYOUTS_DATA); 																							                    // Create a Sorts object and wait for it to be ready.
  	new PeriodicalExecuter(this.houseSortsReady.bind(this), .2);

  	$('font').observe('change', function(event){																																			// Listen for changes in the font select.
  		this.setLetters(true);
  	}.bind(this));
  	
  	$$('.feature-group input').each(function(feaCheck){                                                               // Listen for clicks on the features check/radio buttons.
    	feaCheck.observe('click', function(ev){
    	  ev.target.blur();
    		this.userOverriddenFeatures = true;
    		this.setLetters();
    	}.bind(this));  	  
  	}.bind(this));
  	
  	$('cultural_sets').observe('change', function(event) {                                                            // Listen for changes to cultural sets.
  	  this.userOverriddenFeatures = true;
  	  this.setLetters();
  	}.bind(this));

  	$('text-history').observe('change', function(event){																															// Listen for selection of the text_history select list.
  		$('user-text').setValue(event.target.getValue());
  		this.setLetters(false);
  	}.bind(this));

  	$('short-direct-link').observe('click', function(event){																													// Listen for click on the direct link input field, so it can be selected.
  		event.target.select();
  	}.bind(this));
  },
  
  /**
   *	Sets the interface once Sorts is ready.
   */
  houseSortsReady: function(pe) {
  	if(this.houseSorts.isReady()){																																										// If the Sorts object is ready ...
  		pe.stop();			 																																																// Stop the PeriodicalExecuter.

  		var qs = String(document.location.href).toQueryParams();              																					// Create a qs object from the query string.

      this.kit = this.determineKit(qs);                                                                               // Determines which kit we're using.
      var lsContainer = $$('.lettersetter')[0];
      if(!this.kit) {
        lsContainer.hide();
        return;                                                                                                       // If a kit couldn't be found, bail.        
      }

      
      $('lettersetter-controls').enable();                                                                            // Enable the controls.

  		this.preSetControls(qs);																																												// Set up the controls.
  		this.setLetters(true, qs);      																																								// Set the image.
  	}
  },
  
  /**
   *  Determines which kit to use for this page based on qs.id, product_id field, or qs.layout.
   */
  determineKit: function(qs) {
    var kit = null;																																													          // Preselects a kit to display based on either 'id' or 'layout' in the query object.
    var product_id = document.getElementById('product_id');
  	if(qs.id != undefined)
  		kit = this.houseSorts.getKitBySalesID(qs.id);
  	else if ($('single-font-kit') && $('single-font-kit').value != "")
  	  kit = this.houseSorts.getKit($('single-font-kit').value, true);
  	else if (product_id)
  	  kit = this.houseSorts.getKitBySalesID(product_id.value);
  	else if (qs.layout)
  		kit = this.houseSorts.getKitByFontName(qs.layout, true);

  	return kit;
  },
  
  /**
   *	Set the new image.
   *
   *	Set feature check boxes if setFeatures is true (usefully omitted when actually manipulating the features).
   *	A qs object is optionally supplied to preset the controls.
   *  notDefault  If true, will force to lettersetter to fetch the version from the ls server.
   */
  setLetters: function(setFeatures, qs, notDefault) {

  	var selectedFontName = $('font').getValue();																																			// Get the currently selected font object and kit object.
  	var fontObj = this.houseSorts.getFont(selectedFontName, this.kit.kitname);

  	if(setFeatures) {																																																	// Set the default fea check states based on the font Object from Sorts.
  		this.setFeaturesInterface(fontObj.features, fontObj.state[0], fontObj.cultural);
  		this.userOverriddenFeatures = false;
  	}

  	if(qs != undefined) {																																													    // If there's a query string object, override some or all of the feature check states.
  		if(qs.tf1 != undefined && fontObj != undefined){																															  // Preset the feature controls if there's a tf1 array and a font object to apply it to.
  			var featuresObj = fontObj.features;
  			var stateObj = this.mergeStateObjects(this.createStateObject(qs.tf1), fontObj.state[0]);										  // Create a merged state object from features in the qs and features in the font object from Sorts.
  			this.setFeaturesInterface(featuresObj, stateObj, fontObj.cultural);																						// Set the features based on this merged state object.
  			this.userOverriddenFeatures = true;
  		}
  	}

  	this.addTextHistory($('user-text').value);																																				// Send the user_text to text_history manager
  	if($('load-indicator') != null) $('load-indicator').show();																												// get the load indicator going

  	Event.observe(this.imageLoader,'load',function(ev){																																// Set up the load event for imageLoader.
  		var imgTag = "<img src='"+this.imageLoader.src+"' ";																														// Create the newly loaded image in an image tag.
  		imgTag += "id='lettersetter-image' ";
  		imgTag += "width='"+fontObj.size[0]+"' ";
  		imgTag += "height='"+fontObj.size[1]+"' ";
  		imgTag += "alt='"+selectedFontName+"' />";
  		$('lettersetter-image-container').update(imgTag);																																// Stick it into the container.

  		$('short-direct-link').setValue('');																																						// Clear the short direct link text
  		this.createShortenedUrl();																																											// Create shortened direct link

      if($('load-indicator') != null) $('load-indicator').hide();                                                     // Deactivate the load indicator

  		Event.stopObserving(this.imageLoader,'load');																																		// Stop observing the image loader.
  	  Event.stopObserving(this.imageLoader,'error');  		
  	}.bind(this));
  	  	

    var imageURL = "";
    if($('user-text').getValue() != "" || this.userOverriddenFeatures || notDefault) {                                // If the user hasn't overridden the default features and there's not user text, use a default image.
      imageURL = this.createLSUrl(fontObj);
      try{
        pageTracker._trackEvent('LetterSetter', 'Set Letters', fontObj.menuname);                                     // Track this action.        
      } catch(e) {}

    } else {
      imageURL = this.DEFAULT_IMAGES_DIR + fontObj.layoutname + ".png";
    	Event.observe(this.imageLoader, 'error', function(ev) {
    	  Event.stopObserving(this.imageLoader,'load');																																	// Stop observing the image loader.
    	  Event.stopObserving(this.imageLoader,'error');  	  
    	  this.setLetters(setFeatures, qs, true);
    	}.bind(this));            
    }

  	this.imageLoader.setAttribute('src',imageURL);																									        	        // Load the the new src url.
  },

  /**
   *	Merges the first state object into the second and returns the unified result. 
   *	The values in the first state object take precedence.
   *	(note that this isn't really a true merge.)
   */
  mergeStateObjects: function(state1, state2) {
  	return $H(state2).merge(state1);
  },
  
  /**
   *	Creates lettersetter url from font object and user selected inputs.
   */
  createLSUrl: function(fontObj) {
  	var imgUrl = this.LETTERSETTER_BASE_URL + "&layout=" + fontObj.layoutname;
  	// Set the user's text if there is some.
  	if ($('user-text').getValue() != "")
  		imgUrl += ("&tb1=" + this.urlEncodeString($('user-text').getValue()));

  	imgUrl += this.createFeaturesQuery();
  	if(fontObj.cultural) imgUrl += this.createCulturalFeatures(fontObj.cultural);

    // console.info("Creating a lettersetter url: %s", imgUrl);
  	return imgUrl;
  },
  
  /**
   *	Returns features as a query string.
   */
  createFeaturesQuery: function() {
  	var feaQuery = "";
  	$$('.feature-group input').each (function(feaCheck){
  		if(!feaCheck.disabled){
  			var state = (feaCheck.checked)? "1":"0";
  			feaQuery += ("&tf1=" + feaCheck.id + "." + state);  			
  		}
  	});
  	
    // Exception for case:
    if($('case').checked) feaQuery += "&tu1=upper";
  	
    // console.info("Creating a Feature Query: %s",  feaQuery);
  	return feaQuery;
  },
  
  /**
   *  Returns required features for the selected cultural set.
   */
  createCulturalFeatures: function(culturalSets) {
    var culturalSelection = $('cultural_sets').getValue();
    var culturalSet = culturalSets.sets[culturalSelection];
    var culturalQuery = "";
    if (culturalSet.lang) culturalQuery += ("&tl1=" + culturalSet.lang);
    if(culturalSet.features) {
      for (var i=0; i<culturalSet.features.length;i++) {
        culturalQuery +=("&tf1=" + culturalSet.features[i] + ".1");
      }
    }
    return culturalQuery;
  },
  
  /**
   *	Returns a features state object based on the tf1 part of the query string.
   */
  createStateObject: function(tf1) {
  	if(typeof tf1 == "string") tf1 = [tf1];

  	var stateObj = {};
  	for (var i=0; i<tf1.length;i++){
  		var state = tf1[i].split('.');
  		stateObj[state[0]] = (state[1] == "1")? true : false;
  	}
  	return stateObj;
  },
  
    
  /**
   *	Adds the passed text argument to the textHistory array
   *	and updates the text_history select element (if its hasn't already been added).
   */
  addTextHistory: function(text){
  	if(text == "")
  		return false;

  	this.textHistory.push(text);									            // Add text to textHistory array.
  	this.textHistory = this.textHistory.uniq();				        // Make sure the array is still unique from duplicates.

  	var options = "";												                  // Create string to update text_history select with.
  	for (var i=0;i<this.textHistory.length;i++) {
  		options += "<option>"+this.textHistory[i]+"</option>\n";
  	}

  	$('text-history').update(options);                  			// Update select element.
  	$('text-history').selectedIndex = -1;		                  // Clear the selected option.
  },
  
  // ***************
  // INTERFACE STUFF


  /**
   *	Populate the font select based on a kit name.
   *	Focus list on fontMenuName if provided.
   */
  populateFontList: function(kitName, fontMenuName) {

  	fonts = this.houseSorts.getFonts(kitName);
  	$('font').update();
  	fonts.each(function(font){
  		var option = Element.extend(document.createElement('option'));
  		option.update(font.menuname);
      if(fontMenuName && font.menuname.toLowerCase().sub(" ", "") == fontMenuName.toLowerCase().sub(" ",""))
        option.writeAttribute('selected', 'selected');
      $('font').insert(option);
  	});

  },

  /**
   *	Sets up the font dropdown and user text field.
   *	Partically determined by page url.
   */
  preSetControls: function(qs) {
  	// console.info("Presetting controls.");

  	var focusFontMenuName = "";																																												// Preselects a font to display based on a layout in the query object (if available).
  	var fontObj = undefined;
  	if(qs.layout) {
  		fontObj = this.houseSorts.getFont(qs.layout, this.kit.kitname, true);
  		focusFontMenuName = fontObj.menuname;
  	}

		// If no fontmenuname, check for single font name.
		if (!focusFontMenuName && $('single-font-name')) focusFontMenuName = $('single-font-name').value;

  	this.populateFontList(this.kit.kitname, focusFontMenuName);       		 																						// Populate font list with currently selected kit and preselected font (if available).

  	if(qs.tb1 != undefined){		  																																										// Presets the user input field if there's a tb1 value in the qs.
  		var txt = unescape(qs.tb1);
  		$('user-text').setValue(txt);
  	}else{
  		$('user-text').clear();
  	}
  },
  
  /**
   *	Sets up the feature interface based on a features object.
   *
   *	state		      An object containing default feature state.
   *  culturalSet   If not undefined, an object containing culural set information.
   */
  setFeaturesInterface: function(features, state, culturalSets) {
  	//console.info("Setting the Features interface.", features, state);
  	if(Object.isHash(state)) state = state.toObject();																																// If the state object is a hash, make it a vanilla object.

  	$$('.feature-group input').each(function(feaCheck){																																// For each feature control (checkbox/radio) ...
  		feaCheck.checked = false;																														 												 		// ... uncheck the input,
  		feaCheck.disable();																																	 												 		// ... disable each feature element.
  		feaCheck.up('dd').addClassName('disabled');

  		if(features.indexOf(feaCheck.id) != -1){																							 												 	// ... If the feature is in the font,
  			feaCheck.enable();																																														// ... enable it,
  			feaCheck.checked = state[feaCheck.id];																									 											// ... and set it's state.
  			feaCheck.up('dd').removeClassName('disabled');
  		}

  	}.bind(this));

    if (culturalSets) this.setCulturalSetsInterface(culturalSets);                                                    // If present, set the cultural set interface.
    else $('cultural_sets').up('dd').addClassName('disabled');

  	this.setFeatureTitles();
  },
  
  /**
   *  Sets the cultural sets drop down. This is a House specific feature that manipulates several OT features.
   */
  setCulturalSetsInterface: function(culturalSets) {
                                                                
    $$('.feature-group input.stylistic_set').each(function(ss) {                                                      // Disable stylistic set interface.
      Event.stopObserving(ss, 'click');
      ss.disable();
      ss.up('dd').addClassName('disabled');
    }.bind(this));
    
    var culturalDropdown = $('cultural_sets');
    culturalDropdown.update();                                                                                        // Clear current contents of dropdown.
    
    var csKeys = Object.keys(culturalSets.sets).sort();
    var defaultKey = culturalSets.default_set;
    var options = "";
    for (var i=0;i<csKeys.length;i++) {                                                                               // Add options to list
      var option = Element.extend(document.createElement('option'));
      option.update(csKeys[i]);
      option.writeAttribute('value', csKeys[i]);
      if (defaultKey == csKeys[i]) option.writeAttribute('selected', 'selected');
      culturalDropdown.insert(option);
    }
    
    $('cultural_sets').up('dd').removeClassName('disabled');                                                          // Show the dropdown.
    
  },
  
  /**
   *	Determines the show/hide state of dt elements based on the disabled state of the associated dd elements.
   */
  setFeatureTitles: function() {
  	$$('.feature-group dt').each(function(el){
  		var hidedt = true;
  		el.nextSiblings().each(function(sib){                                                                           // For each DT, loop through the following DD nodes...
  		  if(sib.nodeName != "DD") throw $break;                                                                        // If we encounter another DT, break.
  			if(!sib.hasClassName('disabled')){                                                                            // If there's a DD without a disabled class...
  				hidedt = false;                                                                                             // Set our hidedt flag to false and break.
  				throw $break;
  			}
  		});
  		if(hidedt) el.hide();
  		else el.show();
  	});
  },  
  
  
  // ***************
  // UTIL STUFF

  /**
   *	Creates a shortened url from a passed url string.
   *	Requires proxy.php to get around xmlhttprequest restrictions.
   */
  
	createShortenedUrl: function() {
		$('short-direct-link').value = '...';
		new Ajax.Request('/gen_url.php?font=' + encodeURIComponent($('font').value) + '&kit=' + encodeURIComponent(this.kit.kitname) + '&url=' + encodeURIComponent($('lettersetter-image').src), {
			method: 'get',
			onSuccess: function(transport) {
				$('short-direct-link').value = transport.responseText;
			}
		});  
	},

  /**
   *	Url encoding function.
   */
  urlEncodeString: function(s) {
  	if (typeof(encodeURIComponent) != "undefined") {
  		return encodeURIComponent(s).replace(/\'/g, '%27');
    } else {
  		return escape(s).replace(/\+/g, '%2B').replace(/\"/g,'%22').rval.replace(/\'/g, '%27');
    }
  }  
  
  
}); 