package tools;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class Configuration implements Serializable {
	public static final String PATH_TAG						=	"path" ;
	public static final String THRESHOLD_TAG				=	"threshold" ;
	public static final String DELTA_TAG					=	"delta" ;
	public static final String OPTIMIZATION_THRESHOLD_TAG 	=	"optimization_threshold" ;
	public static final String URL_TAG 						=	"url" ;
	
	private String URL ;
	private HashMap<String, Field> fields;
	private HashMap<String, Field> dictionary = new HashMap<String, Field>();
	private String rootPath;
	private String elementPath, elementTag ;
	private int delta ;
	private double threshold ;
	private ConfigTree root;
	private double optimization_threshold;

	public Configuration() {
		this.fields = new HashMap<String, Field>();
		this.rootPath = "";
		this.delta = -1 ;
		this.threshold = 1.0 ;
		this.root = null;
		this.URL = "." ;
	}

	public void putField(String fieldName, Field field) {
		dictionary.put(fieldName, field) ;
	}
	public Field getField(String fieldName) {
		return dictionary.get(fieldName) ;
		/*
		if (fields.containsKey(fieldName)) {
			return fields.get(fieldName) ;
		}
		else {
			Collection<Field> colFields = fields.values() ;
			for (Iterator<Field> itF = colFields.iterator() ; itF.hasNext() ; ) {
				Field field = itF.next().get(fieldName) ;
				if (field != null) {
					return field ;
				}
			}
		}
		return null ;
		*/
	}
	public void addField(String name) {
		Field field = new Field(this, name);
		fields.put(name, field);
	}

	public void addField(String name, String path) {
		Field field = new Field(this, name, path);
		fields.put(name, field);
	}

	public void addField(String name, String path, String type) {
		Field field = new Field(this, name, path, type);
		fields.put(name, field);
	}

	public void addField(String name, String path, String type,
			double importanceLevel) {
		Field field = new Field(this, name, path, type, importanceLevel);
		fields.put(name, field);
	}

	public void addField(String name, String path, String type,
			double importanceLevel, int compareFunction) {
		Field field = new Field(this, name, path, type, importanceLevel,
				compareFunction);
		fields.put(name, field);
	}

	public Field getFieldByName(String name) {
		if (fields.containsKey(name)) {
			return fields.get(name);
		} else {
			return null;
		}
	}

	public void setURL(String URL) {
		this.URL = URL ;
	}
	public void compile(ConfigTree root, Collection<Field> fields) {
		for (Iterator<Field> itField = fields.iterator(); itField.hasNext();) {			
			Field field = itField.next() ;
			
			String path = field.getPath() ;
			String name = field.getName() ;

			StringTokenizer st = new StringTokenizer(path, "/");
			ConfigTree curTree = root, nextTree;
			if (!st.hasMoreTokens()) {
				nextTree = new ConfigTree("", null, curTree);
				curTree.addChild(nextTree) ;
				curTree = nextTree ;
			}
			while (st.hasMoreTokens()) {
				String segment = st.nextToken();
				String conditions = "";
				if (segment.contains("[")) {
					conditions = segment.substring(segment.indexOf("[") + 1,
							segment.indexOf("]"));					
				}				
				nextTree = curTree.getChild(segment);
				if (nextTree == null || conditions.length() > 0) {
					nextTree = new ConfigTree(segment, null, curTree);
					if (conditions.length() > 0) {
						StringTokenizer conTok = new StringTokenizer(
								conditions, ", ='");
						while (conTok.hasMoreTokens()) {
							nextTree.addCondition(conTok.nextToken(), conTok
									.nextToken());
						}
					}
					curTree.addChild(nextTree);
				}
				curTree = nextTree;
			}
			curTree.name = name;
			if (field.isComplex()) {
				curTree.setComplex() ;
				compile(curTree, field.content) ;
			}
		}

	}
	
	public String getElementTag() {
		return this.elementTag ;
	}
	public void compile() {		
		root = new ConfigTree(rootPath);		
		compile(root, fields.values());
		/*
		for (Iterator<String> itName = nameCol.iterator(); itName.hasNext();) {
			String name = itName.next();
			Field field = fields.get(name);
			
			String path = field.path;

			StringTokenizer st = new StringTokenizer(path, "/");
			ConfigTree curTree = root, nextTree;
			while (st.hasMoreTokens()) {
				String segment = st.nextToken();
				String conditions = "";
				if (segment.contains("[")) {
					conditions = segment.substring(segment.indexOf("[") + 1,
							segment.indexOf("]"));					
				}				
				nextTree = curTree.getChild(segment);
				if (nextTree == null || conditions.length() > 0) {
					nextTree = new ConfigTree(segment, null, curTree);
					if (conditions.length() > 0) {
						StringTokenizer conTok = new StringTokenizer(
								conditions, ", ='");
						while (conTok.hasMoreTokens()) {
							nextTree.addCondition(conTok.nextToken(), conTok
									.nextToken());
						}
					}
					curTree.addChild(nextTree);
				}
				curTree = nextTree;
			}
			curTree.name = name;
		}
		*/

	}

	private void makeDictionary(Field field) {
		putField(field.getName(), field) ;
		Collection<Field> fields = field.content ;
		if (fields != null) {
			for (Iterator<Field> itField = fields.iterator() ; itField.hasNext() ; ) {
				makeDictionary(itField.next()) ;
			}	
		}
	}
	private void makeDictionary() {
		Collection<String> keys = fields.keySet() ;
		for (Iterator<String> itKey = keys.iterator() ; itKey.hasNext() ; ) {
			String key = itKey.next() ;
			Field field = fields.get(key) ;
			makeDictionary(field) ;
		}
	}
	public Configuration(String fileName) {
		this() ;
		
		Configuration configuration = null ;
		System.out.println("Test fromFile configuration") ;
		try {
			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
			
			Document doc = docBuilder.parse(fileName);
			doc.getDocumentElement().normalize();
			Node root = doc.getDocumentElement();
			
			NodeList children = root.getChildNodes();
			
			NodeList fields = ((Element) root).getElementsByTagName("fields") ;			
			for (int iChild = 0 ; iChild < children.getLength() ; iChild ++) {

				Node curNode = children.item(iChild) ;
				if (curNode.getNodeType() == Node.ELEMENT_NODE) {
					Element curEl = (Element) curNode ;
					String type = curEl.getAttribute("type") ;
					if (type.equals("root")) {
						rootPath = getValue((Element) curEl.getElementsByTagName(PATH_TAG).item(0)) ;
						if (rootPath.contains("/")) {
							elementPath = rootPath.substring(0, rootPath.lastIndexOf("/")) ;
							elementTag = rootPath.substring(rootPath.lastIndexOf("/") + 1) ;							
						}
						else {
							elementPath = "" ;
							elementTag = rootPath ;							
						}
						threshold = Double.parseDouble(getValue((Element) curEl.getElementsByTagName(THRESHOLD_TAG).item(0))) ;
						delta = Integer.parseInt(getValue((Element) curEl.getElementsByTagName(DELTA_TAG).item(0))) ;
						optimization_threshold = Double.parseDouble(getValue((Element) curEl.getElementsByTagName(OPTIMIZATION_THRESHOLD_TAG).item(0))) ;
						URL = Authority.PATH + getValue((Element) curEl.getElementsByTagName(URL_TAG).item(0)) ;
						System.out.println("PARSED url: " + URL);
					}
					if (type.equals("node")) {						
						addField(processNodes(curEl.getChildNodes())) ;
					}
				}
			}
			makeDictionary() ;
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private Field processNodes(NodeList childNodes) {
		try {							
			Field newField = new Field(this);
			for (int iNode = 0 ; iNode < childNodes.getLength() ; iNode ++) {
				Node node = childNodes.item(iNode) ;
				if (node.getNodeType() == Node.ELEMENT_NODE) {
					if (node.getNodeName().equals("content")) {
						NodeList contentNodes = node.getChildNodes() ;
						for (int iContentNode = 0 ; iContentNode < contentNodes.getLength() ; iContentNode ++) {
							Node nodeContent = contentNodes.item(iContentNode) ;
							if (nodeContent.getNodeType() == Node.ELEMENT_NODE) {
								newField.setValue(node.getNodeName(), processNodes(nodeContent.getChildNodes()))	;
							}
						}
					}
					else {
						newField.setValue(node.getNodeName(), getValue((Element) node)) ;
					}
				}
			}
			/*
			String name = getField(curEl, "name") ;
			String path = getField(curEl, "path") ;
			String typee = getField(curEl, "type") ;
			String importance = getField(curEl, "importance") ;
			String algorithm = getField(curEl, "algorithm") ;
			Field newField = new Field(getField(curEl, "name"),
					getField(curEl, "path"),
					getField(curEl, "type"), 
					Double.parseDouble(getField(curEl, "importance")),
					CompareUtilities.class.getDeclaredField(
							getField(curEl, "algorithm")).getInt(null)) ;
			if (getField(curEl, "sort").equals("true")) {
				newField.setSortable() ;
			}
			*/							
			return newField ;
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		return null ;
	}

	public void addField(Field field) {
		fields.put(field.getName(), field) ;
	}

	private String getValue(Element curEl) {
		return curEl.getChildNodes().item(0).getNodeValue().trim();
	}
	
	public String getURL() {
		return this.URL ;
	}
	
	public HashMap<String, Field> getField() {
		return this.fields ;
	}
	public String getRootPath() {
		return this.rootPath;
	}
	public int getDelta() {
		return this.delta ;
	}
	
	public double getThreshold() {
		return this.threshold ;
	}
	public ConfigTree getRoot() {
		return this.root ;
	}
	public double getOptimizationThreshold() {
		return this.optimization_threshold; 
	}
}
