// Copyright 2000, CERN, Geneva, Switzerland
package hep.physics.yappi.io;

import java.io.*;
import java.util.*;

import org.freehep.util.*;
import org.freehep.xml.util.*;

import hep.physics.yappi.*;

/**
 * @author Mark Donszelmann
 * @version $Id: HTMLYappiWriter.java,v 1.10 2002/06/12 23:15:14 duns Exp $
 */

public class HTMLYappiWriter extends XHTMLWriter {

    private static final String minus = "&#150;";
    private static final String plus =  "+";
    private static final String plusorminus = "&plusmn;";

    public HTMLYappiWriter(Writer out) {
        super(out);
    }

    public void close() throws IOException {
        super.close();
    }

    public void write(Object object) {
        if (object instanceof ParticleType) {
            write((ParticleType)object);
        } else if (object instanceof Family) {
            write((Family)object);
        } else {
            System.err.println("HTMLYappiWriter: do not know how to write object of type: "+object.getClass());
        }
    }

    public void writeHead(String title) {
        printComment(" WARNING: This file was generated by a program, do NOT edit ");

        openTag("head");

        setAttribute("name", "keywords");
        setAttribute("content", "hep, particle property, pdg, yappi");
        printTag("meta");

        setAttribute("name", "Author");
        setAttribute("content", "Auto-Generated by YaPPI: see http://yappi.freehep.org/");
        printTag("meta");

        openTag("title");
        println("Particle: "+title);
        closeTag();  // title

        setAttribute("rel", "stylesheet");
        setAttribute("type", "text/css");
        setAttribute("href", "hep/physics/yappi/io/yappi.css");
        printTag("link");

        closeTag();  // head
    }

    public void write(Family family) {
        if (family != null) {
            setAttribute("class", "family");
            openTag("div");

            encode(family.getName(), "family");

            closeTag(); // div
            printTag("hr");
        }
    }

    public void write(ParticleType particle) {
        setAttribute("class", "particle");
        openTag("div");

        // top table (name, spec)
        setAttribute("border",0);
        setAttribute("width", "100%");
        setAttribute("summary", "Particle name and definition");
        openTag("table");
        openTag("tr");

        setAttribute("nowrap", true);
        setAttribute("width", "20%");
        setAttribute("class", "particle-name");
        openTag("td");
        encode(particle.getName(), "particlename");
        closeTag(); // td

        setAttribute("nowrap", true);
        setAttribute("width", "20%");
        setAttribute("class", "particle-asciiname");
        openTag("td");
        println(particle.getName());
        closeTag(); // td

        setAttribute("nowrap", true);
        openTag("td");
        closeTag(); // td

// FIXME: this is hardcoded info
        setAttribute("nowrap", true);
        setAttribute("width", "30%");
        setAttribute("class", "particle-info");
        openTag("td");
        printPlain("I<sup class=\"particle\">G</sup>");
        printPlain("(J<sup class=\"particle\">PC</sup>) = ");
        printPlain("0<sup class=\"particle\">"+plus+"</sup>");
        printPlain("(0<sup class=\"particle\">"+minus+plus+"</sup>)");
        closeTag(); // td

        closeTag(); // tr
        closeTag(); // table

        closeTag(); // div

        // small vertical space
        openTag("p");
        print(" ");
        closeTag(); // p

        // center table
        setAttribute("class", "property");
        openTag("div");
        setAttribute("border", 0);
        setAttribute("summary", "Particle data");
        setAttribute("class", "property");
        openTag("table");
        Iterator iterator = particle.getData();
        while (iterator.hasNext()) {
            Data data = (Data)iterator.next();

            write(data);
        }
        closeTag(); // table
        closeTag(); // div

        // small vertical space
        openTag("p");
        print(" ");
        closeTag(); // p

        // decay mode table
        setAttribute("class", "decay");
        openTag("div");
        writeDecayModes(particle);
        closeTag(); // div

        // bottom line
        printTag("hr");

        // address
        setAttribute("class", "footer");
        openTag("address");
        println("Generated by ");
        setAttribute("href", "http://yappi.freehep.org");
        setAttribute("class", "footer");
        openTag("a");
        print("YaPPI, Yet another Particle Property Interface");
        closeTag(); // a
        println(", ");
        setAttribute("href", "http://www.cern.ch");
        setAttribute("class", "footer");
        openTag("a");
        print("CERN");
        closeTag(); // a
        println(", Copyright 2000, 2001.");
        closeTag(); // address

        setAttribute("class", "footer");
        openTag("address");
        println("Data provided by the ");
        setAttribute("href", "http://pdg.lbl.gov");
        setAttribute("class", "footer");
        openTag("a");
        print("PDG, Particle Data Group");
        closeTag(); // a
        println(", Copyright 2000.");
        closeTag(); // address
    }

