/**
 * TableConfig.java
 * Created June 26, 2006
 * Copyright (C) Tandberg Television 2006
 */
package com.tandbergtv.workflow.web.table;

import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * This class is a Singleton. It parses the table-config.xml file and
 * creates Table objects from it. It maintains a cache of Table
 * objects.
 * 
 * @author Sathesh
 * @version 1.0
 */
public class TableConfig implements Serializable {

	private static TableConfig tableCofig = null;

	private static final long serialVersionUID = 1L;

	private static final Logger logger = Logger.getLogger(TableConfig.class);

	private static String TABLE_TAG_NAME = "table";

	private static String TABLE_ID = "id";

	private static final String DEFAULT_SORTING_COLUMN_NAME = "defaultSortingColumn";

	private static final String DEFAULT_SORTING_ORDER = "defaultSortingOrder";

	private static final String ACTION_PATH = "actionPath";
	
	private static final String DEFAULT_COLUMN_WIDTH = "defaultColumnWidth";

	/**
	 * Private constructor to implement Singleton
	 * @throws Exception 
	 * 
	 */
	private TableConfig() {
		
	}

	/**
	 * Static method to get an instance of the TableConfig object
	 * 
	 * @return TableConfig instance.
	 * @throws Exception 
	 */
	public static synchronized TableConfig getInstance() {
		if (tableCofig == null) {
			tableCofig = new TableConfig();
		}
		return tableCofig;
	}

	/**
	 * This method returns a table object based on the passed ID.
	 * 
	 * @param tableId
	 *            table ID
	 * @return table instance
	 * @author rao
	 */
	public Table getTable(String tableId, File tableConfigFile) {
		return this.getTable(tableId, tableConfigFile, null);
	}

	/**
	 * This method returns a table object based on the passed ID.
	 * 
	 * @param tableId table ID
	 * @param runtimeProperties The runtime properties used to determine table extension columns
	 * @return table instance
	 */
	public Table getTable(String tableId, File tableConfigFile, Map<String, String> runtimeProperties) {		
		Map<String, Table> tables = getTables(tableConfigFile);
		Table table = tables.get(tableId);
		TableExtensionBuilder extensionBuilder = new TableExtensionBuilder();
		extensionBuilder.buildTable(table, runtimeProperties);
		return table;
	}
	
	/**
	 * This method will parse the xml file and puts the content xml file in the
	 * form of objects, Tables and Columns.
	 * 
	 * @author rao
	 * @throws Exception 
	 */
	private Map<String, Table> getTables(File tableConfigFile) {
		Map<String, Table> tables = new HashMap<String, Table>();

		NodeList nodes = getNodes(tableConfigFile);
		
		if (nodes == null)
			return tables;

		for (int i = 0; i < nodes.getLength(); i++) {
			Table table = getTable(nodes.item(i));

			tables.put(table.getId(), table);
		}

		for (String tableId : tables.keySet()) {
			logger.debug("Table Name-->" + tableId);
			
			for (Column column : tables.get(tableId).getColumns())
				logger.debug("maps-->" + column.getColumnAttributesMap());
		}
		
		return tables;
	}
	
	/**
	 * Gets the list of DOM nodes corresponding to a table's configuration
	 * 
	 * @return
	 */
	private NodeList getNodes(File tableConfigFile) {
		NodeList nodes = null;
		
		try {
//			String dir = System.getProperty(PRODUCT_DIR) + separator + "conf";
//			String filename = dir + separator + TABLE_CONFIG_FILE_NAME;
			Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(tableConfigFile);
			
			nodes = document.getDocumentElement().getElementsByTagName(TABLE_TAG_NAME);
		} catch (Exception e) {
			logger.warn("Failed to read the table configuration file", e);
		}

		return nodes;
	}
	
	/**
	 * Creates a table from the specified XML configuration
	 * 
	 * @param node
	 * @return
	 */
	private Table getTable(Node node) {
		Table table = new Table();
		NamedNodeMap attributes = node.getAttributes();
		
		table.setId(attributes.getNamedItem(TABLE_ID).getNodeValue());
		table.setDefaultSortingColumn(attributes.getNamedItem(DEFAULT_SORTING_COLUMN_NAME).getNodeValue());
		table.setDefaultSortingOrder(attributes.getNamedItem(DEFAULT_SORTING_ORDER).getNodeValue());
		table.setActionPath(attributes.getNamedItem(ACTION_PATH).getNodeValue());
		table.setDefaultColumnWidth(attributes.getNamedItem(DEFAULT_COLUMN_WIDTH).getNodeValue());
		
		List<Column> columns = new ArrayList<Column>();
		// Checking for the whether table has columns or not
		if (node.hasChildNodes()) {
			NodeList columnNodeList = node.getChildNodes();
			
			for (int j = 0; j < columnNodeList.getLength(); j++) {
				NodeList columnParams = columnNodeList.item(j).getChildNodes();
				// Eliminating the direct value node of Column Node by checking for child nodes
				if (columnParams.getLength() > 0) {
					Column column = new Column();
					HashMap<String, String> map = new HashMap<String, String>();
					
					for (int k = 0; k < columnParams.getLength(); k++) {
						Node n = columnParams.item(k);
						NodeList children = n.getChildNodes();
						
						for (int l = 0; l < children.getLength(); l++)
							map.put(n.getNodeName(), children.item(l).getNodeValue());
					}

					column.setColumnAttributesMap(map);
					columns.add(column);
				}
			}
			
			table.setColumns(columns);
		}
		
		return table;
	}
}
