/*******************************************************************************
 * Copyright (C) 2009-2011 FuseSource Corp.
 * Copyright (c) 2008 IBM Corporation and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 *******************************************************************************/
package org.fusesource.hawtjni.generator;

import java.io.PrintStream;
import java.util.Arrays;
import java.util.Comparator;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 
 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
 */
public class DOMWriter {

	static String ENCONDING = "UTF8";
	PrintStream out;
	String[] attributeFilter;
	String nodeFilter;

	public DOMWriter(PrintStream out) {
		this.out = new PrintStream(out);
	}

	String nodeName(Node node) {
		// TODO use getLocalName()?
		return node.getNodeName();
	}
	
	boolean filter(Attr attr) {
		if (attributeFilter == null) return false;
		String name = attr.getNodeName();
		for (int i = 0; i < attributeFilter.length; i++) {
			if (name.matches(attributeFilter[i])) return false;
		}
		return true;
	}
	
	void print(String str) {
		out.print(str);
	}
	void println() {
		out.println();
	}

	public void print(Node node) {
		print(node, 0);
	}
	
	public void print(Node node, int level) {
		if (node == null)
			return;
		int type = node.getNodeType();
		switch (type) {
			case Node.DOCUMENT_NODE: {
				print("<?xml version=\"1.0\" encoding=\"");
				print(ENCONDING);
				print("\"?>");
				println();
				print(((Document) node).getDocumentElement());
				break;
			}
			case Node.ELEMENT_NODE: {
				Attr attrs[] = sort(node.getAttributes());
				String name = nodeName(node);
				boolean gen = name.equals("arg") || name.equals("retval");
				for (int i = 0; i < attrs.length && !gen; i++) {
					Attr attr = attrs[i];
					if (nodeName(attr).startsWith(nodeFilter)) gen = true;
				}
				if (!gen) break;
				for (int i = 0; i < level; i++) print("\t");
				print("<");
				print(name);
				for (int i = 0; i < attrs.length; i++) {
					Attr attr = attrs[i];
					if (filter(attr)) continue;
					print(" ");
					print(nodeName(attr));
					print("=\"");
					print(normalize(attr.getNodeValue()));
					print("\"");
				}
				print(">");
				NodeList children = node.getChildNodes();
				int count = 0;
				if (children != null) {
					int len = children.getLength();
					for (int i = 0; i < len; i++) {
						if (children.item(i).getNodeType() == Node.ELEMENT_NODE) count++;
					}
					if (count > 0) println();
					for (int i = 0; i < len; i++) {
						print(children.item(i), level + 1);
					}
					if (count > 0) {
						for (int i = 0; i < level; i++) print("\t");
					}
				}
				print("</");
				print(nodeName(node));
				print(">");
				println();
				break;
			}
		}
		out.flush();
	}

	Attr[] sort(NamedNodeMap attrs) {
		if (attrs == null)
			return new Attr[0];
		Attr result[] = new Attr[attrs.getLength()];
		for (int i = 0; i < result.length; i++) {
			result[i] = (Attr) attrs.item(i);
		}
		Arrays.sort(result, new Comparator<Node>() {
			public int compare(Node arg0, Node arg1) {
				return nodeName(arg0).compareTo(nodeName(arg1));
			}
		});
		return result;
	}

	String normalize(String s) {
		if (s == null) return "";
		StringBuffer str = new StringBuffer();
		for (int i = 0, length = s.length(); i < length; i++) {
			char ch = s.charAt(i);
			switch (ch) {
				case '"': str.append("\""); break;
				case '\r':
				case '\n':
					// FALL THROUGH
				default: str.append(ch);
			}
		}
		return str.toString();
	}
	
	public void setNodeFilter(String filter) {
		
		nodeFilter = filter;
	}
	
	public void setAttributeFilter(String[] filter) {
		attributeFilter = filter;
	}
}