    private void write(Data data) {
        String posError = data.getPosErrorAsString();
        String negError = data.getNegErrorAsString();
        int rowspan = ((posError == null) || (posError.equals(negError))) ? 1 : 2;

        setAttribute("class", "property");
        openTag("tr");

        // name
        setAttribute("rowspan", rowspan);
        setAttribute("nowrap", true);
        setAttribute("class", "property");
        openTag("td");
        println(data.getName());
        closeTag(); // td

        // value
        setAttribute("align", "right");
        setAttribute("rowspan", rowspan);
        setAttribute("nowrap", true);
        setAttribute("class", "property");
        openTag("td");
        int mantissa = writeValue(data.getValueAsString());
        closeTag(); // td

        // posError
        printPlain("<td nowrap=\"nowrap\"");
        if (posError != null) {
            if (rowspan == 1) {
                printPlain(" class=\"property\">");
                printPlain(plusorminus+" ");
                writeValue(posError);
            } else {
                printPlain(" class=\"property-poserror\">");
                printPlain(plus+" ");
                writeValue(posError);
            }
        } else {
            printPlain(" class=\"property\">");
        }
        printPlain("</td>");

        // x 10^-3
        setAttribute("rowspan", rowspan);
        setAttribute("nowrap", true);
        setAttribute("class", "property");
        openTag("td");
        if (mantissa != 0) {
            writeMantissa(mantissa);
        } else {
            print(" ");
        }
        closeTag(); // td

        // unit
        setAttribute("rowspan", rowspan);
        setAttribute("nowrap", true);
        setAttribute("class", "property");
        openTag("td");
        println(data.getUnit());
        closeTag(); // td

        // scalefactor
        setAttribute("rowspan", rowspan);
        setAttribute("nowrap", true);
        setAttribute("class", "property");
        openTag("td");
        double scaleFactor = data.getScaleFactor();
        if (scaleFactor > 0) {
            println("(S = "+scaleFactor+")");
        }
        closeTag(); // td

        closeTag(); // tr

        if (rowspan == 2) {
            // negError
            openTag("tr");
            printPlain("<td valign=\"bottom\" nowrap=\"nowrap\" class=\"property-negerror\">");
            printPlain(minus+" ");
            writeValue(negError);
            printPlain("</td>");
            closeTag(); // tr
        }
    }

