// $Id: Module2Latex.java,v 1.28 2004/03/08 14:22:03 m31 Exp $
//
// This file is part of the program suite "Principia Mathematica II"
// which is a working prototype for the main project:
// "Hilbert II" - http://www.qedeq.org
//
// Copyright 2001-2004  Michael Meyling <mime@qedeq.org>.
//
// "Principia Mathematica II" 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.
//
// 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.

package com.meyling.principia.latex;


import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.meyling.principia.argument.Argument;
import com.meyling.principia.argument.ArgumentException;
import com.meyling.principia.argument.Counter;
import com.meyling.principia.argument.CounterMarker;
import com.meyling.principia.argument.PatternVariable;
import com.meyling.principia.argument.Text;
import com.meyling.principia.io.Output;
import com.meyling.principia.io.ParsingException;
import com.meyling.principia.io.Utility;
import com.meyling.principia.logic.basic.BasicFormulaPatternVariable;
import com.meyling.principia.logic.basic.BasicSubjectVariablePatternVariable;
import com.meyling.principia.logic.basic.Conjunction;
import com.meyling.principia.logic.basic.Disjunction;
import com.meyling.principia.logic.basic.Equivalence;
import com.meyling.principia.logic.basic.ExistentialQuantifier;
import com.meyling.principia.logic.basic.Implication;
import com.meyling.principia.logic.basic.Negation;
import com.meyling.principia.logic.basic.PredicateVariable;
import com.meyling.principia.logic.basic.PropositionVariable;
import com.meyling.principia.logic.basic.SubjectVariable;
import com.meyling.principia.logic.basic.UniversalQuantifier;
import com.meyling.principia.logic.paragraph.Abbreviation;
import com.meyling.principia.logic.paragraph.Axiom;
import com.meyling.principia.logic.paragraph.LinkLabel;
import com.meyling.principia.logic.paragraph.Paragraph;
import com.meyling.principia.logic.paragraph.ParagraphCheck;
import com.meyling.principia.logic.paragraph.ProofLine;
import com.meyling.principia.logic.paragraph.Proposition;
import com.meyling.principia.logic.paragraph.RuleDeclaration;
import com.meyling.principia.logic.rule.AddAxiom;
import com.meyling.principia.logic.rule.AddSentence;
import com.meyling.principia.logic.rule.ApplyAxiom;
import com.meyling.principia.logic.rule.ApplySentence;
import com.meyling.principia.logic.rule.ElementaryEquivalence;
import com.meyling.principia.logic.rule.Generalization;
import com.meyling.principia.logic.rule.HypotheticalSyllogism;
import com.meyling.principia.logic.rule.LeftAddition;
import com.meyling.principia.logic.rule.LeftAdditionConjunction;
import com.meyling.principia.logic.rule.LeftAdditionEquivalence;
import com.meyling.principia.logic.rule.LeftAdditionImplication;
import com.meyling.principia.logic.rule.LinkReference;
import com.meyling.principia.logic.rule.ModusPonens;
import com.meyling.principia.logic.rule.Particularization;
import com.meyling.principia.logic.rule.RenameBoundSubjectVariable;
import com.meyling.principia.logic.rule.RenameFreeSubjectVariable;
import com.meyling.principia.logic.rule.ReplacePredicateVariable;
import com.meyling.principia.logic.rule.ReplacePropositionVariable;
import com.meyling.principia.logic.rule.ReverseAbbreviation;
import com.meyling.principia.logic.rule.ReverseImplication;
import com.meyling.principia.logic.rule.RightAddition;
import com.meyling.principia.logic.rule.RightAdditionConjunction;
import com.meyling.principia.logic.rule.RightAdditionEquivalence;
import com.meyling.principia.logic.rule.RightAdditionImplication;
import com.meyling.principia.logic.rule.Rule;
import com.meyling.principia.logic.rule.SubstLine;
import com.meyling.principia.logic.rule.UseAbbreviation;
import com.meyling.principia.module.Description;
import com.meyling.principia.module.Email;
import com.meyling.principia.module.Headline;
import com.meyling.principia.module.Import;
import com.meyling.principia.module.ImportList;
import com.meyling.principia.module.Module;
import com.meyling.principia.module.ModuleAddress;
import com.meyling.principia.module.ModuleContext;
import com.meyling.principia.module.Name;
import com.meyling.principia.module.ParagraphList;
import com.meyling.principia.module.Specification;
import com.meyling.principia.module.UsedbyList;
import com.meyling.principia.module.Version;


/**
 * This class converts an module into an LaTeX file.
 *
 * @version $Revision: 1.28 $
 * @author  Michael Meyling
 */
public final class Module2Latex {


    /**
     * for converting a <code>String</code> into an
     * (Argument) <ccode>class</code>
     */
    private Module module;


    /** address of module to convert */
    private final String moduleAddress;


