package j3deditor.bin.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

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;


/**
 * Tool for loading and saving XML files.
 *
 * @author Risto Seene
 */
public class XMLParser{

	/** representation of the loaded XML document tree */
	private Document d;


	/**
	 * Creates an instance of <code>XMLParser</code>.
	 */
	public XMLParser(){
	}

	/**
	 * Creates an instance of <code>XMLParser</code>
	 * and loads an XML file if possible.
	 *
	 * @param resource
	 * 	stream of the XML file that will be loaded
	 * @throws Exception
	 *	if cannot load specified file
	 */
	public XMLParser(InputStream resource) throws Exception{
		d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(resource);
	}

	/**
	 * Creates an instance of <code>XMLParser</code>
	 * and loads an XML file if possible.
	 * 
	 * @param file
	 * 	XML file that will be loaded
	 * @throws Exception
	 * 	if cannot load specified file
	 */
	public XMLParser(File file) throws Exception{
		d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);

	}


	/**
	 * Returns the root element of the document tree.
	 *
	 * @return Returns the root element of the document tree
	 */
	public Node getRoot(){
		return (d.getFirstChild());
	}

	/**
	 * Returns the requested attribute of the root node.
	 * 
	 * @param attribute
	 * 	the name of the attribute
	 * @return Returns the requested attribute of the root node
	 */
	public String getRootAttribute(String attribute){
		try{
			return getAttribute(attribute, getRoot()).getNodeValue();
		} catch(Throwable t){
		}
		return null;
	}

	/**
	 * Returns the requested attribute of the given node.
	 * 
	 * @param attribute
	 * 	the name of the attribute
	 * @param node
	 * 	node which attribute is requested
	 * @return Returns the given attribute of the given node
	 * @throws Exception
	 * 	if unable to aquire the requested attribute
	 */
	public Node getAttribute(String attribute, Node node) throws Exception{
		NamedNodeMap map = node.getAttributes();
		return map.getNamedItem(attribute);
	}


	/**
	 * Returns 2-dimensional array that contains the elements
	 * of the document tree.
	 *
	 * @return Returns 2-dimensional String array that
	 *	contains the elements of the document tree
	 * @throws Exception
	 * 	if something bad happens
	 */
	public String[][] getTable() throws Exception{
		NodeList nodes = ((Element)d.getFirstChild()).getElementsByTagName("*");
		String[][] table = new String[nodes.getLength()][2];

		for(int i = 0; i < nodes.getLength(); i++){
			Node node = nodes.item(i);
			table[i][0] = node.getNodeName();
			table[i][1] = node.getFirstChild().getNodeValue();
		}

		return table;
	}


	/**
	 * Returns 2-dimensional array that contains the specified
	 * descendants of the element named <code>node</code>.
	 *
	 * @param node
	 *	name of the node which descendants are required
	 * @param elements
	 *	array of node names which are required
	 * @return Returns 2-dimensional <code>String</code> array that
	 *	contains the elements of the document tree
	 * @throws Exception
	 * 	if something bad happens
	 */
	public String[][] getTable(String node, String[] elements) throws Exception{
		ArrayList<String[]> rows = new ArrayList<String[]>();
		NodeList nodes = ((Element)d.getFirstChild()).getElementsByTagName(node);

		for(int i = 0; i < nodes.getLength(); i++){
			String[] row = new String[elements.length];

			for(int j = 0; j < elements.length; j++){
				Node childNode = ((Element)nodes.item(i)).getElementsByTagName(elements[j]).item(0);
				try{
					row[j] = childNode.getFirstChild().getNodeValue();
				} catch(Throwable t){
					row[j] = null;
				}
			}

			rows.add(row);
		}

		String[][] table = new String[rows.size()][elements.length];

		for(int k = 0; k < rows.size(); k++){
			for(int l = 0; l < elements.length; l++){
				String[] row = rows.get(k);
				table[k][l] = row[l];

			}
		}

		return table;
	}


	/**
	 * Creates the document tree from 2-dimensional array[m][n]
	 * where n equals 2.
	 *
	 * @param data
	 *	2-dimensional String array
	 * @throws Exception
	 * 	ArrayOutOfBoundsException if <code>data</code> has less than 2 columns
	 */
	public void setData(String[][] data) throws Exception{
		d = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
		Element root = d.createElement("Java3Deditor");
		d.appendChild(root);

		for(int i = 0; i < data.length; i++){
			Element e = d.createElement(data[i][0]);
			e.appendChild(d.createTextNode(data[i][1]));
			root.appendChild(e);
		}
	}


	/**
	 * Saves the document tree to specified file.
	 *
	 * @param file
	 *	name of the file
	 */
	public void saveFile(File file){
		try{
			Deployer.createFile(file);
			OutputStream out = new FileOutputStream(file);

			Transformer t = TransformerFactory.newInstance().newTransformer();
			t.transform(new DOMSource(d), new StreamResult(out));
			out.close();
		} catch(Throwable t){
			Utilities.out.throwError(t);
		}
	}

}
