package ac.essec.ooechs.tex2html;

/**
 * <p/>
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version,
 * provided that any use properly credits the author.
 * This program 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 General Public License for more details at http://www.gnu.org
 * </p>
 *
 * @author Olly Oechsle, University of Essex, Date: 06-Dec-2006
 * @version 1.0
 */

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.FileReader;
import java.io.*;
import java.util.Vector;

public class TexParser {

    protected String title;
    protected String author;
    protected String institute;

    StringBuffer html;
    int sectionCounter = 0;
    int subSectionCounter = 0;
    int subSubSectionCounter = 0;

    public TexParser(String in, String out) {

        html = new StringBuffer(2048);

        File f = new File(in);

        if (!f.exists()) throw new RuntimeException("File does not exist");

        BufferedReader input = null;

        try {

            input = new BufferedReader(new FileReader(f));

            StringBuffer buffer = new StringBuffer();

            int i;

            while ((i = input.read()) != -1) {
                buffer.append((char) i);
            }

            char[] chars = buffer.toString().toCharArray();

            parseChars(chars);

            save(out);

        } catch (IOException e) {
            throw new RuntimeException("Cannot read file");
        } finally {
            try {
                if (input != null) {
                    //flush and close both "input" and its underlying FileReader
                    input.close();
                }
            }
            catch (IOException ex) {
                ex.printStackTrace();
            }
        }


    }

    public void save(String out) throws IOException {
        FileOutputStream fStream = new FileOutputStream(out);
        fStream.write(toHTML().getBytes());
        fStream.close();
    }


    public void parseChars(char[] chars) {

        int cursor = 0;
        boolean insideComment = false;
        boolean insideTag = false;
        boolean insideValue = false;

        StringBuilder buffer = new StringBuilder();

        Vector<Object> tags = new Vector<Object>(100);
        TexTag currentTag = null;

      
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];

            boolean escaped = i == 0? false : chars[i - 1]  ==  '\\';

            if (!escaped && c == '%') insideComment = true;
            if (c == '\n') insideComment = false;