    /** will the resulting document type */
    private final String ending = "pdf";


    /**
     * Constructor.
     *
     * @param   moduleAddress   convert module at this address
     */
    public Module2Latex(final String moduleAddress) {
        super();
        this.moduleAddress = moduleAddress;
    }


    /**
     * Convert a {@link Module} (referenced by module address
     * in constructor) into html.
     *
     * @throws  ParsingException    if any parsing error occurs
     * @throws  IOException         if any file problem occurs
     */
    public final void writeModule()
        throws ParsingException, IOException {

        this.module = ModuleContext.getInstance().loadModule(moduleAddress);
        final StringBuffer buffer = new StringBuffer();
        final Output output = new Output(buffer);

        // write module header
        final Argument header = module.getArgument(0);
        final SimpleDateFormat formatter
          = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
        final String date = formatter.format(new Date());

        output.writeln("% -*- TeX:EN -*-");
        output.writeln("%%% ====================================================================");
        output.writeln("%%% $RCSfile: Module2Latex.java,v $");
        output.writeln("%%% $Revision: 1.28 $");
        output.writeln("%%% ====================================================================");
        output.writeln("%%%");
        output.writeln("%%% Generated by class " + Module2Latex.class.getName());
        output.writeln("%%% at " + date + ".");
        output.writeln("%%%");
        output.writeln("%%% Project Hilbert II - http://www.qedeq.org");
        output.writeln("%%% Copyright 2001, 2002, 2003, 2004 Michael Meyling (mime@qedeq.org)");
        output.writeln("%%%");
        output.writeln("%%% This file is part of *Principia Mathematica II*, the prototype of");
        output.writeln("%%% Hilbert II.");
        output.writeln("%%%");
        output.writeln("%%% Permission is granted to copy, distribute and/or modify this document");
        output.writeln("%%% under the terms of the GNU Free Documentation License, Version 1.2");
        output.writeln("%%% or any later version published by the Free Software Foundation;");
        output.writeln("%%% with no Invariant Sections, no Front-Cover Texts, and no");
        output.writeln("%%% Back-Cover Texts.");
        output.writeln("%%%");
        output.writeln();
//?     output.writeln("\\documentclass{amsart}");
        output.writeln("\\documentclass[a4paper]{article}");
//?        output.writeln("\\documentclass[leqno,titlepage,openany]{amsldoc}[1999/12/13]");
        output.writeln();
/**/
        output.writeln("\\usepackage{amsmath,amsthm,amsfonts}");
        output.writeln("\\usepackage[]{color}");
        output.writeln("\\usepackage{xr}");
        output.writeln("\\usepackage{epsfig,longtable}");
//        output.writeln("\\usepackage{makeidx}");
        output.writeln("\\usepackage{varioref}");
//        output.writeln("\\usepackage[letterpaper,colorlinks,breaklinks,backref]{hyperref}");
        output.writeln("\\usepackage[bookmarksnumbered,colorlinks,breaklinks,plainpages,backref,"
////            + "pdfpagescrop={53 486 389 754},"
            + "bookmarksopen=true,pdfpagemode=None]{hyperref}");
//            + "bookmarksopen=true,pdfpagemode=FullScreen]{hyperref}");
        output.writeln();
        output.writeln("\\oddsidemargin       8mm");
        output.writeln("\\evensidemargin      9mm");
        output.writeln("\\topmargin           0mm");
        output.writeln("\\headsep             10mm");
        output.writeln("\\marginparsep        2.5mm");
        output.writeln("\\marginparwidth      25mm");
        output.writeln("\\textwidth           160mm");
        output.writeln("\\textheight          220mm");
        output.writeln();
// ?        output.writeln("\\def\\boldindex#1{\\textbf{\\hyperpage{#1}}}");
        output.writeln();
        output.writeln("\\newtheorem{axm}{Axiom}[section]");
        output.writeln("\\newtheorem{abr}{Abbreviation}[section]");
        output.writeln("\\newtheorem{thm}{Theorem}[section]");
        output.writeln("\\newtheorem{dec}{Rule Declaration}[section]");
        output.writeln("\\newtheorem{cor}[thm]{Corollary}");
        output.writeln("\\newtheorem{prop}{Proposition}");
        output.writeln("\\newtheorem{lem}[thm]{Lemma}");
        output.writeln("\\theoremstyle{remark}");
        output.writeln("\\newtheorem*{rmk}{Remark}");
        output.writeln();
        output.writeln("\\makeindex");
        output.writeln("\\makeatletter");
        output.writeln("\\externaldocument{" + module.getModuleAddress().getFileName() + "}");
        output.writeln("\\hyperbaseurl{" + module.getModuleAddress().getFileName() + "}");
        output.writeln("\\makeatother");
        output.writeln();
        output.writeln("\\setlength{\\LTleft}{0pt}");
        output.writeln("\\setlength{\\LTright}{0pt}");
        output.writeln();
        output.writeln("\\setlength{\\parindent}{0pt}");
        output.writeln("\\frenchspacing \\sloppy");
        output.writeln("\\pagestyle{headings}");
        output.writeln();
        output.writeln("\\title{"
            + ((Headline) header.getArgument(1)).getText() + "}");
        final Argument authors = header.getArgument(4);
        for (int i=0; i < authors.getArgumentSize(); i++) {
            output.writeln("\\author{"
                + ((Text) authors.getArgument(i).getArgument(0)).getText()
                + "}");
        }
        final String email = ((Email) header.getArgument(3)).getText();
        output.writeln("\\date{\\tt <" + email + ">}");
        output.writeln();
        output.writeln();
        output.writeln("\\begin{document}");
        output.writeln();
        output.writeln("\\maketitle");
        output.writeln();
/*
        output.writeln("\\makeatletter");
        output.writeln("\\long\\def\\hyper@section@backref#1#2#3{%");
        output.writeln("\\typeout{BACK REF #1 / #2 / #3}%");
        output.writeln("\\hyperlink{#3}{#2}}");
        output.writeln("\\makeatother");
        output.writeln("\\pdfbookmark{Title}{tit}");
*/
        output.writeln("\\setlongtables");
        output.writeln();
        output.writeln();
//        output.writeln("\\tableofcontents");
        output.writeln();
        output.writeln("{\\small");
        output.writeln("This document is part of the project ``Hilbert II''.");
        output.writeln("To get more information about this project look at:\\\\");
        output.writeln("\\mbox{\\url{http://www.qedeq.org}}.");
        output.writeln();
        output.writeln();
        output.writeln("Permission is granted to copy, distribute and/or modify this document");
        output.writeln("under the terms of the GNU Free Documentation License, Version 1.2");
        output.writeln("or any later version published by the Free Software Foundation;");
        output.writeln("with no Invariant Sections, no Front-Cover Texts, and no");
        output.writeln("Back-Cover Texts. See also under \\url{http://www.gnu.org/copyleft/}");
        output.writeln("}");
        output.writeln();
        output.writeln();
        output.writeln("\\section*{Abstract}");
        output.writeln();
//      output.writeln("\\begin{abstract}");
        output.writeln(qedeq2latex(((Description) header.getArgument(2)).getText()));
//      output.writeln("\\end{abstract}");
        output.writeln();
        output.writeln();
        output.writeln("\\section*{Specification}");
        output.writeln();
        output.writeln("This document has the following specification:");
        output.writeln();
        output.writeln("\\begin{longtable}[h!]{l@{\\extracolsep{\\fill}}p{12cm}}");
        output.writeln("Name: & "
            + ((Name) header.getArgument(0).getArgument(0)).getText()
            + " \\\\");
        output.writeln("Version: & "
            + ((Version) header.getArgument(0).getArgument(1)).getText()
            + " \\\\");
        output.writeln("Rule version: & "
            + ((Version) header.getArgument(0).getArgument(2)).getText()
            + " \\\\");
        output.writeln("Orgin: & "
            + "\\url{" + module.getModuleAddress() + "}"
            + " \\\\");
        output.writeln("\\end{longtable}");
        output.writeln();
        output.writeln("\\medskip");
        output.writeln();
        if (authors.getArgumentSize() < 2) {
            output.writeln("Author of this module: ");
        } else {
            output.writeln("Authors of this module: ");
        }
        output.writeln("\\begin{longtable}[h!]{l@{\\extracolsep{\\fill}}l}");
        for (int i=0; i < authors.getArgumentSize(); i++) {
            output.writeln(
                ((Text) authors.getArgument(i).getArgument(0)).getText()
                + " & " + ((Email) authors.getArgument(i).getArgument(1)).getText()
                + " \\\\");
        }
        output.writeln("\\end{longtable}");
        output.writeln();
        output.writeln();

        int position = 1;
        if (module.getArgument(position) instanceof ImportList) {
            output.writeln();
            output.writeln();
            output.writeln("\\section*{References}");
            output.writeln();
            output.writeln("This document uses the results of the following documents:");
            output.writeln();
            output.writeln("\\begin{longtable}[h!]{l@{\\extracolsep{\\fill}}p{12cm}}");
            final ImportList imports
                = (ImportList) module.getArgument(position);
            final int lines = imports.getArgumentSize();
            // step through all imports and get them

            for (int n = 0; n < lines; n++) {
                final Import imp = ((Import) imports.getArgument(n));
                output.writeln("Name: & "
                    + ((Name) imp.getArgument(0).getArgument(0)).getText()
                    + " \\\\");
                output.writeln("Version: & "
                    + ((Version) imp.getArgument(0).getArgument(1)).getText()
                    + " \\\\");
                output.writeln("Rule version: & "
                    + ((Version) imp.getArgument(0).getArgument(2)).getText()
                    + " \\\\");
                final Module mod;
                try {
                    mod = ModuleContext.getInstance().loadModule(
                        module, (Specification) imp.getArgument(0));
                } catch (ArgumentException e) { // TODO ok?
                    throw new IllegalArgumentException(e.getDescription());
                }
                String link = createQedeqLink(mod);

                output.writeln("Orgin: & " + link + " \\\\");
                link = createLink(mod);
                output.writeln(this.ending + ": & " + link + " \\\\");
            }
            output.writeln("\\end{longtable}");
            position++;
        }

        UsedbyList usedby = null;
        if (module.getArgument(position) instanceof UsedbyList) {
            usedby
                = (UsedbyList) module.getArgument(position);
            position++;
        }
        if (module.getArgument(position) instanceof ParagraphList) {
            output.writeln();
            output.writeln();
            output.writeln("\\section*{Content}");
            output.writeln();
            final ParagraphList paragraphs
                = (ParagraphList) module.getArgument(position);
            writeParagraphList(paragraphs, output);
            position++;
        }

        if (usedby != null) {
            output.writeln();
            output.writeln();
            output.writeln("\\section{Cross Reference}");
            output.writeln();
            output.writeln("This module is used by the following modules:");
            output.writeln();
            output.writeln("\\begin{longtable}[h!]{l@{\\extracolsep{\\fill}}p{12cm}}");
            final int lines = usedby.getArgumentSize();
            // step through all specifications
            for (int n = 0; n < lines; n++) {
                final Specification spec = ((Specification) usedby.getArgument(n));
                output.writeln("Name: & "
                    + ((Name) spec.getArgument(0)).getText()
                    + " \\\\");
                output.writeln("Version: & "
                    + ((Version) spec.getArgument(1)).getText()
                    + " \\\\");
                output.writeln("Rule version: & "
                    + ((Version) spec.getArgument(2)).getText()
                    + " \\\\");
                final Module mod;
                try {
                    mod = ModuleContext.getInstance().loadModule(module, spec);
                } catch (ArgumentException e) {     // TODO ok?
                    throw new IllegalArgumentException(e.getDescription());
                }
                String link = createQedeqLink(mod);
                output.writeln("Orgin: & " + link + " \\\\");
                link = createLink(mod);
                output.writeln(this.ending + ": & " + link + " \\\\");
            }
            output.writeln("\\end{longtable}");
            position++;
        }
        output.writeln();
        output.writeln();
        output.writeln("\\end{document}");

        String result = new ModuleAddress(moduleAddress).getGeneratedName("tex");
//        String result = ModuleAddress.getLocalName(moduleAddress);
//        result = result.substring(0, result.lastIndexOf(".")) + ".tex";
        final File f = new File(result).getParentFile();
        f.mkdirs();
        Utility.saveFile(result, buffer);
        System.out.println("created:\n  " + result);
    }


