/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */
package org.richfaces.renderkit;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Properties;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;

import org.ajax4jsf.javascript.JSReference;
import org.ajax4jsf.javascript.ScriptUtils;
import org.ajax4jsf.renderkit.HeaderResourcesRendererBase;
import org.ajax4jsf.resource.util.URLToStreamHelper;
import org.ajax4jsf.webapp.tidy.TidyParser;
import org.ajax4jsf.webapp.tidy.TidyXMLFilter;
import org.richfaces.component.TemplateComponent;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ContentHandler;

/**
 * @author Nick Belaevski - mailto:nbelaevski@exadel.com
 * created 22.06.2007
 *
 */
public abstract class TemplateEncoderRendererBase extends HeaderResourcesRendererBase {
	private static TransformerFactory transformerFactory;

	private static TransformerFactory getTransformerFactory() {
		synchronized (TemplateEncoderRendererBase.class) {
			if (transformerFactory == null) {
				transformerFactory = TransformerFactory.newInstance();
			}
		}

		return transformerFactory;
	}
	
	public final boolean getRendersChildren() {
		return true;
	}

	public void writeScriptBody(Writer writer, String string) throws IOException {
		if (string != null) {
			Properties tidyProperties = new Properties();
			InputStream propertiesStream = null;
			try {
				propertiesStream = URLToStreamHelper.urlToStreamSafe(
						TidyXMLFilter.class.getResource("tidy.properties"));
				tidyProperties.load(propertiesStream);
			} finally {
				if (propertiesStream != null) {
					propertiesStream.close();
				}
			}
	
			TidyParser tidyParser = new TidyParser(tidyProperties);
			Document parsedHtml = tidyParser.parseHtmlByTidy(new StringReader(string), null);
	
			Element documentElement = null;
			
			if (parsedHtml != null) {
				documentElement = parsedHtml.getDocumentElement();
			}
			
			if (documentElement != null) {
				writer.write("[");
				
				NodeList nodeList = documentElement.getChildNodes();
				Node bodyNode = nodeList.item(nodeList.getLength() - 1);
				NodeList bodyChildren = bodyNode.getChildNodes();
				int bodyChildrenLength = bodyChildren.getLength();
	
				try {
					Transformer transformer;
	
					TransformerFactory factory = getTransformerFactory();
					
					synchronized (factory) {
						transformer = factory.newTransformer();
					}
	
					transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
					transformer.setOutputProperty(OutputKeys.METHOD, "xml");
	
					ContentHandler contentHandler = createContentHandler(writer);
					Result result = new SAXResult(contentHandler);
	
					for (int i = 0; i < bodyChildrenLength; i++) {
						if (i != 0) {
							writer.write(", ");
						}
						transformer.transform(new DOMSource(bodyChildren.item(i)), result);
					}
				} catch (TransformerException e) {
					throw new IOException(e.getMessage());
				}
				
				writer.write("]");
			
			} else {
				writer.write(ScriptUtils.toScript(JSReference.NULL));
			}
		} else {
			writer.write(ScriptUtils.toScript(JSReference.NULL));
		}
	}
	
	protected void writeScriptBody(FacesContext context, UIComponent component, boolean children)
	throws IOException {
		ResponseWriter writer = context.getResponseWriter();
		StringWriter dumpingWriter = new StringWriter();
		ResponseWriter clonedWriter = writer.cloneWithWriter(dumpingWriter);
		context.setResponseWriter(clonedWriter);
		
		TemplateComponent templateComponent = null;
		if (component instanceof TemplateComponent) {
			templateComponent = (TemplateComponent) component;
		}
		
		try {
			if (templateComponent != null) {
				templateComponent.startTemplateEncode();
			}
			
			if (children) {
				this.renderChildren(context, component);
			} else {
				this.renderChild(context, component);
			}
		} finally {
			if (templateComponent != null) {
				templateComponent.endTemplateEncode();
			}

			clonedWriter.flush();
			context.setResponseWriter(writer);
		}

		writeScriptBody(writer, dumpingWriter.toString());
	}
	
	public void encodeChildren(FacesContext context, UIComponent component)
	throws IOException {
		ResponseWriter writer = context.getResponseWriter();
		writer.startElement("script", component);
		writer.write("var evaluator = ");
		writeScriptBody(context, component, true);
		writer.write(";\n new Insertion.Top($('" + component.getClientId(context) + "'), evaluator.invoke('getContent', window).join(''));");
		writer.endElement("script");
	}
	
	
	protected ContentHandler createContentHandler(Writer writer) {
		return new MacroDefinitionJSContentHandler(writer, "Richfaces.evalMacro(\"", "\", context)");
	}

	public static void main(String[] args) throws Exception {
		StringBuffer buffer = new StringBuffer();
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
		String s;
		while ((s = reader.readLine()) != null) {
			buffer.append(s);
		}
		
		new TemplateEncoderRendererBase(){

			protected Class<? extends UIComponent> getComponentClass() {
				return null;
			}
			
		}.writeScriptBody(new PrintWriter(System.out), buffer.toString());
	}
}
