/**
 * 
 */
package com.tandbergtv.workflow.web.page;

import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.tandbergtv.workflow.web.common.StaticCodes;

/**
 * This class maintains the cache of all the dynamic jsp page objects configured
 * in the page-config.xml file.
 * 
 * ONLY A BABOON COULD HAVE FORMATTED THIS SO POORLY. I NEARLY WENT BLIND TRYING TO READ
 * THIS UTTER SHITE.
 * 
 * @author rao
 * 
 */
public abstract class PageConfig implements Serializable {

	private static final long serialVersionUID = -4093319842848673616L;

	private static final Logger logger = Logger.getLogger(PageConfig.class);

	private static String PAGE_TAG_NAME = "page";

	private static String PAGE_NAME = "name";

	private HashMap<String, Page> pages = new HashMap<String, Page>();
	
	public Page getPage(String pageName, File configFile) throws Exception {
		getPages(configFile);
		return pages.get(pageName);
	}
	
	/**
	 * This method will parse the xml file and puts the content of the xml file
	 * in the form of objects, Page and Field.
	 * 
	 * @author rao
	 * @throws Exception 
	 */
	private void getPages(File configFile) throws Exception {
		Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(configFile);
		
		Element element = doc.getDocumentElement();
		NodeList pageNodeList = element.getElementsByTagName(PAGE_TAG_NAME);
		Page page = null;
		Collection<Field> fields = null;
		Map<String, Field> fieldsMap = null;
		for (int i = 0; i < pageNodeList.getLength(); i++) {
			page = new Page();
			page.setName(pageNodeList.item(i).getAttributes().getNamedItem(
					PAGE_NAME).getNodeValue());
			
			logger.debug("Page name " + page.getName());
			
			fields = new ArrayList<Field>();
			fieldsMap = new HashMap<String, Field>();
			// Checking for the whether Page has fields or not
			if (pageNodeList.item(i).hasChildNodes()) {
				NodeList fieldNodeList = pageNodeList.item(i).getChildNodes();
				
				if (fieldNodeList != null)
					logger.debug("Found " + fieldNodeList.getLength() + " fields");
				
				Field field = null;
				HashMap<String, String> hmFieldAttributes = new HashMap<String, String>();
				Map<String, String> hmOptions = null;

				// iterating thru Each field node
				for (int j = 0; j < fieldNodeList.getLength(); j++) {
					Node fieldNode = fieldNodeList.item(j);
					
					if (!fieldNode.getNodeName().equals("field"))
						continue;
					
					field = new Field();

					NamedNodeMap fieldAttributesMap = fieldNode == null ? null
							: fieldNode.getAttributes();
					if (field != null && fieldAttributesMap != null) {
						hmFieldAttributes = new HashMap<String, String>();
						for (int m = 0; m < fieldAttributesMap.getLength(); m++) {
							hmFieldAttributes.put(fieldAttributesMap.item(m)
									.getNodeName(), fieldAttributesMap.item(m)
									.getNodeValue());
						}

						// Setting fileds attributes map
						field.setAttributesMap(hmFieldAttributes);
						
						logger.debug("Reading field " + field.getName());

						/* Pre-defined list of values */
						if (fieldAttributesMap.getNamedItem(
								StaticCodes.FIELD_TYPE).getNodeValue().equals(
								StaticCodes.FIELD_TYPE_DROPDOWN)
								|| fieldAttributesMap.getNamedItem(
										StaticCodes.FIELD_TYPE).getNodeValue()
										.equals(StaticCodes.FIELD_TYPE_LIST)) {
							hmOptions = new LinkedHashMap<String, String>();
							
							/* Get the 'source' attribute */
							String source = fieldAttributesMap.getNamedItem(StaticCodes.FIELD_SOURCE).getNodeValue();
							
							if (source.equals(StaticCodes.FIELD_SOURCE_XML)) {
								NodeList optionsNodeList = fieldNode
										.getChildNodes();
								for (int n = 0; n < optionsNodeList.getLength(); n++) {
									if (optionsNodeList
											.item(n)
											.getNodeName()
											.equals(
													StaticCodes.FIELD_TYPE_OPTION)) {
										hmOptions
												.put(
														optionsNodeList
																.item(n)
																.getAttributes()
																.getNamedItem(
																		StaticCodes.FIELD_TYPE_OPTION_VALUE)
																.getNodeValue(),
														optionsNodeList
																.item(n)
																.getAttributes()
																.getNamedItem(
																		StaticCodes.FIELD_TYPE_OPTION_TEXT)
																.getNodeValue());
									}

								}

								// hmOptions= CommonUtils.sort(hmOptions);
								field.setValuesMap(hmOptions);

							} else if (fieldAttributesMap.getNamedItem(
									StaticCodes.FIELD_SOURCE).getNodeValue()
									.equals(StaticCodes.FIELD_SOURCE_DB)) {
								hmOptions = getOptionsMap(fieldAttributesMap
										.getNamedItem(StaticCodes.FIELD_NAME)
										.getNodeValue().trim());
								field.setValuesMap(hmOptions);
							} else if (source.equals("class")) {
								/* This fucking indentation is really pissing me off */
								String className = fieldAttributesMap.getNamedItem("provider-class").getNodeValue();
								
								hmOptions = getFieldValues(field, className);
								field.setValuesMap(hmOptions);
							}

						}

						// Add to the collection
						fields.add(field);

						// put into the Map
						fieldsMap.put(fieldAttributesMap.getNamedItem(
								StaticCodes.FIELD_NAME).getNodeValue().trim(),
								field);

					}

				}

				// Add collection of fields to the Page
				page.setFields(fields);

				// Put map of Fields to the page
				page.setFieldsMap(fieldsMap);
			}
			pages.put(page.getName(), page);
		}
		for (Entry entry : pages.entrySet()) {
			logger.debug("Page Name-->" + entry.getKey());
			for (Iterator iter = pages.get(entry.getKey()).getFields()
					.iterator(); iter.hasNext();) {
				Field element1 = (Field) iter.next();
				logger
						.debug("Attributes maps-->"
								+ element1.getAttributesMap());
				logger.debug("Option Values Map-->" + element1.getValuesMap());
			}
		}

	}

	/**
	 * Method to get an option values map for the given field name.
	 * 
	 * @param fieldName
	 *            Field Name
	 * @return Options Map
	 */
	protected abstract Map<String, String> getOptionsMap(String fieldName); 

	@SuppressWarnings("unchecked")
	private Map<String, String> getFieldValues(Field field, String className) throws Exception {
		logger.debug("Loading field provider class " + className);
		ClassLoader loader = getClass().getClassLoader();
		
		Class<ISearchFieldDataProvider> clazz =
			(Class<ISearchFieldDataProvider>)loader.loadClass(className);
		ISearchFieldDataProvider provider = clazz.newInstance();
		
		return provider.getData();
	}
}
