/**
 * <p>
 * Copyright © 2009-2010, Bruce-Robert Pocock
 * </p>
 * <p>
 * Based upon public domain sample code provided by Authorize.net
 * </p>
 * <p>
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * </p>
 * <p>
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * </p>
 * <p>
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * </p>
 * 
 * @author brpocock
 */
package net.authorize.arb.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 
 * 
 * TODO: The documentation for this type (XmlTreeUtil) is incomplete.
 * (brpocock, Oct 13, 2009)
 * 
 * @author brpocock
 * 
 */
public class XmlTreeUtil {

	/**
	 * WRITEME
	 */
	private String lineSpace = "\n";
	/**
	 * WRITEME
	 */
	private boolean print_document_node = true;
	/**
	 * WRITEME
	 */
	private String tabSpace = "   ";

	/**
	 * WRITEME
	 */
	public XmlTreeUtil () {
		// No op
	}

	/**
	 * 
	 * TODO: document this method (brpocock, Oct 13, 2009)
	 * 
	 * @param node WRITEME
	 * @param os WRITEME
	 * @param nodeLevelStart WRITEME
	 * @throws IOException WRITEME
	 */
	private void _printTree (final Node node, final OutputStream os,
			final int nodeLevelStart) throws IOException {
		int nodeLevel = nodeLevelStart;
		switch (node.getNodeType ()) {
		case Node.DOCUMENT_NODE:

			if (print_document_node) {
				final String data = "<?xml version=\"1.0\"?>"
					+ lineSpace;
				os.write (data.getBytes ());
			}

			final Document doc = (Document) node;
			_printTree (doc.getDocumentElement (), os, nodeLevel);
			break;
		case Node.ELEMENT_NODE:
			String tab = "";
			for (int i = 0; i < nodeLevel; i++ ) {
				tab += tabSpace;
			}
			nodeLevel++ ;
			final String name = node.getNodeName ();
			final String elmName = tab + "<" + name;
			os.write (elmName.getBytes ());
			final NamedNodeMap attributes = node.getAttributes ();
			for (int i = 0; i < attributes.getLength (); i++ ) {
				final Node current = attributes.item (i);
				final String attrSet = " " + current.getNodeName ()
				+ "=\"" + current.getNodeValue () + "\"";
				os.write (attrSet.getBytes ());
			}

			final NodeList children = node.getChildNodes ();

			// whitespace counted as childnode

			if (children != null && children.getLength () > 0) {
				final String endElm = ">" + lineSpace;
				os.write (endElm.getBytes ());
				for (int i = 0; i < children.getLength (); i++ ) {
					_printTree (children.item (i), os, nodeLevel);
				}
				final String closeElm = tab + "</" + name + ">"
				+ lineSpace;
				os.write (closeElm.getBytes ());
			} else {
				final String closeElm = " />" + lineSpace;
				os.write (closeElm.getBytes ());
			}
			nodeLevel-- ;
			break;
		case Node.TEXT_NODE:
			String value = node.getNodeValue ();
			if (value != null) {
				value = value.trim ();
				os.write (value.getBytes ());
			} else {
				// System.out.println(node.hasChildNodes());
			}
			break;
		case Node.CDATA_SECTION_NODE:
			String dataValue = node.getNodeValue ();
			if (dataValue != null && dataValue.length () > 0) {
				dataValue = "<![CDATA[" + dataValue + "]]>";
				os.write (dataValue.getBytes ());
			}
			break;
		case Node.PROCESSING_INSTRUCTION_NODE:
		case Node.ENTITY_REFERENCE_NODE:
		case Node.DOCUMENT_TYPE_NODE:
			break;
		}
	}

	/**
	 * 
	 * TODO: document this method (brpocock, Oct 13, 2009)
	 * 
	 * @param XMLDocument WRITEME
	 * @return WRITEME
	 */
	public String printTree (final Document XMLDocument) {
		return new String (printTreeBytes (XMLDocument));
	}

	/**
	 * 
	 * TODO: document this method (brpocock, Oct 13, 2009)
	 * 
	 * @param XMLDocument WRITEME
	 * @param os WRITEME
	 */
	public void printTree (final Document XMLDocument,
			final OutputStream os) {
		final int nodeLevel = 0;
		try {
			_printTree (XMLDocument, os, nodeLevel);
		} catch (final IOException e) {
			e.printStackTrace ();
		}

	}

	/**
	 * 
	 * TODO: document this method (brpocock, Oct 13, 2009)
	 * 
	 * @param XMLDocument WRITEME
	 * @return WRITEME
	 */
	public byte [] printTreeBytes (final Document XMLDocument) {
		byte [] xmlData = new byte [0];
		try {
			final ByteArrayOutputStream baos = new ByteArrayOutputStream ();
			this.printTree (XMLDocument, baos);
			xmlData = baos.toByteArray ();
			baos.close ();
		} catch (final IOException e) {
			/* */
		}
		return xmlData;
	}

	/**
	 * 
	 * TODO: document this method (brpocock, Oct 13, 2009)
	 * 
	 */
	public void setCollapsed () {
		tabSpace = "";
		lineSpace = "";
	}

	/**
	 * 
	 * TODO: document this method (brpocock, Oct 13, 2009)
	 * 
	 * @param b WRITEME
	 */
	public void setPrintDocumentNode (final boolean b) {
		print_document_node = b;
	}
}