    private void writeDecayModes(ParticleType particle) {
        setAttribute("width","100%");
        setAttribute("border",0);
        setAttribute("cellpadding",2);
        setAttribute("cellspacing",0);
        setAttribute("summary", "Particle Decay Modes");
        setAttribute("class", "decay");
        openTag("table");

        // heading row
        setAttribute("class", "decay-header");
        openTag("tr");

        // name
        setAttribute("nowrap", true);
        setAttribute("colspan", 2);
        setAttribute("align", "left");
        setAttribute("class", "decay-header");
        openTag("th");
        encode(particle.getName(), "decayheader");
        println(" Decay Modes");
        closeTag(); // th

        // fraction
        setAttribute("nowrap", true);
        setAttribute("colspan", 4);
        setAttribute("align", "left");
        setAttribute("class", "decay-header");
        openTag("th");
        print("Fraction (");
        setAttribute("class", "symbol");
        openTag("span");
        print("G");
        closeTag(); // span
        setAttribute("class", "decay-header");
        openTag("sub");
        print("i");
        closeTag(); // sub
        print("/");
        setAttribute("class", "symbol");
        openTag("span");
        print("G");
        closeTag(); // span
        println(")");
        closeTag(); // th

        // confidence level
        setAttribute("nowrap", true);
        setAttribute("class", "decay-header");
        openTag("th");
        print("Confidence");
        printTag("br");
        println("Level");
        closeTag(); // th

        // p(MeV/c), using extra table to keep "p" centered
        setAttribute("nowrap", true);
        setAttribute("align", "right");
        setAttribute("class", "decay-header");
        openTag("th");
            setAttribute("border", 0);
            setAttribute("cellspacing", 0);
            setAttribute("cellpadding", 0);
            setAttribute("class", "decay");
            openTag("table");
            setAttribute("nowrap", true);
            setAttribute("class", "decay-header");
            openTag("th");
            print("p");
            printTag("br");
            println("(MeV/c)");
            closeTag(); // th
            closeTag(); // table
        closeTag(); // th

        closeTag(); // tr

        // decay channel entries without a group
        Iterator decayIterator = particle.getDecayChannels();
        while (decayIterator.hasNext()) {
            DecayChannel decay = (DecayChannel)decayIterator.next();
            if (decay.getDecayGroup() == null) {
                write(decay);
            }
        }

        // decay group entries
        Iterator groupIterator = particle.getDecayGroups();
        while (groupIterator.hasNext()) {
            DecayGroup group = (DecayGroup)groupIterator.next();

            write(group);

            // decay channel entries of this group
            Iterator iterator = particle.getDecayChannels();
            while (iterator.hasNext()) {
                DecayChannel decay = (DecayChannel)iterator.next();
                if (decay.getDecayGroup() == group) {
                    write(decay);
                }
            }
        }

        closeTag(); // table
    }

    private void write(DecayGroup group) {
        setAttribute("class", "decay-group");
        openTag("tr");

        setAttribute("nowrap", true);
        setAttribute("colspan", 20);
        setAttribute("class", "decay-group");
        openTag("th");
        encode(group.getName(), "decay-group");
        closeTag(); // th

        closeTag(); // tr
    }

    private int bgRow = 0;

