package de.lcag.common;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;

import routines.LcagFileTools;
import routines.LcagXmlTools;

public class XmlTable extends HashTable implements Storage {
	private String filePath = null;
	private String startXPath = "/*";

	public XmlTable() {
	}

	public XmlTable(String pTableName, String aFilePath) {
		super(pTableName);

		setFilePath(aFilePath);
	}

	public XmlTable(String aFilePath, AbstractTable pSrcTable) {
		super(pSrcTable);
		setFilePath(aFilePath);
	}

	public XmlTable(String aFilePath, String... pColumnNameList) {
		super(pColumnNameList);
		setFilePath(aFilePath);
	}

	private String setFilePath(String aFilePath) {
		if (!aFilePath.toLowerCase().endsWith(".xml")) {
			aFilePath += ".xml";
		}

		return this.filePath = aFilePath;
	}

	public XmlTable(String aFilePath, Object pDefaultValue, SimpleDateFormat pDefaultDateFormat) {
		super(pDefaultValue, pDefaultDateFormat);
		setFilePath(aFilePath);
	}

	@Override
	public void save() throws IOException {
		saveAs(this.filePath);
	}

	public void readColumNames() throws IOException {
		String text = LcagFileTools.readTextFile(filePath);
		String eol = this.getEol();
		String headline = text.substring(0, text.indexOf(eol));

		String[] cols = headline.split(getSeparator().trim());
		int colNo = 0;

		for (String col : cols) {
			col = col.trim();

			this.addColumn(col);
		}
	}

	/**
	 * Read the XML file leaf elements. Drops all existing table data. Note: In case
	 * of duplicate tag names only the value of the last one survives
	 */
	@Override
	public void read() throws IOException {
		init();

		try {
			Document document = LcagXmlTools.getDocument(this.filePath);

			this.read(document);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void read(Document pDocument) {
		init();
		convertToTable(pDocument);
	}

	/**
	 * * Appends the content of the given XML to the existing table using the same
	 * settings as when creating the object.
	 * 
	 * @param pXML        - XML String or file path to use
	 * @param pSourceName - adds this name into the additional "_Source" column if
	 *                    OPTION_SOURCE_COLUMN is set add
	 */
	public void append(String pXML, String pSourceName) {
		try {
			Document document = LcagXmlTools.getDocument(pXML);
			convertToTable(document, pSourceName);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * * Appends the content of the given XML to the existing table using the same
	 * settings as when creating the object.
	 * 
	 * @param pXML - XML String or file path to use
	 */
	public void append(String pXML) {
		try {
			Document document = LcagXmlTools.getDocument(pXML);
			convertToTable(document);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void readLeafElementFields(Node pStartElement, int pRowNo) {
		List<Node> nodeList = LcagXmlTools.selectNodes(pStartElement, "//*[not(*)]");

		for (Node aNode : nodeList) {
			Element aElement = (Element) aNode;
			String value = aElement.getTextTrim();
			String columnName = aElement.getName();
			List<Node> attributeList = LcagXmlTools.selectNodes(aNode, "attribute::*");

			if (this.useColumn(columnName))
				this.addCellValue(pRowNo, columnName, value);

			for (Node aAttribute : attributeList) {
				String attributeColumnName = columnName + "_" + aAttribute.getName();
				value = aAttribute.getText();

				if (this.useColumn(attributeColumnName))
					this.addCellValue(pRowNo, attributeColumnName, value);
			}
		}
	}

	private void convertToTable(Document pDocument, String pSourceName) {
		int rowNo = -1;
		List<Node> nodeList = LcagXmlTools.selectNodes(pDocument.getRootElement(), startXPath);

		for (Node aNode : nodeList) {
			rowNo = this.newRow();

			if (optionIsSet(OPTION_ADD_SOURCE_COLUMN) && pSourceName != null) {
				this.addCellValue(rowNo, "_Source_", pSourceName);
			}

			this.readLeafElementFields(aNode, rowNo);
		}
	}

	private void convertToTable(Document pDocument) {
		convertToTable(pDocument, null);
	}

	public void setStartXPath(String startXPath) {
		this.startXPath = startXPath;
	}
}
