/*
* File DocMaker.java
*
* Copyright (C) 2010 Remco Bouckaert remco@cs.auckland.ac.nz
*
* This file is part of BEAST2.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST 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
* of the License, or (at your option) any later version.
*
*  BEAST 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 BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA  02110-1301  USA
*/
package beastfx.app.tools;




import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

import javax.imageio.ImageIO;

import beast.base.core.BEASTObject;
import beast.base.core.BEASTVersion2;
import beast.base.core.Citation;
import beast.base.core.Description;
import beast.base.core.Input;
import beast.base.core.Log;
import beast.base.core.Loggable;
import beast.pkgmgmt.BEASTClassLoader;
import beast.pkgmgmt.PackageManager;
import beastfx.app.util.FXUtils;
import javafx.embed.swing.JFXPanel;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.ImageView;



/**
 * Plug in documentation generator.
 * Goes through all plug-ins and generate one page per plug-in.
 * <p/>
 * Usage: DocMaker <target directory>
 * where <target directory> is the place where the HTML files
 * should go. Default directory is /tmp.
 * *
 */
public class DocMaker {

    /**
     * output directory *
     */
    String m_sDir = "/tmp";
    /**
     * names of the plug-ins to document *
     */
    Set<String> m_beastObjectNames;
    /**
     * map of plug-in name to its derived plug-ins *
     */
    HashMap<String, String[]> m_isa;
    /**
     * map of plug-in name to its ancestor plug-ins *
     */
    HashMap<String, List<String>> m_ancestors;
    /**
     * map of plug-in name to its description (from @Description annotations) *
     */
    HashMap<String, String> m_descriptions;

    Set<String> m_sLoggables;
    
    BEASTVersion2 version = new BEASTVersion2();
    
    public DocMaker(String[] args) {
        this();
        if (args.length > 0) {
            if (args[0].equals("-javadoc")) {
                makeJavaDoc();
                System.exit(0);
            }
            m_sDir = args[0];
        }
    } // c'tor

    public DocMaker() {
        // find plug ins to document
        m_beastObjectNames = PackageManager.listServices("beast.base.core.BEASTInterface");
        /** determine hierarchical relation between plug-ins **/
        m_isa = new HashMap<>();
        m_ancestors = new HashMap<>();
        m_descriptions = new HashMap<>();
        m_sLoggables = new HashSet<>();
        for (String beastObjectName : m_beastObjectNames) {
            m_ancestors.put(beastObjectName, new ArrayList<>());
        }
//        for (String beastObjectName : m_beastObjectNames) {
        Iterator<String> iter = m_beastObjectNames.iterator();
        while (iter.hasNext()) {
            String beastObjectName = iter.next();
            try {
                Class<?> _class = BEASTClassLoader.forName(beastObjectName);
                BEASTObject beastObject = (BEASTObject) _class.newInstance();
                String description = getInheritableDescription(beastObject.getClass());
                Log.warning.println(beastObjectName + " => " + description);
                m_descriptions.put(beastObjectName, description);
                String[] implementations = getImplementations(beastObject);
                m_isa.put(beastObjectName, implementations);
                for (String imp : implementations) {
                    m_ancestors.get(imp).add(beastObjectName);
                }
                if (beastObject instanceof Loggable) {
                    m_sLoggables.add(beastObjectName);
                }
            } catch (Throwable e) {
                Log.err.println(beastObjectName + " is not documented (or abstract class) :" + e.getMessage());
                rmFailedPageOfBeastObject(beastObjectName, iter);
            }
        }
    } // c'tor

    // if the page creation is failed, the BeastObject should not have html
    private void rmFailedPageOfBeastObject(String beastObjectName, Iterator<String> iter) {
        iter.remove();
        Log.err.println("Remove " + beastObjectName + " from the list of BEAST objects");
    }


