/*****************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
 * ------------------------------------------------------------------------- *
 * This software is published under the terms of the Apache Software License *
 * version 1.1, a copy of which has been included  with this distribution in *
 * the LICENSE file.                                                         *
 *****************************************************************************/
package org.apache.cocoon.generation;

import org.apache.avalon.excalibur.pool.Recyclable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.Constants;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.ResourceNotFoundException;
import org.apache.cocoon.caching.CacheValidity;
import org.apache.cocoon.caching.Cacheable;
import org.apache.cocoon.caching.TimeStampCacheValidity;
import org.apache.cocoon.components.xpath.XPathProcessor;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.Source;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.util.HashUtil;
import org.apache.cocoon.xml.dom.DOMStreamer;
import org.apache.cocoon.xml.XMLUtils;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;
import org.xml.sax.SAXException;

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import java.io.BufferedInputStream;
import java.io.StringWriter;
import java.io.PrintWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
import java.net.URL;
import java.net.URLEncoder;


/**
 * @author <a href="mailto:dims@yahoo.com">Davanum Srinivas</a>
 * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
 * @author <a href="mailto:barozzi@nicolaken.com">Nicola Ken Barozzi</a> 
 * @author <a href="mailto:reinhard.poetz@at.efp.cc">Reinhard Poetz</a>
 * @version CVS $Revision$ $Date$
 */
public class HTTPHTMLGenerator extends ComposerGenerator implements Recyclable, Composable, Disposable {

    /** The  source */
    private Source inputSource;
    
    /** The source for the HTTP-Client */
    private String s;
    
    /** The HTTP-Method */
    private String m;
    
    /** The Request-Paramters for the HTTP-Client */
    private NameValuePair nvp[];   

    /** The Request-Paramters for the HTTP-Client */
    private Vector header;         

    /** XPATH expression */
    private String xpath = null;

    /** XPath Processor */
    private XPathProcessor processor = null;    

    /**
     * Recycle this component.
     * All instance variables are set to <code>null</code>.
     */
    public void recycle() {
        super.recycle();
        if (this.inputSource != null) {
            this.inputSource.recycle();
            this.inputSource = null;
        }
        this.xpath = null;
        this.s = null;
        this.nvp = null;
        this.header = null;
        this.m = null;
    }

    /**
     * Setup the html generator.
     * Try to get the last modification date of the source for caching.
     */
    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par)
    throws ProcessingException, SAXException, IOException {
        super.setup(resolver, objectModel, src, par);

        Vector v = new Vector();

        Request request = (Request) objectModel.get(Constants.REQUEST_OBJECT);
        xpath = request.getParameter("xpath");
        m = request.getMethod();
        
        if(xpath == null)
            xpath = par.getParameter("xpath",null);

        // create NameValuePair[] containing the Request-Paramters for the HTTP-Request
        if (par.getParameterAsBoolean("copy-parameters", true )) {

            Enumeration params = request.getParameterNames();
            while (params.hasMoreElements()) {
                String name = (String)params.nextElement();
                String[] values = request.getParameterValues( name );
                for (int i = 0; i < values.length; i++) {
                    v.add(new NameValuePair(name, values[i]));
                }
            }
 			
            this.nvp = new NameValuePair[v.size()];
            for(int i = 0; i < v.size(); i++) {
            	this.nvp[i] = (NameValuePair) v.elementAt(i);	
            }

    	}

        // create NameValuePair[] containing the Header-Paramters for the HTTP-Request
        header = new Vector();                
        if (par.getParameterAsBoolean("use-header-parameters", true )) {

            Enumeration h_params = request.getHeaderNames();
            while (h_params.hasMoreElements()) {
                String h_name = (String) h_params.nextElement();
                Enumeration h_values = request.getHeaders( h_name );
                while(h_values.hasMoreElements()) {
                	String h_value = (String) h_values.nextElement();
                    this.header.add(new Header(h_name, h_value));
                }
            }
    	}    

        this.s = src;
    }

    /**
     * Generate XML data.
     */
    public void generate()
    throws IOException, SAXException, ProcessingException {
        try
        {
            // Setup an instance of Tidy.
            Tidy tidy = new Tidy();
            tidy.setXmlOut(true);
            tidy.setXHTML(true);
            //Set Jtidy warnings on-off
            tidy.setShowWarnings(getLogger().isWarnEnabled());
            //Set Jtidy final result summary on-off
            tidy.setQuiet(!getLogger().isInfoEnabled());
            //Set Jtidy infos to a String (will be logged) instead of System.out
            StringWriter stringWriter = new StringWriter();
            PrintWriter errorWriter = new PrintWriter(stringWriter);
            tidy.setErrout(errorWriter);          

			// Setup HTTP-Connection
            URL u = new URL(this.s);
            
            HttpClient _httpClient = new HttpClient();
            HttpMethod _httpMethod;
            
            // HTTP-Method
            if(this.m.equals("POST")) {
            	_httpMethod = new PostMethod(u.getPath());			
            	((PostMethod) _httpMethod).setUseDisk(false);            
            }
            else {
            	_httpMethod = new GetMethod(u.getPath());			
            	((GetMethod) _httpMethod).setUseDisk(false);                  	
            }
            
            // HTTPSession
            _httpClient.startSession(u);
            // Request-Parameters
			((HttpMethod) _httpMethod).setQueryString(this.nvp);   
			// in einer Schleife der Vector mit den Header-Objekten dem HTTP-Request übergeben
            for(int i = 0; i < this.header.size(); i++) {
				((HttpMethod) _httpMethod).setRequestHeader( (Header) this.header.elementAt(i));               	
            }			
            // value for "accept-encoding" must be empty
			((HttpMethod) _httpMethod).setRequestHeader("accept-encoding", "");

			_httpClient.executeMethod(_httpMethod);			         

            // Extract the document using JTidy and stream it.
            org.w3c.dom.Document doc = tidy.parseDOM(new BufferedInputStream(_httpMethod.getResponseBodyAsStream()), null);
            _httpClient.endSession();
                        
             // FIXME: Jtidy doesn't warn or strip duplicate attributes in same
            // tag; stripping.
            XMLUtils.stripDuplicateAttributes(doc, null);

            errorWriter.flush();
            errorWriter.close();
            if(getLogger().isWarnEnabled()){
               getLogger().warn(stringWriter.toString());
            }
            
            if(xpath != null)
            {
                Transformer serializer = TransformerFactory.newInstance().newTransformer();
                serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");

                NodeList nl = processor.selectNodeList(doc, xpath);
                int length = nl.getLength();
                for(int i=0;i<length;i++)
                {
                    SAXResult result = new SAXResult(this.contentHandler);
                    result.setLexicalHandler(this.lexicalHandler);
                    serializer.transform(new DOMSource(nl.item(i)), result);
                }
            } else {
                DOMStreamer streamer = new DOMStreamer(this.contentHandler,this.lexicalHandler);
                streamer.stream(doc);
            }
        } catch (IOException e){
            getLogger().warn("HTMLGenerator.generate()", e);
            throw new ResourceNotFoundException("Could not get resource "
                + this.inputSource.getSystemId(), e);
        } catch (SAXException e){
            getLogger().error("HTMLGenerator.generate()", e);
            throw e;
        // } catch (ProcessingException e){
        //    throw e;
        } catch (Exception e){
            getLogger().error("Could not setup jtidy", e);
            throw new ProcessingException("Exception in HTMLGenerator.generate()",e);
        }
    }
    
    public void dispose()
    {
        this.manager.release((Component)this.processor);
    }    
}