    private void write(DecayChannel decay) {
        String posError = decay.getPosErrorAsString();
        String negError = decay.getNegErrorAsString();
        int rowspan = ((posError == null) || (posError.equals(negError))) ? 1 : 2;
        setAttribute("class", "decay-"+(((bgRow % 2) == 0) ? "even" : "odd"));
        openTag("tr");

        // name
        setAttribute("nowrap", true);
        setAttribute("rowspan", rowspan);
        setAttribute("class", "decay");
        openTag("td");
        encode(decay.getName(), "decay");
        closeTag(); // td

        // FIXME: to be added
        // comment
        setAttribute("nowrap", true);
        setAttribute("rowspan", rowspan);
        setAttribute("class", "decay");
        openTag("td");
        print("&nbsp;");
        closeTag(); // td

        // value
        setAttribute("nowrap", true);
        setAttribute("rowspan", rowspan);
        setAttribute("char", ".");
        setAttribute("align", "char");
        setAttribute("class", "decay");
        openTag("td");
        int mantissa = writeValue(decay.getFractionAsString());
        closeTag(); // td

        // posError
        printPlain("<td nowrap=\"nowrap\" char=\".\" align=\"char\"");
        if (posError != null) {
            if (rowspan == 1) {
                printPlain(" class=\"decay\">");
                printPlain(plusorminus+" ");
                writeValue(posError);
            } else {
                printPlain(" class=\"decay-poserror\">");
                printPlain(plus+" ");
                writeValue(posError);
            }
        } else {
            printPlain(" class=\"decay\">&nbsp;");
        }
        printPlain("</td>");

        // FIXME: to be added
        // closing brace
        setAttribute("nowrap", true);
        setAttribute("rowspan", rowspan);
        setAttribute("align", "right");
        setAttribute("class", "decay");
        openTag("td");
        print("&nbsp;");
        closeTag(); // td

        // FIXME: to be added
        // unit
        setAttribute("nowrap", true);
        setAttribute("rowspan", rowspan);
        setAttribute("class", "decay");
        openTag("td");
        if (mantissa != 0) {
            writeMantissa(mantissa);
        } else {
            print("%");
        }
        closeTag(); // td

        // FIXME: to be added
        // confidence level/ scalefactor
        setAttribute("nowrap", true);
        setAttribute("rowspan", rowspan);
        setAttribute("align", "right");
        setAttribute("class", "decay");
        openTag("td");
        print("&nbsp;");
        closeTag(); // td

        // p
        setAttribute("nowrap", true);
        setAttribute("rowspan", rowspan);
        setAttribute("align", "right");
        setAttribute("class", "decay");
        openTag("td");
        String p = decay.getP();
        if (p != null) {
            print(""+decay.getP());
        } else {
            print(minus+" ");
        }
        closeTag(); // td

        closeTag(); // tr

        // negError
        if (rowspan == 2) {
            // negError
            setAttribute("class", "decay-"+(((bgRow % 2) == 0) ? "even" : "odd"));
            openTag("tr");
            printPlain("<td valign=\"bottom\" nowrap=\"nowrap\" char=\".\" align=\"char\" class=\"decay-negerror\">");
            printPlain(minus+" ");
            writeValue(negError);
            printPlain("</td>");
            closeTag(); // tr
        }

        // change color for next row
        bgRow++;
    }

    /**
     * @return mantissa
     */
    private int writeValue(String value) {
        String v = value;
        int m = 0;

        // handle special strings here
        if (!value.equals("seen")) {
            // find the mantissa
            int e = (value.indexOf("E") >= 0) ? value.indexOf("E") : value.indexOf("e");
            if (e > 0) {
                v = value.substring(0, e);
                try {
                    m = Integer.parseInt(value.substring(e+1));
                } catch (NumberFormatException nfe) {
                    System.err.println("Problem getting mantissa from string: "+value);
                }
            } else {
                v = value;
            }
        }

        printPlain(normalize(v));
        return m;
    }

    private void writeMantissa(int m) {
        print("x 10");
        setAttribute("class", "decay");
        openTag("sup");
        print(""+m);
        closeTag(); // sup
    }

    // FIXME: add others
    private static HashMap greekTable = new HashMap();
    static {
        greekTable.put("Delta",     "D");
        greekTable.put("Gamma",     "G");
        greekTable.put("Lambda",    "L");
        greekTable.put("Omega",     "W");
        greekTable.put("Sigma",     "S");
        greekTable.put("Upsilon",   "U");
        greekTable.put("Xi",        "X");
        greekTable.put("alpha",     "a");
        greekTable.put("beta",      "b");
        greekTable.put("chi",       "c");
        greekTable.put("delta",     "d");
        greekTable.put("eta",       "h");
        greekTable.put("gamma",     "g");
        greekTable.put("mu",        "m");
        greekTable.put("nu",        "n");
        greekTable.put("omega",     "w");
        greekTable.put("phi",       "f");
        greekTable.put("pi",        "p");
        greekTable.put("psi",       "y");
        greekTable.put("rho",       "r");
        greekTable.put("sigma",     "s");
        greekTable.put("tau",       "t");
        greekTable.put("upsilon",   "u");
        greekTable.put("xi",        "x");

        greekTable.put("arrowright", "->");
    }