    /**
     * Convert a {@link ParagraphList} into LaTeX.
     *
     * @param   paragraphs  convert this
     * @param   output      destination for resulting html code
     */
    private final void writeParagraphList(final ParagraphList paragraphs,
            final Output output) {

        final int lines = paragraphs.getArgumentSize();
        // step through all paragraphs
        for (int n = 0; n < lines; n++) {
            output.writeln();
            final Paragraph paragraph
                = (Paragraph) paragraphs.getArgument(n);
            String preText = "";
            String postText = "";
            int pos = 1;
            if (paragraph.getArgument(pos) instanceof Text) {
                preText = ((Text) paragraph.getArgument(pos)).getText();
                pos++;
            }
            if (preText.length() > 0) {
                output.writeln();
                output.writeln();
                output.writeln(qedeq2latex(preText));
                output.writeln();
                output.writeln();
            }
            output.writeln();
            output.writeln();
            final ParagraphCheck kind
                = (ParagraphCheck) paragraph.getArgument(pos);
            final String label = ((LinkLabel) paragraph.getArgument(0)).getLabel();
            pos++;
            if (kind instanceof Axiom) {
                output.writeln("\\begin{axm}[" + label +"]");
                output.writeln("\\hypertarget{" + label + "}{}");
                output.writeln("\\begin{displaymath}");
                writeFormula(kind.getArgument(0), output);
                output.writeln("\\end{displaymath}");
                output.writeln("\\end{axm}");
            } else if (kind instanceof Abbreviation) {
                output.writeln("\\begin{abr}[" + label +"]");
                output.writeln("\\hypertarget{" + label + "}{}");
                output.writeln("\\begin{displaymath}");
                writeFormula(kind.getArgument(0), output);
                output.write(" \\equiv  ");
                writeFormula(kind.getArgument(1), output);
                output.writeln("\\end{displaymath}");
                output.writeln("\\end{abr}");
            } else if (kind instanceof Proposition) {
                output.writeln("\\begin{thm}[" + label +"]");
                output.writeln("\\hypertarget{" + label + "}{}");
                output.writeln("\\begin{displaymath}");
                writeFormula(kind.getArgument(0).getArgument(0), output);
                output.writeln("\\end{displaymath}");
                output.writeln("\\end{thm}");
                output.writeln("\\begin{proof}");
                output.writeln("\\mbox{}\\\\\n\\begin{longtable}[h!]{r@{\\extracolsep{\\fill}}p{9cm}@{\\extracolsep{\\fill}}p{4cm}}");
                for (int i = 1; i <= kind.getArgument(1).getArgumentSize(); i++) {
                    output.writeln("\\label{" + label + ":" + i + "}");
                    output.write("  $" + i + "$ & ");
                    writeProofLine((ProofLine) kind.getArgument(1).getArgument(i - 1),
                        output, label);
                    output.writeln(" \\\\");
                }
//              output.writeln(" & & \\mbox{\\qedhere}\n\\end{longtable}");
                output.writeln(" & & \\qedhere\n\\end{longtable}");
                output.writeln("\\end{proof}");
            } else if (kind instanceof RuleDeclaration) {
                output.writeln("\\begin{dec}[" + label +"]");
                output.writeln(((Text) kind.getArgument(1)).getText());
                output.writeln("\\hypertarget{" + label + "}{}");
//                    + ((Text) kind.getArgument(1)).getText());
                output.writeln("\\label{" + label + "}");
                output.writeln("\\end{dec}");
                output.writeln();
                if (kind.getArgumentSize() > 2) {
                    output.writeln();
                    output.writeln("{\\it References, needed for declaration:}");
                    output.writeln();
                    output.writeln();
                    for (int i = 2; i < kind.getArgumentSize(); i++) {
                        if (2 < i) {
                            output.write(", ");
                        }

                        final String reference
                            = ((LinkReference) kind.getArgument(i))
                            .getReference();
                        final String link = createLink(
                            reference);
                        output.writeln(link);
                    }
                    output.writeln();
                }
            } else {
                output.writeln(toLatex(kind.toString()));
            }
            if (pos < paragraph.getArgumentSize()) {
                postText = ((Text) paragraph.getArgument(pos)).getText();
                if (postText.length() > 0) {
                    output.writeln();
                    output.writeln();
                    output.writeln(qedeq2latex(postText));
                    output.writeln();
                    output.writeln();
                }
                output.writeln();
                output.writeln("\\bigskip");
                output.writeln();
            }
        }
    }