    /**
     * print @Description and Input.description info so that it can
     * be inserted in the code before creating Javadoc documentation
     * for the Beast II SDK.
     */
    void makeJavaDoc() {
        for (String beastObjectName : m_beastObjectNames) {
            try {
                BEASTObject beastObject = (BEASTObject) BEASTClassLoader.forName(beastObjectName).newInstance();
                Log.info.println(beastObjectName + ":@description:" + beastObject.getDescription());
                for (Input<?> input : beastObject.listInputs()) {
                    Log.info.println(beastObjectName + ":" + input.getName() + ":" + input.getTipText());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * create CSS style sheet for all pages 
     * @throws FileNotFoundException *
     */
    void createCSS() throws FileNotFoundException  {
        PrintStream out = new PrintStream(m_sDir + "/doc.css");
        out.println(getCSS());
        out.close();
    }

    String getCSS() {
    		return  "<!--\n" +
    				 "a.summary-letter {text-decoration: none}\n" +
    				 "blockquote.smallquotation {font-size: smaller}\n" +
    				 "div.display {margin-left: 3.2em}\n" +
    				 "div.example {margin-left: 3.2em}\n" +
    				 "div.indentedblock {margin-left: 3.2em}\n" +
    				 "div.lisp {margin-left: 3.2em}\n" +
    				 "div.smalldisplay {margin-left: 3.2em}\n" +
    				 "div.smallexample {margin-left: 3.2em}\n" +
    				 "div.smallindentedblock {margin-left: 3.2em; font-size: smaller}\n" +
    				 "div.smalllisp {margin-left: 3.2em}\n" +
    				 "kbd {font-style:oblique}\n" +
    				 "pre.display {font-family: inherit}\n" +
    				 "pre.format {font-family: inherit}\n" +
    				 "pre.menu-comment {font-family: serif}\n" +
    				 "pre.menu-preformatted {font-family: serif}\n" +
    				 "pre.smalldisplay {font-family: inherit; font-size: smaller}\n" +
    				 "pre.smallexample {font-size: smaller}\n" +
    				 "pre.smallformat {font-family: inherit; font-size: smaller}\n" +
    				 "pre.smalllisp {font-size: smaller}\n" +
    				 "span.nocodebreak {white-space:nowrap}\n" +
    				 "span.nolinebreak {white-space:nowrap}\n" +
    				 "span.roman {font-family:serif; font-weight:normal}\n" +
    				 "span.sansserif {font-family:sans-serif; font-weight:normal}\n" +
    				 "ul.no-bullet {list-style: none}\n" +
    				 "body {margin-left: 5%; margin-right: 5%;}\n" +
    				 "\n" +
    				 "H1 {             \n" +
    				 "    background: white;\n" +
    				 "    color: rgb(25%, 25%, 25%);\n" +
    				 "    font-family: monospace;\n" +
    				 "    font-size: xx-large;\n" +
    				 "    text-align: center\n" +
    				 "}\n" +
    				 "\n" +
    				 "H2 {\n" +
    				 "    background: white;\n" +
    				 "    color: rgb(40%, 40%, 40%);\n" +
    				 "    font-family: monospace;\n" +
    				 "    font-size: x-large;\n" +
    				 "    text-align: center\n" +
    				 "}\n" +
    				 "\n" +
    				 "H3 {\n" +
    				 "    background: white;\n" +
    				 "    color: rgb(40%, 40%, 40%);\n" +
    				 "    font-family: monospace;\n" +
    				 "    font-size: large\n" +
    				 "}\n" +
    				 "\n" +
    				 "H4 {\n" +
    				 "    background: white;\n" +
    				 "    color: rgb(40%, 40%, 40%);\n" +
    				 "    font-family: monospace\n" +
    				 "}\n" +
    				 "\n" +
    				 "span.samp{font-family: monospace}\n" +
    				 "span.command{font-family: monospace}\n" +
    				 "span.option{font-family: monospace}\n" +
    				 "span.file{font-family: monospace}\n" +
    				 "span.env{font-family: monospace}\n" +
    				 "\n" +
    				 "ul {\n" +
    				 "    margin-top: 0.25ex;\n" +
    				 "    margin-bottom: 0.25ex;\n" +
    				 "}\n" +
    				 "li {\n" +
    				 "    margin-top: 0.25ex;\n" +
    				 "    margin-bottom: 0.25ex;\n" +
    				 "}\n" +
    				 "p {\n" +
    				 "    margin-top: 0.6ex;\n" +
    				 "    margin-bottom: 1.2ex;\n" +
    				 "}\n" +
    				 "caption {\n" +
	                 "	font:  20pt Arial, Helvetica, sans-serif;\n" +
	                 "	text-align: left;\n" +
	                 "	height: 45px;\n" +
	                 "	color: #243D02;\n" +
	                 "	border-top: 1px solid #243D02;\n" +
	                 "}\n" +
	                 "table, th, td {border: 0px;)\n"
    				 ;
    }

    /**
     * create plug in index pages, shown in left frame 
     * @throws FileNotFoundException *
     */
    void createIndex() throws FileNotFoundException  {
        {
            PrintStream out = new PrintStream(m_sDir + "/index.html");
            out.println("<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN'\n" +
                    "		   'http://www.w3.org/TR/html4/frameset.dtd'>\n" +
                    "		<HTML>\n" +
                    "		<HEAD>\n" +
                    "		<TITLE>BEAST " + version.getVersionString() + " Documentation</TITLE>\n" +
                    "		</HEAD>\n" +
                    "		<FRAMESET cols='20%, 80%'>\n" +
                    "		  <FRAMESET rows='50, 200'>\n" +
//                    "		      <FRAME src='http://www.omnomnomnom.com/random/rotate.php' align='center'>\n" +
                    "		      <FRAME src='beast.png' align='center'>\n" +
                    "		      <FRAME src='contents.html'>\n" +
                    "		  </FRAMESET>\n" +
                    "		  <FRAME name='display' src='front.html'>\n" +
                    "		</FRAMESET>\n" +
                    "		</HTML>");
            out.close();
        }

        {
        }

        {
        	try {
        		ImageView icon = FXUtils.getIcon("/beastfx/app/inputeditor/icon/beast.png");
        		BufferedImage img = SwingFXUtils.fromFXImage(icon.getImage(), null);
        		ImageIO.write(img, "png", new File(m_sDir + "/beast.png"));
        		
//	            InputStream in = new FileInputStream(new File("src/beast/app/draw/icons/beast.png"));
//	            OutputStream out = new FileOutputStream(new File(m_sDir + "/beast.png"));
//	            byte[] buf = new byte[1024];
//	            int len;
//	            while ((len = in.read(buf)) > 0) {
//	                out.write(buf, 0, len);
//	            }
//	            in.close();
//	            out.close();
        	} catch (Exception e) {
				// ignore exception -- too bad we could not 
        		Log.warning.println("WARNING: something went wrong copying beast.png image:" + e.getMessage());
			}
        }
        PrintStream out = new PrintStream(m_sDir + "/contents.html");
        out.println("<html>\n<head><title>BEAST " + version.getVersionString() + " Documentation index</title>\n" +
                "<link rel='StyleSheet' href='doc.css' type='text/css'>\n" +
                "</head>\n");
        out.println("<body>\n");
        out.println("<h1>BEAST " + version.getVersionString() + " Documentation index</h1>\n");
        String prev = null;
        String prevPackage = null;
        
        String [] names = m_beastObjectNames.toArray(new String[] {});
        Arrays.sort(names);
        for (String beastObjectName : names) {
            String next = beastObjectName.substring(0, beastObjectName.indexOf('.'));
            if (prev != null && !next.equals(prev)) {
                out.println("<hr/>");
            }
            prev = next;
            String name = beastObjectName.substring(beastObjectName.lastIndexOf('.') + 1);
            String packageName = beastObjectName.substring(0, beastObjectName.lastIndexOf('.'));
            // count nr of packages
            int i = 0;
            while (beastObjectName.indexOf('.', i) > 0) {
                name = "." + name;
                i = beastObjectName.indexOf('.', i) + 1;
            }
            Log.warning.println(name + " <= " + beastObjectName);
            if (prevPackage == null || !packageName.equals(prevPackage)) {
                out.println("<span style='color:grey'>" + packageName + "</span><br/>");
            }
            out.println("<a href='" + beastObjectName + ".html' target='display'>" + name + "</a><br/>");
            prevPackage = packageName;
        }
        out.println("</body>\n");
        out.println("</html>\n");
        out.close();
    } // createIndex

    /**
     * Find all beastObjects that are derived from given beastObject *
     */
    String[] getImplementations(BEASTObject beastObject) {
        String name = beastObject.getClass().getName();
        List<String> implementations = new ArrayList<>();
        for (String beastObjectName : m_beastObjectNames) {
            try {
                if (!beastObjectName.equals(name) && beastObject.getClass().isAssignableFrom(BEASTClassLoader.forName(beastObjectName))) {
                    implementations.add(beastObjectName);
                }
            } catch (Throwable e) {
            }
        }
        return implementations.toArray(new String[0]);
    }

    /**
     * Extract description from @Description annotation
     * but only if the description is inheritable *
     */
    String getInheritableDescription(Class<?> beastObjectClass) {
        String str = "";
        Class<?> superClass = beastObjectClass.getSuperclass();
        if (superClass != null) {
            String superName = getInheritableDescription(superClass);
            if (superName != null) {
                str += superName + "<br/>";
            }
        }
        Annotation[] classAnnotations = beastObjectClass.getAnnotations();
        for (Annotation annotation : classAnnotations) {
            if (annotation instanceof Description) {
                Description description = (Description) annotation;
                if (description.isInheritable()) {
                    str += description.value();
                } else {
                    return null;
                }
            }
        }
        return str;
    }

    /**
     * Create page for individual plug-in 
     * @throws FileNotFoundException *
     */
    void createBEASTObjectPage(String beastObjectName) throws FileNotFoundException {
        PrintStream out = new PrintStream(m_sDir + "/" + beastObjectName + ".html");
        try {
            out.print(getHTML(beastObjectName, true));
        } catch (Throwable e) {
        	Log.warning.println("Page creation failed for " + beastObjectName + ": " + e.getMessage());
        }
        out.close();
    } // createBEASTObjectPage


    public String getHTML(String beastObjectName, boolean useExternalStyleSheet) throws InstantiationException, IllegalAccessException, ClassNotFoundException  {
        StringBuffer buf = new StringBuffer();
        buf.append("<html>\n<head>\n<title>BEAST " + version.getVersionString() + " Documentation: " + beastObjectName + "</title>\n");
        if (useExternalStyleSheet) {
            buf.append("<link rel='StyleSheet' href='/tmp/styles.css' type='text/css'>\n");
        } else {
            buf.append("<style type='text/css'>\n");
            buf.append(getCSS());
            buf.append("</style>\n");
        }
        buf.append("</head>\n");
        buf.append("<body>\n");
        buf.append("<h1>BEAST " + version.getVersionString() + " Documentation: " + beastObjectName + "</h1>\n");
        BEASTObject beastObject = (BEASTObject) BEASTClassLoader.forName(beastObjectName).newInstance();

        // show all implementation of this plug-in
        String[] implementations = m_isa.get(beastObjectName);
        if (implementations == null) {
            // this class is not documented, perhaps outside ClassDiscover path?
            buf.append("No documentation available for " + beastObjectName + ". Perhaps it is not in the ClassDiscovery path\n");
            buf.append("</body>\n");
            buf.append("</html>\n");
            return buf.toString();
        }

        if (implementations.length > 0) {
            buf.append("<table border='1px'>\n");
            buf.append("<thead><tr><td>implemented by the following</td></tr></thead>\n");
            for (String imp : implementations) {
                buf.append("<tr><td><a href='" + imp + ".html'>" + imp + "</a></td></tr>\n");
            }
            buf.append("</table>\n");
        }

        // show descriptions of all plug-ins implemented by this plug in...
        buf.append("<p>" + m_descriptions.get(beastObjectName) + "</p>\n");

        // show citation (if any)
        for (Citation citation : beastObject.getCitationList()) {
            buf.append("<h2>Reference:</h2><p>" + citation.value() + "</p>\n");
            if (citation.DOI().length() > 0) {
                buf.append("<p><a href=\"https://doi.org/" + citation.DOI() + "\">doi:" + citation.DOI() + "</a></p>\n");
            }
        }

        // show if this is Loggable
        if (m_sLoggables.contains(beastObjectName)) {
            buf.append("<p>Logable:");
            buf.append(" yes, this can be used in a log.");
            buf.append("</p>\n");
//        } else {
//        	buf.append(" no, this cannot be used in a log.");
        }

        // show short list its inputs
        buf.append("<h2>Inputs:</h2>\n");
        buf.append("<p>");
        List<Input<?>> inputs = null;
        try {
        	inputs = beastObject.listInputs();
            if (inputs.size() == 0) {
                buf.append("&lt;none&gt;");
            } else {
                for (Input<?> input : inputs) {
                    buf.append("<a href='#" + input.getName()+"'>" + input.getName() + "</a>, ");
                }
                buf.delete(buf.length() - 3, buf.length()-1);
            }
        } catch (Throwable e) {
            buf.append("could not determine inputs:" + e.getMessage());
        }
        // list its inputs
        buf.append("</p>\n");

        for (Input<?> input : inputs) {
        	buf.append("<p>&nbsp</p>");
            buf.append("<table id='" + input.getName() + "' border='1px' width='90%'>\n");
            buf.append("<caption>" + input.getName() + "</caption>\n");
            buf.append("<thead><tr bgcolor='#AAAAAA'><td>type: " + getType(beastObject, input.getName()) + "</td></tr></thead>\n");
            buf.append("<tr><td>" + input.getTipText() + "</td></tr>\n");
            buf.append("<tr><td>\n");
            switch (input.getRule()) {
                case OPTIONAL:
                    buf.append("Optional input");
                    if (input.defaultValue != null) {
                        if (input.defaultValue instanceof Integer ||
                                input.defaultValue instanceof Double ||
                                input.defaultValue instanceof Boolean ||
                                input.defaultValue instanceof String) {
                            buf.append(". Default: " + input.defaultValue.toString());
                        }
                    }
                    break;
                case REQUIRED:
                    buf.append("Required input");
                    break;
                case XOR:
                    buf.append("Either this, or " + input.getOther().getName() + " needs to be specified");
                    break;
                case FORBIDDEN:
                    buf.append("Forbidden: must not be specified");
                    break;
            }
            buf.append("</td></tr>\n");
            buf.append("</table>\n");
        }
        buf.append("</body>\n");
        buf.append("</html>\n");
        return buf.toString();
    } // getHTML

    /**
     * determine type of input of a plug in with name name
     */
    String getType(BEASTObject beastObject, String name) {
        try {
            Field[] fields = beastObject.getClass().getFields();
            for (int i = 0; i < fields.length; i++) {
                if (fields[i].getType().isAssignableFrom(Input.class)) {
                    final Input<?> input = (Input<?>) fields[i].get(beastObject);
                    if (input.getName().equals(name)) {
                        Type t = fields[i].getGenericType();
                        Type[] genericTypes = ((ParameterizedType) t).getActualTypeArguments();
                        if (input.getType() != null) {
                            return (input.getType().isAssignableFrom(BEASTObject.class) ? "<a href='" + input.getType().getName() + ".html'>" : "") +
                                    input.getType().getName() +
                                    (input.get() != null && input.get() instanceof List<?> ? "***" : "") +
                                    (input.getType().isAssignableFrom(BEASTObject.class) ? "</a>" : "");
                        }
                        if (input.get() != null && input.get() instanceof List<?>) {
                            Type[] genericTypes2 = ((ParameterizedType) genericTypes[0]).getActualTypeArguments();
                            try {
	                            Class<?> _class = (Class<?>) genericTypes2[0];
	                            Object o = null;
	                            try {
	                                o = BEASTClassLoader.forName(_class.getName()).newInstance();
	                            } catch (Throwable e) {
	                            }
	                            if (o != null && o instanceof BEASTObject) {
	                                return "<a href='" + _class.getName() + ".html'>" + _class.getName() + "***</a>";
	                            } else {
	                                return _class.getName() + "***";
	                            }
                            } catch (Throwable e) {
                                return "Unknown list type***";
                            }
                        } else {
                            Class<?> genericType = (Class<?>) genericTypes[0];
                            try {
	                            Class<?> _class = genericType;
	                            Object o = null;
	                            try {
	                                o = BEASTClassLoader.forName(_class.getName()).newInstance();
	                            } catch (Throwable e) {
	                            }
	                            if (o != null && o instanceof BEASTObject) {
	                                return "<a href='" + _class.getName() + ".html'>" + _class.getName() + "</a>";
	                            } else {
	                                return _class.getName();
	                            }
                            } catch (Throwable e) {
                                return "Unknown type";
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "?";
    } // getType

    /**
     *
     * @throws FileNotFoundException
     */
    protected void createFrontPage() throws FileNotFoundException  {
        PrintStream out = new PrintStream(m_sDir + "/front.html");
        out.println("<html>\n<head><title>BEAST " + version.getVersionString() + " Documentation Front Page</title>\n" +
                "<link rel='StyleSheet' href='doc.css' type='text/css'>\n" +
                "</head>\n");
        out.println("<body>\n");
        out.println("<h1>BEAST " + version.getVersionString() + " XML Reference</h1>");
        out.println("<p>This documentation is used to help the user who wants to edit " +
                "BEAST2 <a href='http://www.w3schools.com/xml/' target='_blank'>XML</a> manually.</p>\n");

        out.println("<h2>How to read:</h2>");
        out.println("<ul>\n<li>Each page is documenting a <b>BEAST object</b>, which is used for " +
                "a value of the <i>spec</i> attribute in the XML. It may contain one or more inputs.</li>");
        out.println("<li>An <b>input</b> connects this BEAST object with the output from another BEAST object " +
                "that is also the type of this input. Each input is documented in a table of the HTML page. ");
        out.println("<ul>\n<li>The table title is the input name.</li>");
        out.println("<li>The first row is its type defined by either a Java primitive wrapper class " +
                "or Java implementation of a BEAST object.</li>");
        out.println("<li>The second row describes what the input do. </li>");
        out.println("The last row shows its validation rule, and the default value.</li>\n</ul>");

        out.println("<li>There are three options for the <b>validation rule</b> of an input: " +
                "\"Required\", \"Optional\", \"Either this, or\".</li>");
        out.println("<ul>\n<li><b>Required</b> means this input is compulsory in the XML.</li>");
        out.println("<li><b>Optional</b> means this input can be absent in the XML, " +
                "in this case, the default value is used.</li>");
        out.println("<li><b>Either this or</b> represents the situation that you have to choose this input " +
                "or the alternative input in the XML, and it will be invalid if both are missing.</li>\n</ul>");
        out.println("<li>If there is <b>***</b> behind a type, it indicates this input type is actually " +
                "a list (java.util.List) of this Java class.</li>\n</ul>");

        out.println("<h2>Example of beast.core.MCMC:</h2>");
        out.println("<p>A common example for beast.core.MCMC is :</p>");
        out.println("<xmp><run chainLength=\"100000000\" id=\"mcmc\" spec=\"MCMC\"></xmp>");
        out.println("<p>But through its XML reference, we know there is a \"Optional\" input <i>preBurnin</i> " +
                "available to define the burn-in, and it has to be an integer.</p>");
        out.println("<p> So we can modify the XML to :</p>");
        out.println("<xmp><run chainLength=\"1000000\" id=\"mcmc\" spec=\"MCMC\" preBurnin=\"1000\"></xmp>");

        out.println("<h2>Example of beast.evolution.operators.ScaleOperator:</h2>");
        out.println("<p>ScaleOperator has a \"Either this or\" rule for inputs <i>tree</i> and <i>parameter</i>, " +
                "so that it has to either scale a tree :</p>");
        out.println("<xmp><operator id='treeScaler' spec='ScaleOperator' scaleFactor=\"0.5\" weight=\"1\" tree=\"@tree\"/></xmp>");
        out.println("<p>or a RealParameter, such as kappa of HKY :</p>");
        out.println("<xmp><parameter id=\"hky.kappa\" value=\"1.0\" lower=\"0.0\"/>\n" +
                "<operator id='kappaScaler' spec='ScaleOperator' scaleFactor=\"0.5\" weight=\"1\" parameter=\"@hky.kappa\"/></xmp>");

        out.println("\n</body>\n");
        out.println("</html>\n");
        out.close();
    }

    /**
     * generate set of documents for plug ins
     * including index page + frame
     * individual pages for each plug in
     * *
     * @throws FileNotFoundException 
     */
    public void generateDocs() throws FileNotFoundException {
        // first, produce CSS & index page
        createCSS();
        createFrontPage();
        createIndex();
        // next, produce pages for individual plug-ins
        for (String beastObjectName : m_beastObjectNames) {
            createBEASTObjectPage(beastObjectName);
        }
    } // generateDocs


    /**
     * Usage: DocMaker <target directory>
     * where <target directory> is the place where the HTML files
     * should go. Default directory is /tmp
     */
    public static void main(String[] args) {
        try {
            System.err.println("Producing documentation...");
            PackageManager.loadExternalJars();
            
            // next line initialises some fx related stuff required for processing some classes and icon 
            JFXPanel jfxPanel = new JFXPanel();
            
            DocMaker b = new DocMaker(args);
            b.generateDocs();
            System.err.println("Done!!!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    } // main


} // BeastDocMaker
