/*
    PCS - A Framework For Java Web Applications
    Copyright (C) 2002 Patrick Carl, patrick.carl@web.de
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
 
    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
    $Id: View.java,v 1.2 2003/01/08 02:33:43 pcs_org Exp $
 
 */

package de.spieleck.pcs.view;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import de.spieleck.pcs.conf.ConfigUser;
import de.spieleck.pcs.conf.ConfigAdmin;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.Templates;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerFactory;
import de.spieleck.pcs.action.ActionResult;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;
import java.util.Enumeration;
import java.util.Hashtable;

import org.w3c.tidy.Tidy;

/**
 * This class is reponsible for displaying an ActionResult. It transform
 * the xml data of the ActionResult via XSL to HTML (or whatever) and writes
 * it to an OutputStream.
 * @author Patrick Carl
 */
public class View implements ConfigUser{
    
    //private static String file = "/conf/view.properties";
    private static String file;
    private static String rootXslPath = null;
    private OutputStream os;
    
    /** Holds value of property useJTidy. */
    private static boolean useJTidy;
    
    /**
     * Creates a new instance of view with the given settings
     * @param os the OutputStream to write on
     * @param xml the XML that should be used as base for the output
     * @param key the key to lookup in the view.properties for getting the
     * name of the stylesheet to use
     * @param style the style of the output
     */
    public View(OutputStream os) throws IOException{
        this();
        setOutputStream(os);
    }
    
    /** Creates an instance of View **/
    
    public View() throws IOException{
        ConfigAdmin.getInstance().addConfigUser(this);
    }
    
    /**
     * used to remove this object from the ConfigAdmins list of ConfigUsers
     * This must be done, since otherwise no old Views would be get by GC since
     * ConfigAdmin helds forever an reference to each View object
     */
    public void cleanUp(){
        ConfigAdmin.getInstance().removeConfigUser(this);
    }
    
    /**
     * sets the stream to write on
     */
    public void setOutputStream(OutputStream os){
        this.os = os;
    }
    /**
     * returns the OutputStream of this View
     */
    public OutputStream getOutputStream(){
        return os;
    }
    
    /**
     * displays the given ActionResult on this View
     * @param ar the ActionResult
     * @param styleSheetFile absolute path of the stylesheet to use
     * @throws ViewException if an exception occurs during transformation
     */
    public synchronized void output(ActionResult ar, String styleSheetFile)
    throws ViewException{
        try{
            // create the source of XML
            Source source = ar.getSource();
            boolean useTidy = isUseJTidy();
            
            // create the used stylesheet
            StreamSource stylesheet = new StreamSource(new File(styleSheetFile));
            
            // create Streams depending of the use of JTidy
            OutputStream xslOut = null;
            InputStream tidyIn = null;
            // if we use JTidy we write the XSL output into a ByteArrayOutputStream
            // and read from this ByteArray as input for JTidy. JTidy writes than to
            // the OutputStream of the View, otherwise we write directly to the
            // View's OutputStream
            if(useTidy)
                xslOut = new ByteArrayOutputStream();
            else
                xslOut = getOutputStream();
            
            // then we transform some xml
            StreamResult result = new StreamResult(xslOut);
            TransformerFactory transFact = TransformerFactory.newInstance();
            Templates cachedXSLT = transFact.newTemplates(stylesheet);
            Transformer trans = cachedXSLT.newTransformer();
            setProperties(trans, ar.getParameters());
            trans.transform(source, result);
            
            // after XSL transformation is finished tidy up
            if(useTidy){
                tidyIn = new ByteArrayInputStream(xslOut.toString().getBytes());
                Tidy tidy = new Tidy();
                tidy.setQuiet(true);
                tidy.setXHTML(true);
                tidy.setErrfile(null);
                tidy.parse(tidyIn, getOutputStream());
            }
        } catch(Exception e){
            e.printStackTrace();
            throw new ViewException(e.getMessage());
        }
    }
    
    /**
     * sets the Parameters of the given Transformer using the values stored in
     * the given Hashtable
     */
    protected void setProperties(Transformer trans, Hashtable h){
        if(h == null)
            return;
        Enumeration e = h.keys();
        String key = null;
        while(e.hasMoreElements()){
            key = (String) e.nextElement();
            trans.setParameter(key, h.get(key));
        }
    }
    
    /**
     * resets the configuration
     */
    public void resetConfig() {
    }
    
    /** Getter for property useJTidy.
     * @return Value of property useJTidy.
     *
     */
    public static boolean isUseJTidy() {
        return useJTidy;
    }
    
    /** Setter for property useJTidy.
     * @param useJTidy New value of property useJTidy.
     *
     */
    public static void setUseJTidy(boolean useJTidy) {
        View.useJTidy = useJTidy;
    }
}