            if (!insideComment) {
                switch (c) {
                    case '\r':
                        // ignore Windows' carriage return character
                        continue;
                    case '\n':
                        if (!insideTag && !insideValue)  {
                            buffer.append(' ');
                            tags.add(new TexText(buffer.toString(), TexText.PLAIN));
                            buffer.setLength(0);
                        } else {
                            buffer.append(' ');
                        }
                        break;
                    case '\\':
                        // start of Tex tag
                        if (buffer.length() > 0 && buffer.charAt(0) == '\\') {
                            // create a tex tag
                            currentTag = new TexTag(buffer.toString());
                            tags.add(currentTag);
                        } else {
                            // it is just text, so add that too.
                            tags.add(new TexText(buffer.toString(), TexText.PLAIN));
                        }
                        buffer.setLength(0);
                        buffer.append("\\");
                        insideTag = true;
                        break;
                    case '}':
                        if (insideValue) {
                            insideValue = false;
                            if (currentTag != null) currentTag.setValue(buffer.toString());
                            buffer.setLength(0);
                            break;
                        } else {
                            tags.add(new TexText(buffer.toString(), TexText.INSIDE_BRACKETS));
                            buffer.setLength(0);
                            break;
                        }
                    default:
                        if (insideTag && (c == ' ' || c == '\n' || c == '{')) {

                            if (c == '{') insideValue = true;

                            insideTag = false;

                            if (buffer.length() > 0) {
                                currentTag = new TexTag(buffer.toString());
                                tags.add(currentTag);
                            }
                            buffer.setLength(0);
                            
                        } else {
                            // not inside tag
                            if (!insideTag && c == '{') {
                                // emphasis character
                                // empty buffer into tex text
                                if (buffer.length() > 0) {
                                    tags.add(new TexText(buffer.toString(), TexText.PLAIN));
                                    buffer.setLength(0);
                                }
                                currentTag = null;
                                break;
                            }
                            buffer.append(c);
                        }
                }

            }

        }

        boolean open[] = new boolean[3];
        boolean insideItemizeDescription = false;
        boolean insideTable = false;
        boolean insideTR = false;
        boolean insideTD = false;

        for (int i = 0; i < tags.size(); i++) {
            Object o = tags.elementAt(i);
            if (o instanceof TexTag) {
                TexTag tag = (TexTag) o;
                if (tag.getName().equals("\\title")) {
                    this.title = tag.getValue();
                }
                if (tag.getName().equals("\\author")) {
                    this.author = tag.getValue();
                }
                if (tag.getName().equals("\\institute")) {
                    this.institute = tag.getValue();
                }
                // make the title
                if (tag.getName().equals("\\maketitle")) {
                    html.append("<html>\n<head>\n");
                    html.append("<title>");
                    html.append(title);
                    html.append("</title>\n</head>\n<body>\n<center>\n<h1>");
                    html.append(title);
                    html.append("</h1>\n");

                    if (author != null) {
                        html.append("<p><big>");
                        html.append(author);
                        html.append("</big></p>\n");
                    }

                    if (institute != null) {
                        html.append("<p>");
                        html.append(institute);
                        html.append("</p>\n");
                    }

                    html.append("</center>\n");
                }

                if (tag.getName().equals("\\hline")) {
                    if (insideTable)  {
                        if (insideTD)  {
                            html.append("</td>\n");
                            insideTD = false;
                        }
                        if (insideTR)  {
                            html.append("</tr>\n");
                            insideTR = false;
                        }
                    }
                }

                if (tag.getName().equals("\\section")) {
                    sectionCounter++;
                    subSectionCounter = 0;
                    subSubSectionCounter = 0;

                    if (open[0]) html.append("\n</p>\n");
                    open[0] = true;

                    html.append("<h2>");
                    html.append(sectionCounter);
                    html.append(" ");
                    html.append(tag.getValue());
                    html.append("</h2>\n");

                    html.append("<p style='margin-left: 20px;'>\n");
                }

                if (tag.getName().equals("\\subsection")) {
                    subSectionCounter++;
                    subSubSectionCounter = 0;

                    if (open[1]) html.append("\n</p>\n");
                    open[1] = true;

                    html.append("<h3 style='margin: 20px;'>");
                    html.append(sectionCounter);
                    html.append(".");
                    html.append(subSectionCounter);
                    html.append(" ");
                    html.append(tag.getValue());
                    html.append("</h3>\n");

                    html.append("<p style='margin-left: 40px;'>\n");

                }

                if (tag.getName().equals("\\subsubsection")) {
                    subSubSectionCounter++;

                    if (open[2]) html.append("\n</p>\n");
                    open[1] = true;

                    html.append("\n<h4 style='margin: 0px; margin-left: 60px;'>");
                    html.append(sectionCounter);
                    html.append(".");
                    html.append(subSectionCounter);
                    html.append(".");
                    html.append(subSubSectionCounter);
                    html.append(" ");
                    html.append(tag.getValue());
                    html.append("</h4>\n");

                    html.append("<p style='margin-left: 60px;'>\n");

                }

                if (tag.getName().equals("\\emph")) {
                    html.append("<i>");
                    html.append(tag.getValue());
                    html.append("</i>");
                }

                if (tag.getName().equals("\\item")) {
                    if (open[2]) {
                        html.append("\n<li style='margin-left: 60px;'>");
                    } else {
                        if (open[1]) {
                            html.append("\n<li style='margin-left: 40px;'>");
                        } else {
                            html.append("\n<li style='margin-left: 20px;'>");
                        }
                    }
                }

                if (tag.getName().equals("\\begin")) {
                    if (tag.getValue().equals("itemize")) {
                        html.append("<ul>\n");
                    }
                    if (tag.getValue().equals("enumerate")) {
                        html.append("<ol>\n");
                    }
                    if (tag.getValue().equals("description")) {
                        // items inside square braces to be bold
                        insideItemizeDescription = true;
                    }
                    if (tag.getValue().equals("abstract")) {
                        html.append("<strong>Abstract</strong> ");
                    }
                    if (tag.getValue().equals("tabular")) {
                        html.append("\n<table border='1'>");
                        insideTable = true;
                        insideTR = false;
                        insideTD = false;
                    }
                }

                if (tag.getName().equals("\\end")) {

                    if (tag.getValue().equals("itemize")) {
                        html.append("</ul>\n");
                    }

                    if (tag.getValue().equals("description")) {
                        // items inside square braces to be bold
                        insideItemizeDescription = false;
                    }                    

                    if (tag.getValue().equals("enumerate")) {
                        html.append("</ol>\n");
                    }
                    if (tag.getValue().equals("document")) {
                        if (open[0]) html.append("\n</p>\n");
                        open[0] = false;
                        html.append("</body>\n</html>");
                    }
                    if (tag.getValue().equals("tabular")) {
                        if (insideTD) {
                            html.append("</td>\n");
                            insideTD = false;
                        }
                        if (insideTR) {
                            html.append("</tr>\n");
                            insideTR = false;
                        }
                        html.append("</table>\n");
                        insideTable = false;
                    }
                }

            } else {
                TexText text = (TexText) o;
                if (insideItemizeDescription && text.text.length() > 0 && text.type == TexText.INSIDE_BRACKETS) {
                    html.append("<strong>");
                    html.append(text);
                    html.append("</strong>");
                } else {
                    if (insideTable) {
                        if (text.text.indexOf("c|") == -1 && text.text.trim().length() > 0) {
                            if (!insideTR) {
                                html.append("<tr>");
                                insideTR = true;
                            }
                            if (!insideTD) {
                                html.append("<td>");
                                insideTD = true;
                            }
                            if (text.text.trim().endsWith("&")) {
                                html.append(text.text.trim().substring(0, text.text.trim().length() - 1));
                                html.append("</td>\n");
                                insideTD = false;
                            } else {
                                html.append(text.text.trim());
                            }
                        }
                    } else {
                        html.append(text);
                    }                                      
                }
            }
        }
    }

    public String toHTML() {
        return html.toString();
    }
}

