/**********************************************
@title:  ErrorDisplayService.js
@author:  James Erb
@date:    06-11-2007
@rev:     1.2
@desc:    Page-specific error messaging framework for ajax on checkout.
 * Modified:  Andrew Southwick 6/24/07 - moved variables from ErrorManagerStateMonitor to
 * errorDisplayService.stateMonitor and errorDisplayService.constants.
 * @author Andrew Southwick
@assumes: prototype.js 1.5 rel.
@event dependency:  create with onload
********************************/

var ErrorDisplayService = Class.create();
 
ErrorDisplayService.prototype = {


	initialize:function() {
		//if multiple instances required at later date
	},

	hiddenFocusDropDowns:[],
	hiddenPreviewDropDowns:[],
	errorMsgTarget:null,
	
	stateMonitor:{
		errorHasFocus:"false",
		currentErrorCollection:{},
		currentMessageCollection:{},
		currentNamespace:"",
		currentModuleErrorNode:null,
		currentModuleErrorNodeName:null,
		currentModuleMessageNode:null,
		currentModuleMessageNodeName:null,
		currentModuleErrorSummaryNode:null,
		currentModuleErrorSummaryNodeName:null,
		currentModuleMessageSummaryNode:null,
		currentModuleMessageSummaryNodeName:null
	},
	constants:{
		ERROR_STYLE_CLASS:"err",
		MESSAGE_STYLE_CLASS:"messageDisplayService",
		MSG_BBL_ID:"msgBbl",
		MSG_TEXT_ID:"msgTxt",
		MODULE_ERROR_ID_NAME:"moduleError",
		MODULE_MESSAGE_ID_NAME:"moduleMessage",
		LONG_ERROR_MSG_THRESHOLD:100,
		LONG_ERROR_MSG_CLASSNAME:"msgBblWide"
	},
	processErrors:function(errorsCollection, namespace) {
		var errorId = null;
		var errorMessage = null;
		var targetNode = null;
		var targetNodeName = null;
		var summaryTargetNode = null;
		var summaryTargetNodeName = null;
		var hasSummaryTargetNode = false;
		var moduleErrorNodeName = null;
		var moduleErrorNode = null;
		var moduleErrorSummaryNode = null;
		var moduleErrorSummaryNodeName = null;
		var hasModuleError = false;
		var hasSummaryTargetNode = false;
		var hasModuleErrorSummaryNode = false;
		var moduleErrorBuffer = "<ul>";
		errorDisplayService.stateMonitor.currentNamespace = namespace;
		this.clearErrorStyles();
		for (var errorCounter = 0; errorCounter < errorsCollection.length; errorCounter++) {
			hasSummaryTargetNode = false;
			summaryTargetNodeName = null;
			if (errorsCollection[errorCounter].idRo != null) {
				errorId = errorsCollection[errorCounter].idRo;
			}
			if (errorsCollection[errorCounter].errorMessageRo != null) {
				errorMessage = errorsCollection[errorCounter].errorMessageRo;
			}
			if ((errorId != null) && (errorMessage != null)) {
				errorDisplayService.stateMonitor.currentErrorCollection = errorsCollection;
				try {
					/*
					 * Check to see if this error is a moduleError, moduleMessage, or field 
					 * error and handle accordingly.  All modules have have either field errors, 
					 * module errors, or module messages.  
					 * If it is a module error then apply to buffer and set isModuleError to true. 
					 * The buffer is needed because there could be multiple module errors.
					 * If it is a module message then apply to buffer and set isModuleMessage 
					 * to true. 
					 */
					if (errorId == errorDisplayService.constants.MODULE_ERROR_ID_NAME) {
						 moduleErrorBuffer += "<li>" + errorMessage + "</li>";
						 hasModuleError = true;
					}
					else {
						/*
						 * Assume a field error - since this is the only other type of error display related to 
						 * the errorsRo collection.  Process the field error accordingly.
						 */
						targetNodeName = namespace +  errorId.substring(0,1).toUpperCase() + errorId.substring(1, errorId.length);
						summaryTargetNodeName = namespace + "Summary" + errorId.substring(0,1).toUpperCase() + errorId.substring(1, errorId.length);
						targetNode = $(targetNodeName);
						summaryTargetNode = $(summaryTargetNodeName);
						if (summaryTargetNode != null) {
							hasSummaryTargetNode = true;
							errorDisplayService.setFieldErrorNodeForDisplay(summaryTargetNode, errorId);
						}
						if (targetNode != null) {
							errorDisplayService.setFieldErrorNodeForDisplay(targetNode, errorId);
						}
						// Original set of properties.
					}
				}
				catch(e) {
					
				}
			}
		}
		if (hasModuleError == true) {
			/*
			 * Update moduleError DOM node with the contents of the moduleErrorBuffer.  Now that
			 * errorsRo collection inspection and processing is complete - moduleErrorBuffer is 
			 * ready to display.
			 */
			moduleErrorBuffer += "</ul>";
			try {
			 	with (errorDisplayService.constants) {
					// Open module module error display.
					moduleErrorNodeName = namespace +  MODULE_ERROR_ID_NAME.substring(0,1).toUpperCase() + MODULE_ERROR_ID_NAME.substring(1, MODULE_ERROR_ID_NAME.length);
					moduleErrorNode = $(moduleErrorNodeName);
					moduleErrorNode.update(moduleErrorBuffer);
					moduleErrorNode.show();
					/* Summary module module error display.  
					 * Not all summary module displays have any module error display.
					 * This is why the conditional check for null on the SummaryNode is present.
					 */
					moduleErrorSummaryNodeName = namespace +  "Summary" + MODULE_ERROR_ID_NAME.substring(0,1).toUpperCase() + MODULE_ERROR_ID_NAME.substring(1, MODULE_ERROR_ID_NAME.length);
					moduleErrorSummaryNode = $(moduleErrorSummaryNodeName);
					if (moduleErrorSummaryNode != null) {
						hasModuleErrorSummaryNode = true;
						moduleErrorSummaryNode.update(moduleErrorBuffer);
						moduleErrorSummaryNode.show();
					}
					
					
					// Assign to stateMonitor so that the clearErrorStyles() method can hide the module errors when needed.
					errorDisplayService.stateMonitor.currentModuleErrorNode = moduleErrorNode;
					errorDisplayService.stateMonitor.currentModuleErrorNodeName = moduleErrorNodeName;
					if (hasModuleErrorSummaryNode == true) {
						errorDisplayService.stateMonitor.currentModuleErrorSummaryNode = moduleErrorSummaryNode;
						errorDisplayService.stateMonitor.currentModuleErrorSummaryNodeName = moduleErrorSummaryNodeName;
					}
			 	}
			}
			catch(e) {
				
			}
		}
  		errorDisplayService.setFieldErrorFocus();
	},
	setFieldErrorFocus:function() {
		var moduleControl = null;
		var isInSummaryMode = false;
		var firstErrorNode = null;
		var firstErrorNodeId = null;
		var focusErrorNode = null;
		var errorNode = null;
		var errorNodeId = null;
		var errorInputNodes = $$('.' + errorDisplayService.constants.ERROR_STYLE_CLASS);
		var currentNamespace = errorDisplayService.stateMonitor.currentNamespace;
		var summaryNodeIdKey = "Summary";
		var validFieldErrorNodes = $w('INPUT SELECT TEXTAREA'); 

		if (errorInputNodes.length > 0) {

			// Find the 1st valid field error
			for (var i=0;i<errorInputNodes.length;i++) {
				if (errorInputNodes[i].tagName && validFieldErrorNodes.include(errorInputNodes[i].tagName) == true) {
					firstErrorNode = errorInputNodes[i];
					firstErrorNodeId = firstErrorNode.getAttribute("id");
					break;
				}
			}

			/* Errors in the half-collapsed billing state removed.  There are no 
			 * field errors now that occur in the summary state
			 * 
			if (checkout.controller.modules[currentNamespace]) {
				moduleControl = checkout.controller.modules[currentNamespace];
				if (moduleControl.isInSummaryMode) {
					isInSummaryMode = moduleControl.isInSummaryMode;
				}	
			}
			if (isInSummaryMode == true) {
				* 
				 * Summary mode is active.  First check the first node and 
				 * then search the errorInputNodes array for another error 
				 * with "Summary" as its ID naming pattern.
				 *
				if (firstErrorNodeId.indexOf(summaryNodeIdKey) != -1) {
					focusErrorNode = firstErrorNode;
				}
				else {
					for (var errorInputCounter = 1;errorInputCounter < errorInputNodes.length; errorInputCounter++) {
						errorNode = errorInputNodes[errorInputCounter];
						errorNodeId = errorNode.getAttribute("id");
						if (errorNodeId.indexOf(summaryNodeIdKey) != -1) {
							focusErrorNode = errorNode;
							break;
						}
					}								
				}
			}
			else {
				* Not in summary mode so set the firstErrorNode as 
				 * the focusErrorNode.
				 *
				focusErrorNode = firstErrorNode;
			}
			*/
			focusErrorNode = firstErrorNode;
		}
		if (focusErrorNode != null) {
			gidLib.setFocus(focusErrorNode);
		}
	},
	processMessages:function(messagesCollection, namespace) {
		var messageId = null;
		var message = null;
		var moduleMessageNodeName = null;
		var moduleMessageNode = null;
		var moduleMessageSummaryNodeName = null;
		var moduleMessageSummaryNode = null;
		var hasModuleMessage = false;
		var hasModuleMessageSummaryNode = false;
		var moduleMessageBuffer = "<ul>";
		errorDisplayService.stateMonitor.currentNamespace = namespace;
		this.clearMessageDisplay();
		for (var messageCounter = 0; messageCounter < messagesCollection.length; messageCounter++) {
			if (messagesCollection[messageCounter].idRo != null) {
				messageId = messagesCollection[messageCounter].idRo;
			}
			if (messagesCollection[messageCounter].messageRo != null) {
				message = messagesCollection[messageCounter].messageRo;
			}
			if ((messageId != null) && (message != null)) {
				errorDisplayService.stateMonitor.currentMessageCollection = messagesCollection;
				try {
					/*
					 * Check to see if this error is a moduleError, moduleMessage, or field 
					 * error and handle accordingly.  All modules have have either field errors, 
					 * module errors, or module messages.  
					 * If it is a module error then apply to buffer and set isModuleError to true. 
					 * The buffer is needed because there could be multiple module errors.
					 * If it is a module message then apply to buffer and set isModuleMessage 
					 * to true. 
					 */
					if (messageId == errorDisplayService.constants.MODULE_MESSAGE_ID_NAME) {
						 moduleMessageBuffer += "<li>" + message + "</li>";
						 hasModuleMessage = true;
					}
				}
				catch(e) {
					
				}
			}
		}
		if (hasModuleMessage == true) {
			/*
			 * Update moduleMessage DOM node with the contents of the moduleMessageBuffer. Now that
			 * messagesRo collection inspection and processing is complete - moduleMessageBuffer is 
			 * ready to display.
			 */
			moduleMessageBuffer += "</ul>"
			try {
			 	with (errorDisplayService.constants) {
					// Open module module message display.
					moduleMessageNodeName = namespace +  MODULE_MESSAGE_ID_NAME.substring(0,1).toUpperCase() + MODULE_MESSAGE_ID_NAME.substring(1, MODULE_MESSAGE_ID_NAME.length);
					moduleMessageNode = $(moduleMessageNodeName);
					moduleMessageNode.update(moduleMessageBuffer);
					moduleMessageNode.show();
					/* Summary module module error display.  
					 * Not all summary module displays have any module message display.
					 * This is why the conditional check for null on the SummaryNode is present.
					 */
					moduleMessageSummaryNodeName = namespace + "Summary" + MODULE_MESSAGE_ID_NAME.substring(0,1).toUpperCase() + MODULE_MESSAGE_ID_NAME.substring(1, MODULE_MESSAGE_ID_NAME.length);
					moduleMessageSummaryNode = $(moduleMessageSummaryNodeName);
					if (moduleMessageSummaryNode != null) {
						hasModuleMessageSummaryNode = true;
						moduleMessageSummaryNode.update(moduleMessageBuffer);
						moduleMessageSummaryNode.show();
					}

					// Assign to stateMonitor so that the clearErrorStyles() method can hide the module errors and messages when needed.
					errorDisplayService.stateMonitor.currentModuleMessageNode = moduleMessageNode;
					errorDisplayService.stateMonitor.currentModuleMessageNodeName = moduleMessageNodeName;
					if (hasModuleMessageSummaryNode == true) {
						errorDisplayService.stateMonitor.currentModuleMessageSummaryNode = moduleMessageSummaryNode;
						errorDisplayService.stateMonitor.currentModuleMessageSummaryNodeName = moduleMessageSummaryNodeName;
					}
			 	}
			}
			catch(e) {
				
			}
		}
	},
	setFieldErrorNodeForDisplay:function(targetNode, errorId) {
		Element.addClassName(targetNode, errorDisplayService.constants.ERROR_STYLE_CLASS);//NB: inputs
		if (errorId != errorDisplayService.constants.MODULE_MESSAGE_ID_NAME && errorId != errorDisplayService.constants.MODULE_ERROR_ID_NAME) {
			Element.addClassName(targetNode.parentNode, errorDisplayService.constants.ERROR_STYLE_CLASS); //NB: parent node access to highlight child label
			targetNode.onmouseover = this.showErrorPreview;
			targetNode.onmouseout = this.hideErrorPreview
			targetNode.onfocus =  this.showErrorMsg;
			targetNode.onblur =  this.closeMsgBbl;
		}
		targetNode.errorId = errorId;
	},
	clearMessageDisplay:function() {
		var currentModuleMessageNode = errorDisplayService.stateMonitor.currentModuleMessageNode;
		var currentModuleMessageSummaryNode = errorDisplayService.stateMonitor.currentModuleMessageSummaryNode;
		
		if (currentModuleMessageNode != null) {
			currentModuleMessageNode.update();
			currentModuleMessageNode.hide();
			errorDisplayService.stateMonitor.currentModuleMessageNode = null; // Reset to null for further processing of new errors collection.
			errorDisplayService.stateMonitor.currentModuleMessageNodeName = null; // NodeName value for debug inspection.
		}
		if (currentModuleMessageSummaryNode != null) {
			currentModuleMessageSummaryNode.update();
			currentModuleMessageSummaryNode.hide();
			errorDisplayService.stateMonitor.currentModuleMessageSummaryNode = null; // Reset to null for further processing of new errors collection.
			errorDisplayService.stateMonitor.currentModuleMessageSummaryNodeName = null; // NodeName value for debug inspection.
		}
	},

	clearErrorStyles:function(){
		this.closeMsgBbl();
		var previousErrorFields = $$('.' + errorDisplayService.constants.ERROR_STYLE_CLASS );
		var currentModuleErrorNode = errorDisplayService.stateMonitor.currentModuleErrorNode;
		var currentModuleErrorSummaryNode = errorDisplayService.stateMonitor.currentModuleErrorSummaryNode;
		var labelErrorNode = null;
		for (var i=0;i<previousErrorFields.length; i++) {
			Element.removeClassName(previousErrorFields[i], errorDisplayService.constants.ERROR_STYLE_CLASS);
			previousErrorFields[i].onfocus = null; //remove onmouseover method too
			previousErrorFields[i].onmouseover = null;
			previousErrorFields[i].onblur = null;
			previousErrorFields[i].onmouseout = null;
			labelErrorNode = previousErrorFields[i].getElementsBySelector(".labelErrorMessage")[0];
			if (labelErrorNode) {
				labelErrorNode.update();
			}
		}
		if (currentModuleErrorNode != null) {
			currentModuleErrorNode.update();
			errorDisplayService.stateMonitor.currentModuleErrorNode = null; // Reset to null for further processing of new errors collection.
			errorDisplayService.stateMonitor.currentModuleErrorNodeName = null; // NodeName value for debug inspection.
		}
		if (currentModuleErrorSummaryNode != null) {
			currentModuleErrorSummaryNode.update();
			errorDisplayService.stateMonitor.currentModuleErrorSummaryNode = null; // Reset to null for further processing of new errors collection.
			errorDisplayService.stateMonitor.currentModuleErrorSummaryNodeName = null; // NodeName value for debug inspection.
		}
		return;
	},
	
	showErrorMsg:function(){
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		ErrorDisplayService.prototype.errorMsgTarget = this;
		ErrorDisplayService.prototype.populateErrorMsg(this);
		ErrorDisplayService.prototype.setErrorMsgPosition(this);
		msgBbl.style.visibility = "visible";
		msgBbl.style.display = "block";
		this.hiddenFocusDropDowns = gidLib.hideDropDownsUnderElement(msgBbl);
		errorDisplayService.stateMonitor.errorHasFocus = true;
		Event.observe(
			window,
			"resize",
			ErrorDisplayService.prototype.errorMsgResizeHandler
		);
	},
	
	showErrorPreview:function(){
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		
		if(errorDisplayService.stateMonitor.errorHasFocus != true){
			ErrorDisplayService.prototype.populateErrorMsg(this);
			ErrorDisplayService.prototype.setErrorMsgPosition(this);
			msgBbl.style.visibility = "visible";
			msgBbl.style.display = "block";
			this.hiddenPreviewDropDowns = gidLib.hideDropDownsUnderElement(msgBbl);
		}
	},
	
	hideErrorPreview:function(){
		gidLib.showDropDowns(this.hiddenPreviewDropDowns);
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		if(errorDisplayService.stateMonitor.errorHasFocus != true){	
			msgBbl.style.visibility = "hidden";
			msgBbl.style.display = "none";
		}
	},
	
	closeMsgBbl:function() {
		gidLib.showDropDowns(this.hiddenFocusDropDowns);
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		msgBbl.style.visibility = "hidden";
		errorDisplayService.stateMonitor.errorHasFocus = false;
		Event.stopObserving(
			window,
			"resize",
			ErrorDisplayService.prototype.errorMsgResizeHandler
		);
		return;
	},
	
	errorMsgResizeHandler:function() {
		ErrorDisplayService.prototype.setErrorMsgPosition(ErrorDisplayService.prototype.errorMsgTarget);
	},
	
	setErrorMsgPosition:function(target) {
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		var targetDimensions = Element.getDimensions(target);
		var msgBblDimensions = Element.getDimensions(msgBbl);
		var targetPosition = Position.cumulativeOffset(target);
		var newX  =  targetPosition[0];
		var newY = targetPosition[1];
		msgBbl.style.left =   (newX - 20) + 'px';
		msgBbl.style.top = (newY - targetDimensions.height/2 - msgBblDimensions.height) + 'px';	
	},
	
	populateErrorMsg:function(node){
		var constants = errorDisplayService.constants;
		var msgBbl = $(constants.MSG_BBL_ID);
		var msgText = $(constants.MSG_TEXT_ID);
		var labelNode = null;
		var labelErrorNode = null;

		/*
		var nid = node.getAttribute('id');
		var originalId = nid;
		var namespace = errorDisplayService.stateMonitor.currentNamespace;
		nid = nid.substring(namespace.length, nid.length);
		nid = nid.substring(0,1).toLowerCase() + nid.substring(1, nid.length);
		*/
		var errorId = node.errorId;
		var newErrorString = "";
		for(i=0;i<errorDisplayService.stateMonitor.currentErrorCollection.length;i++){
			if(errorDisplayService.stateMonitor.currentErrorCollection[i].idRo == errorId){
				newErrorString = errorDisplayService.stateMonitor.currentErrorCollection[i].errorMessageRo;
			}
			msgText.innerHTML = newErrorString;
			msgBbl.className = (newErrorString && newErrorString.length > constants.LONG_ERROR_MSG_THRESHOLD ? constants.LONG_ERROR_MSG_CLASSNAME  : "");
		}

		// Populate the error message in the label tag for screen readers
		labelNode = $$("label[for="+node.id+"]")[0];
		if (labelNode && newErrorString != "") {
			labelErrorNode = labelNode.getElementsBySelector(".labelErrorMessage")[0]; 
			if (labelErrorNode) {
				labelErrorNode.update(checkout.constants.messages.errors.LABEL_ERROR_MESSAGE_PREFIX + " " + newErrorString);
			}
		}
	}
};

errorDisplayService = new ErrorDisplayService();