    /**
     * Convert a {@link ProofLine} into html.
     *
     * @param   line    convert this
     * @param   output  destination for resulting html code
     * @param   label   for proof line references
     */
    public final void writeProofLine(final ProofLine line,
            final Output output, final String label) {
        output.write("$");
        writeFormula(line.getArgument(0), output);
        output.writeln("$");
        output.write("  & {\\tiny ");
        final Rule rule = (Rule) line.getArgument(1);
        final String reference
            = this.module.getRuleDeclarationLabel(rule.getClass());
        final RuleDeclaration declaration
            = this.module.getRuleDeclaration(rule.getClass());
        final String description = ((Text) declaration.getArgument(1)).getText();
//        final String address = createLink(this.module.getLabelModule(reference));

        if (rule instanceof ModusPonens) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "MP"));
            output.write("with ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(", ");
            final int j = ((Counter) rule.getArgument(1)).getNumber();
            output.write("\\hyperref[" + label + ":" + j + "]{$" + j
                + "$}");
        } else if (rule instanceof AddAxiom) {
            output.write(createLink(reference, description));
            final String ref
                = ((LinkReference) rule.getArgument(0)).getReference();
            output.write(createLink(ref));
        } else if (rule instanceof AddSentence) {
            output.write(createLink(reference, description));
            final String ref
                = ((LinkReference) rule.getArgument(0)).getReference();
            output.write(createLink(ref));
        } else if (rule instanceof Generalization) {
            output.write(createLink(reference, description));
            output.write("by $");
            writeFormula(rule.getArgument(1), output);
            output.write("$ in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
        } else if (rule instanceof Particularization) {
            output.write(createLink(reference, description));
            output.write("by $");
            writeFormula(rule.getArgument(1), output);
            output.write("$ in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
        }  else if (rule instanceof RenameBoundSubjectVariable) {
            output.write(createLink(reference, "rename"));
            output.write(" $");
            writeFormula(rule.getArgument(1), output);
            output.write("$ into $");
            writeFormula(rule.getArgument(2), output);
            output.write("$ in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" at occurence $");
            output.write(((Counter) rule.getArgument(3)).getNumber());
            output.writeln("$");
        } else if (rule instanceof RenameFreeSubjectVariable) {
            output.write(createLink(reference, "rename"));
            output.write(" $");
            writeFormula(rule.getArgument(1), output);
            output.write("$ into $");
            writeFormula(rule.getArgument(2), output);
            output.write("$ in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.writeln();
        } else if (rule instanceof ReplacePredicateVariable) {
            output.write(createLink(reference, "replace"));
            output.write(" $");
            writeFormula(rule.getArgument(1), output);
            output.write("$ by $");
            writeFormula(rule.getArgument(2), output);
            output.write("$ in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.writeln();
        } else if (rule instanceof ReplacePropositionVariable) {
            output.write(createLink(reference, "replace"));
            output.write(" $");
            writeFormula(rule.getArgument(1), output);
            output.write("$ by $");
            writeFormula(rule.getArgument(2), output);
            output.write("$ in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.writeln();
        } else if (rule instanceof ReverseAbbreviation) {
            output.write(createLink(reference, description));
            final String ref
                = ((LinkReference) rule.getArgument(1)).getReference();
            output.write(createLink(ref));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" at occurence $");
            output.write(((Counter) rule.getArgument(2)).getNumber());
            output.writeln("$");
        } else if (rule instanceof UseAbbreviation) {
            output.write(createLink(reference, description));
            final String ref
                = ((LinkReference) rule.getArgument(1)).getReference();
            output.write(createLink(ref));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" at occurence $");
            output.write(((Counter) rule.getArgument(2)).getNumber());
            output.writeln("$");
      // new rules following
        } else if (rule instanceof SubstLine) {
            output.write(createLink(reference, description));
//            output.write(createLink(reference, "substitute variables"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.writeln();
        } else if (rule instanceof ApplyAxiom) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "apply"));
            final String ref
                = ((LinkReference) rule.getArgument(1)).getReference();
            output.write(createLink(ref));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.writeln();
        } else if (rule instanceof ApplySentence) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "apply"));
            final String ref
                = ((LinkReference) rule.getArgument(1)).getReference();
            output.write(createLink(ref));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.writeln();
        } else if (rule instanceof HypotheticalSyllogism) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "HS"));
            output.write("with ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" and ");
            final int j = ((Counter) rule.getArgument(1)).getNumber();
            output.write("\\hyperref[" + label + ":" + j + "]{$" + j
                + "$}");
            output.writeln();
        } else if (rule instanceof RightAddition) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "right disjunction addition"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" with ");
            writeFormula(rule.getArgument(2), output);
            output.writeln();
        } else if (rule instanceof LeftAddition) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "left disjunction addition"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" with ");
            writeFormula(rule.getArgument(2), output);
            output.writeln();
        } else if (rule instanceof RightAdditionImplication) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "right implication addition"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" with ");
            writeFormula(rule.getArgument(2), output);
            output.writeln();
        } else if (rule instanceof LeftAdditionImplication) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "left implication addition"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" with ");
            writeFormula(rule.getArgument(2), output);
            output.writeln();
        } else if (rule instanceof RightAdditionConjunction) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "right conjunction addition"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" with ");
            writeFormula(rule.getArgument(2), output);
            output.writeln();
        } else if (rule instanceof LeftAdditionConjunction) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "left conjunction addition"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" with ");
            writeFormula(rule.getArgument(2), output);
            output.writeln();
        } else if (rule instanceof RightAdditionEquivalence) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "right equivalence addition"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" with ");
            writeFormula(rule.getArgument(2), output);
            output.writeln();
        } else if (rule instanceof LeftAdditionEquivalence) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "left equivalence addition"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" with ");
            writeFormula(rule.getArgument(2), output);
            output.writeln();
        } else if (rule instanceof ReverseImplication) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "reverse implication"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.writeln();
        } else if (rule instanceof ElementaryEquivalence) {
//            output.write(createLink(reference, description));
            output.write(createLink(reference, "elementary equivalence"));
            output.write("in ");
            final int i = ((Counter) rule.getArgument(0)).getNumber();
            output.write("\\hyperref[" + label + ":" + i + "]{$" + i
                + "$}");
            output.write(" at ");
            final int j = ((Counter) rule.getArgument(3)).getNumber();
            output.write("\\hyperref[" + label + ":" + j + "]{$" + j
                + "$}");
            output.write(" of ");
            final String ref1
                = ((LinkReference) rule.getArgument(1)).getReference();
            output.write(createLink(ref1));
            output.write(" with ");
            final String ref2
                = ((LinkReference) rule.getArgument(1)).getReference();
            output.write(createLink(ref2));
            output.writeln();
        } else {
            output.write(createLink(reference, description));
            for (int i = 2; i < rule.getArgumentSize(); i++) {
                output.write(rule.getArgument(i) + " ");
            }
        }
        output.write("}");
    }

    /**
     * Convert a {@link Formula} or its sub arguments into LaTeX.
     *
     * @param   argument    convert this
     * @param   output      destination for resulting LaTeX
     */
    public final void writeFormula(final Argument argument,
            final Output output) {
        if (argument instanceof Conjunction) {
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\ \\wedge \\  ");
            writeFormulaRest(argument.getArgument(1), output);
        } else if (argument instanceof Disjunction) {
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\ \\vee \\  ");
            writeFormulaRest(argument.getArgument(1), output);
        } else if (argument instanceof Implication) {
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\ \\rightarrow \\  ");
            writeFormulaRest(argument.getArgument(1), output);
        } else if (argument instanceof Equivalence) {
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\ \\leftrightarrow \\  ");
            writeFormulaRest(argument.getArgument(1), output);
        } else {
            writeFormulaRest(argument, output);
        }
    }


    /**
     * Convert a {@link Formula} or its sub arguments into LaTeX.
     *
     * @param   argument    convert this
     * @param   output      destination for resulting LaTeX
     */
    private final void writeFormulaRest(final Argument argument,
            final Output output) {
        if (argument instanceof Conjunction) {
            output.write("(");
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\ \\wedge \\  ");
            writeFormulaRest(argument.getArgument(1), output);
            output.write(")");
        } else if (argument instanceof Disjunction) {
            output.write("(");
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\ \\vee \\  ");
            writeFormulaRest(argument.getArgument(1), output);
            output.write(")");
        } else if (argument instanceof Implication) {
            output.write("(");
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\ \\rightarrow \\  ");
            writeFormulaRest(argument.getArgument(1), output);
            output.write(")");
        } else if (argument instanceof Equivalence) {
            output.write("(");
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\ \\leftrightarrow \\  ");
            writeFormulaRest(argument.getArgument(1), output);
            output.write(")");
        } else if (argument instanceof Negation) {
            output.write("\\neg ");
            writeFormulaRest(argument.getArgument(0), output);
        } else if (argument instanceof ExistentialQuantifier) {
            output.write("\\exists ");
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\, ");
//            output.write("(");
            writeFormulaRest(argument.getArgument(1), output);
//            output.write(")");
        } else if (argument instanceof UniversalQuantifier) {
            output.write("\\forall ");
            writeFormulaRest(argument.getArgument(0), output);
            output.write("\\, ");
//            output.write("(");
            writeFormulaRest(argument.getArgument(1), output);
//            output.write(")");
        } else if (argument instanceof SubjectVariable) {
            String var = "";
            final int n = ((CounterMarker)
                argument.getArgument(0)).getNumber();
            if (argument.getArgument(0) instanceof PatternVariable) {
                var = "x_{@" + n + "}";
            } else {
                switch (n) {
                    case 1: var = "x";
                            break;
                    case 2: var = "y";
                            break;
                    case 3: var = "z";
                            break;
                    case 4: var = "u";
                            break;
                    case 5: var = "v";
                            break;
                    case 6: var = "w";
                            break;
                    case 7: var = "r";
                            break;
                    case 8: var = "s";
                            break;
                    case 9: var = "t";
                            break;
                    case 10: var = "a";
                            break;
                    case 11: var = "b";
                            break;
                    case 12: var = "c";
                            break;
                    case 13: var = "d";
                            break;
                    default:
                            var = "x_{" + n + "}";
                }
            }
            output.write(var);
        } else if (argument instanceof PropositionVariable) {
            String var = "";
            final int n = ((CounterMarker)
                argument.getArgument(0)).getNumber();
            if (argument.getArgument(0) instanceof PatternVariable) {
                var = "P_{@" + n + "}";
            } else {
                switch (n) {
                case 1: var = "P";
                        break;
                case 2: var = "Q";
                        break;
                case 3: var = "A";
                        break;
                case 4: var = "B";
                        break;
                case 5: var = "C";
                        break;
                case 6: var = "D";
                        break;
                default:
                        var = "P_{" + n + "}";
                }
            }
            output.write(var);
        } else if (argument instanceof PredicateVariable) {
            String var = "";
            final int n = ((CounterMarker)
                argument.getArgument(0)).getNumber();
            if (argument.getArgument(0) instanceof PatternVariable) {
                var = "R_{@" + n + "}";
            } else {
                switch (n) {
                case 1: var = "R";
                        break;
                case 2: var = "F";
                        break;
                case 3: var = "G";
                        break;
                case 4: var = "H";
                        break;
                case 5: var = "I";
                        break;
                case 6: var = "J";
                        break;
                default:
                        var = "R_{" + n + "}";
                }
            }
            output.write(var + "(");
            Argument list = argument.getArgument(1);
            for (int i = 0; i < list.getArgumentSize(); i++) {
                if (i > 0) {
                    output.write(", ");
                }
                writeFormulaRest(list.getArgument(i), output);
            }
            output.write(")");
        } else if (argument instanceof BasicSubjectVariablePatternVariable) {
            final int n = ((PatternVariable)
                argument).getNumber();
                output.write("@S_{" + n + "}");
        } else if (argument instanceof BasicFormulaPatternVariable) {
            final int n = ((PatternVariable)
                argument).getNumber();
                output.write("@F_{" + n + "}");
        }

    }


    /**
     * Escape certain control seqences: &quot;, &amp;.
     * Doesn't escape &lt: or &gt;!
     *
     * @param   text    excape this text
     * @return  escaped text
     */
    public final static String toLatex(final String text) {

        final StringBuffer buffer = new StringBuffer(text.length());
        for (int i = 0; i < text.length(); i++) {
            final char c = text.charAt(i);
            switch (c) {
            case '_':  buffer.append("\\_");
                break;
            case '&':   buffer.append("\\&");
                break;
            case '#':   buffer.append("\\#");
                break;
            case '%':   buffer.append("\\%");
                break;
            case '$':   buffer.append("\\$");
                break;
            case '^':   buffer.append("\\^ ");
                break;
            case '~':   buffer.append("\\~ ");
                break;
            case '\\':   buffer.append("$\\backslash$");
                break;
            default:
                buffer.append(c);
            }
        }
        return buffer.toString();
    }



    /**
     * Replace \qedeq by LaTeX commands.
     *
     * @param   text    excape this text
     * @return  pure LaTeX
     */
    public final static String qedeq2latex(final String text) {

        final StringBuffer buffer = new StringBuffer(text.length());

        int i = 0;
        int j = 0;
        int k = 0;
        while (0 <= (j = text.indexOf("\\qedeq{", i))) {
            buffer.append(text.substring(i, j));
            int counter = 0;
            for (k = j + 7; k < text.length(); k++) {
                final char c = text.charAt(k);
                if (c == '}') {
                    if (counter == 0) {
                        break;
                    } else {
                        counter--;
                    }
                } else if (c == '{') {
                    counter++;
                }
            }
//            System.out.println(text.substring(j + 7, k));
//            System.out.println(Qedeq2Latex.convert(text.substring(j + 7, k)));
            buffer.append('$');
            buffer.append(Qedeq2Latex.convert(text.substring(j + 7, k)));
            buffer.append('$');
            i = k + 1;
        }
        buffer.append(text.substring(i));
        return buffer.toString();
    }


    /**
     * Strip html tags out of text.
     *
     * @param   text    strip this text
     * @return  striped text
     */
    public final static String stripTags(final String text) {

        final StringBuffer buffer = new StringBuffer(text.length());
        boolean quoted = false;
        for (int i = 0; i < text.length(); i++) {
            final char c = text.charAt(i);
            switch (c) {
            case '\"':  quoted = !quoted;
                break;
            case '<':
                if (!quoted) {
                    do {
                        i++;
                    } while (i < text.length() && text.charAt(i) != '>');
                } else {
                    buffer.append("&lt;");
                }
                break;
            case '>': buffer.append("&gt;");
                break;
            case '&': buffer.append("&amp;");
                break;
            default:
                buffer.append(c);
            }
        }
        return buffer.toString();
    }


    /**
     * Get link address for referenced module.
     *
     * @param   referenced  link to this module
     * @return  link address
     */
    public final String createLink(final Module referenced) {
        final String address = ModuleAddress.newEnding(
            this.module.getModuleAddress().createRelativeAddress(
            referenced.getModuleAddress()),
            ending);
        return "\\url{" + address + "} ";
    }


    /**
     * Get link address for referenced module.
     *
     * @param   refernce    label within module
     * @return  link command
     */
    public final String createLink(final String reference) {
        return createLink(reference, reference);
    }


    /**
     * Get link address for referenced module.
     *
     * @param   refernce    label within module
     * @param   description  describes linked object
     * @return  link command
     */
    public final String createLink(final String reference,
            final String description) {
        final String address = ModuleAddress.newEnding(
            this.module.getModuleAddress().createRelativeAddress(
            this.module.getLabelModule(reference).getModuleAddress()),
            ending);
//        if (address.length() > 0) {
//            return "\\href{" + address + "#" + reference + "}{" + description + "} ";
//        } else {
            return "\\hyperref{" + address + "}{}{" + reference + "}{"
                + toLatex(description) + "} ";
//        }
    }


    /**
     * Get link address for referenced module.
     *
     * @param   referenced  link to this module
     * @return  link address
     */
    public final String createQedeqLink(final Module referenced) {
        final String address = this.module.getModuleAddress().createRelativeAddress(
            referenced.getModuleAddress());
        return "\\url{" + address + "} ";
    }



}