    /**
     * Encode string with format
     * <pre>
     *                               - *      0
     *  Kbar(2)*[(1430)0]    as      K  (1430)              [] shows optional part
     *                                2
     * </pre>
     */
    private void encode(String s, String cls) {
        char c;
        while (!s.equals("")) {

            String particleName = "";

            // look for greek characters
            Iterator iterator = greekTable.keySet().iterator();
            boolean found = false;
            while (!found && iterator.hasNext()) {
                String greek = (String)iterator.next();
                if (s.startsWith(greek)) {
                    found = true;
                    particleName = "<span class=\"symbol\">"+greekTable.get(greek)+"</span>";
                    s = s.substring(greek.length());
                }
            }

            // normal character and no digit
            if (!found && !Character.isDigit(s.charAt(0))) {
                particleName = s.substring(0, 1);
                s = s.substring(1);
            }

            // check for bar
            if (s.startsWith("bar")) {
                printPlain("<span class=\"overline\">");
                printPlain(particleName);
                printPlain("</span>");
                s = s.substring(3);
            } else {
                printPlain(particleName);
            }

            // eat digits
            if (!found) {
                int pos = 0;
                int len = s.length();
                while ((pos < len) && Character.isDigit(s.charAt(pos))) {
                    pos++;
                }
//                if (pos == 0) pos = 1;
                printPlain(s.substring(0,pos));
                s = s.substring(pos);
            }

            // check for sub
            if ((s.length() >= 3) && (s.charAt(0)=='(') && (s.charAt(2)==')')) {
                // (c)
                printPlain("<sub class=\""+cls+"\">");
                printPlain(normalize(s.substring(1,2)));
                printPlain("</sub>");
                s = s.substring(3);
            }

            // look for 0, +, - or *
            if (s.length() > 0) {
                if (s.startsWith("0")) {
                    printPlain("<sup class=\""+cls+"\">");
                    printPlain("0");
                    printPlain("</sup>");
                    s = s.substring(1);
                } else if (s.startsWith("+")) {
                    printPlain("<sup class=\""+cls+"\">");
                    printPlain(plus);
                    printPlain("</sup>");
                    s = s.substring(1);
                } else if (s.startsWith("-")) {
                    printPlain("<sup class=\""+cls+"\">");
                    printPlain(minus);
                    printPlain("</sup>");
                    s = s.substring(1);
                } else if (s.startsWith("*")) {
                    printPlain("<sup class=\""+cls+"\">");
                    printPlain("*");
                    printPlain("</sup>");
                    s = s.substring(1);
                } else if (s.startsWith("'")) {
                    printPlain("<sup class=\""+cls+"\">");
                    printPlain("'");
                    printPlain("</sup>");
                    s = s.substring(1);
                } else if (s.startsWith("pm")) {
                    printPlain("<sup class=\""+cls+"\">");
                    printPlain(plusorminus);
                    printPlain("</sup>");
                    s = s.substring(2);
                } else if (s.startsWith("mp")) {
                    printPlain("<sup class=\""+cls+"\">");
                    printPlain(minus+"/"+plus);
                    printPlain("</sup>");
                    s = s.substring(2);
                } else {
                }
            }

            // look for (1650)
            if ((s.length() >= 3) && (s.charAt(0) == '(')) {
                int pos = s.indexOf(')');
                if (pos < 0) {
                    System.err.println("Error: non matching braces in: "+s);
                    s = s.substring(1);
                } else {
                    // (1650) or (K+K-)
                    printPlain("(");
                    encode(normalize(s.substring(1,pos)), cls);
                    printPlain(")");
                    s = s.substring(pos+1);
                }

                // look for 0, +, -, ' or *
                if (s.length() > 0) {
                    c = s.charAt(0);
                    if ((c == '0') || (c =='+') || (c == '-') || (c == '\'') || (c == '*')) {
                        printPlain("<sup class=\""+cls+"\">");
                        if (c == '-') {
                            printPlain(minus);
                        } else {
                            printPlain(s.substring(0, 1));
                        }
                        printPlain("</sup>");
                        s = s.substring(1);
                    }
                }

            }

            // eat up all white space  (avoids "K + c.c." to become "K+c.c."
            while ((s.length() > 0) && (Character.isWhitespace(s.charAt(0)))) {
                printPlain(" ");
                s = s.substring(1);
            }
        }
    }
}
