// $Id: ModuleWriter.java,v 1.4 2004/02/27 16:30:00 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.module;


import com.meyling.principia.argument.Argument;
import com.meyling.principia.argument.Counter;
import com.meyling.principia.io.Output;
import com.meyling.principia.io.Utility;
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.ProofLineList;
import com.meyling.principia.logic.paragraph.Proposition;
import com.meyling.principia.logic.paragraph.Sentence;
import com.meyling.principia.logic.rule.AddAxiom;
import com.meyling.principia.logic.rule.AddSentence;
import com.meyling.principia.logic.rule.Generalization;
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.Rule;
import com.meyling.principia.logic.rule.UseAbbreviation;


/**
 * This class writes a module file.
 *
 * @version $Revision: 1.4 $
 * @author  Michael Meyling
 */
public final class ModuleWriter {


    /**
     * Write a module.
     *
     * @param   module  write this module
     * @param   output  to this output
     * @throws  ParsingException    if any parsing error occurs
     */
    public final static void writeModule(final Module module,
            final Output output) {

        output.writeln(ModuleCreator.getName(Module.class) + " (");
        // write module header
        final Header header = (Header) module.getArgument(0);
        output.writeln("  " + ModuleCreator.getName(Header.class) + " (");
        Specification spec = (Specification) header.getArgument(0);
        output.writeln("    " + ModuleCreator.getName(Specification.class)
            + " (");
        output.write("      " + ModuleCreator.getName(Name.class) + " (");
        output.write(Utility.quote(((Name) spec.getArgument(0))
            .getText()));
        output.writeln("),");
        output.write("      " + ModuleCreator.getName(Version.class)
            + " (");
        output.write(Utility.quote(((Version) spec.getArgument(1))
            .getText()));
        output.writeln("),");
        output.write("      " + ModuleCreator.getName(Version.class)
            + " (");
        output.write(Utility.quote(((Version) spec.getArgument(2))
            .getText()));
        output.writeln("),");
        LocationList locations = (LocationList) spec.getArgument(3);
        output.writeln("      " + ModuleCreator.getName(LocationList.class)
            + " (");
        for (int i = 0; i < locations.getArgumentSize(); i++) {
            output.write("        " + ModuleCreator.getName(Location.class)
                + " (");
            output.write(Utility.quote(((Location) locations.getArgument(i))
                .getText()));
            output.write(")");
            if (i + 1 < locations.getArgumentSize()) {
                output.writeln(",");
            } else {
                output.writeln();
            }
        }
        output.writeln("      )");
        output.writeln("    ),");
        output.write("    " + ModuleCreator.getName(Headline.class)
            + " (");
        output.write(Utility.quote(((Headline) header.getArgument(1))
            .getText()));
        output.writeln("),");
        output.writeln("    " + ModuleCreator.getName(Description.class)
            + " (");
        output.write("      ");
        output.writeln(Utility.quote(((Description) header.getArgument(2))
            .getText()));
        output.writeln("    ),");
        output.write("    "+ ModuleCreator.getName(Email.class) + " (");
        output.write(Utility.quote(((Email) header.getArgument(3))
            .getText()));
        output.writeln("),");
        final AuthorList authors = (AuthorList) header.getArgument(4);
        output.writeln("    " + ModuleCreator.getName(AuthorList.class)
            + " (");
        for (int i = 0; i < authors.getArgumentSize(); i++) {
            output.write("      " + ModuleCreator.getName(Author.class)
            + " (");
            output.write(Utility.quote(((Author) authors.getArgument(i))
                .getText()));
            output.write(",");
            output.write(ModuleCreator.getName(Email.class) + " (");
            output.write(Utility.quote(((Email) authors.getArgument(i)
                .getArgument(1)).getText()));
            output.write("))");
            if (i + 1 < authors.getArgumentSize()) {
                output.writeln(",");
            } else {
                output.writeln();
            }
        }
        output.writeln("    )");
        output.writeln("  ),");
        final ImportList imports = module.getImports();
        if (imports != null) {
            output.writeln("  "+ ModuleCreator.getName(ImportList.class)
                + " (");
            for (int i = 0; i < imports.getArgumentSize(); i++) {
                final Import imp = (Import) imports.getArgument(i);
                output.writeln("    " + ModuleCreator.getName(Import.class)
                    + " (");
                spec = (Specification) imp.getArgument(0);
                output.writeln("      "
                    + ModuleCreator.getName(Specification.class)  + " (");
                output.write("        " + ModuleCreator.getName(Name.class)
                    + " (");
                output.write(Utility.quote(((Name) spec.getArgument(0))
                    .getText()));
                output.writeln("),");
                output.write("        "+ ModuleCreator.getName(Version.class)
                    + " (");
                output.write(Utility.quote(((Version) spec.getArgument(1))
                    .getText()));
                output.writeln("),");
                output.write("        " + ModuleCreator.getName(Version.class)
                    + " (");
                output.write(Utility.quote(((Version) spec.getArgument(2))
                    .getText()));
                output.writeln("),");
                locations = (LocationList) spec.getArgument(3);
                output.writeln("        "
                    + ModuleCreator.getName(LocationList.class) + " (");
                for (int j = 0; j < locations.getArgumentSize(); j++) {
                    output.write("          "
                        + ModuleCreator.getName(Location.class) + " (");
                    output.write(Utility.quote(
                        ((Location) locations.getArgument(j)).getText()));
                    output.write(")");
                    if (j + 1 < locations.getArgumentSize()) {
                        output.writeln(",");
                    } else {
                        output.writeln();
                    }
                }
                output.writeln("        )");
                if (imp.getArgumentSize() > 1) {
                    output.writeln("      ),");
                    output.write("      "
                        + ModuleCreator.getName(LinkLabel.class) + " (");
                    output.write(Utility.quote(
                        ((LinkLabel) imp.getArgument(1)).getText()));
                    output.writeln(")");

                } else {
                    output.writeln("      )");
                }
                if (i + 1 < imports.getArgumentSize()) {
                    output.writeln("    ),");
                } else {
                    output.writeln("    )");
                }
            }
            output.writeln("  ),");     // end of import list
        }

        final UsedbyList usedby = module.getUsedby();
        if (usedby != null) {
            output.writeln("  " + ModuleCreator.getName(UsedbyList.class)
                + " (");
            for (int i = 0; i < usedby.getArgumentSize(); i++) {
                spec = (Specification) usedby.getArgument(i);
                output.writeln("    "
                    + ModuleCreator.getName(Specification.class) + " (");
                output.write("        " + ModuleCreator.getName(Name.class)
                    + " (");
                output.write(Utility.quote(((Name) spec.getArgument(0))
                    .getText()));
                output.writeln("),");
                output.write("      " + ModuleCreator.getName(
                    Version.class) + " (");
                output.write(Utility.quote(((Version) spec.getArgument(1))
                    .getText()));
                output.writeln("),");
                output.write("      " + ModuleCreator.getName(
                    Version.class) + " (");
                output.write(Utility.quote(((Version) spec.getArgument(2))
                    .getText()));
                output.writeln("),");
                locations = (LocationList) spec.getArgument(3);
                output.writeln("      "
                    + ModuleCreator.getName(LocationList.class) + " (");
                for (int j = 0; j < locations.getArgumentSize(); j++) {
                    output.write("        "
                        + ModuleCreator.getName(Location.class) + " (");
                    output.write(Utility.quote(
                        ((Location) locations.getArgument(j)).getText()));
                    output.write(")");
                    if (j + 1 < locations.getArgumentSize()) {
                        output.writeln(",");
                    } else {
                        output.writeln();
                    }
                }
                output.writeln("      )");
                if (i + 1 < usedby.getArgumentSize()) {
                    output.writeln("    ),");
                } else {
                    output.writeln("    )");
                }
            }
            output.writeln("  ),");     // end of usedby list
        }
        writeParagraphList(module.getParagraphs(), output);
        output.writeln(")");            // end of module
        output.writeln();
    }


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

        output.writeln("  " + ModuleCreator.getName(ParagraphList.class)
            + " (");
        final int lines = paragraphs.getArgumentSize();
        // step through all paragraphs
        for (int n = 0; n < lines; n++) {
            output.writeln("    " + ModuleCreator.getName(Paragraph.class)
                + " (");
            final Paragraph paragraph
                = (Paragraph) paragraphs.getArgument(n);
            final String label = paragraph.getLabel();
            final String preText = paragraph.getPreceedingText();
            final ParagraphCheck paragraphCheck
                = paragraph.getParagraphCheck();
            final String postText = paragraph.getFollowingText();
            output.write("      " + ModuleCreator.getName(LinkLabel.class)
                + " (");
            output.write(Utility.quote(label));
            output.writeln("),");
            if (preText != null) {
                output.write("      ");
                output.write(Utility.quote(preText));
                output.writeln(",");
            }


            if (paragraphCheck instanceof Axiom) {
                output.writeln("      " + ModuleCreator.getName(Axiom.class)
                    + " (");
                writeFormula(4, paragraphCheck.getArgument(0), output);
                output.writeln();
                output.write("      )");
            } else if (paragraphCheck instanceof Abbreviation) {
                output.writeln("      "
                    + ModuleCreator.getName(Abbreviation.class) + " (");
                writeFormula(4, paragraphCheck.getArgument(0), output);
                output.writeln(",");
                writeFormula(4, paragraphCheck.getArgument(1), output);
                output.writeln();
                output.write("    )");
            } else if (paragraphCheck instanceof Proposition) {
                output.writeln("      "
                    + ModuleCreator.getName(Proposition.class) + " (");
                output.writeln("        "
                    + ModuleCreator.getName(Sentence.class) + " (");
                writeFormula(5, paragraphCheck.getArgument(0)
                    .getArgument(0), output);
                output.writeln();
                output.writeln("        ),");
                output.writeln("        "
                    + ModuleCreator.getName(ProofLineList.class) + " (");
                for (int i = 0; i < paragraphCheck.getArgument(1)
                        .getArgumentSize(); i++) {
                    writeProofLine((ProofLine) paragraphCheck
                        .getArgument(1).getArgument(i), output);
                    if (i + 1 < paragraphCheck.getArgument(1).getArgumentSize()) {
                        output.writeln(",");
                    } else {
                        output.writeln();
                    }
                }
                output.writeln("        )");
                output.write("      )");
            } else {
                output.write(ModuleCreator.writeArgument(paragraphCheck));
            }

            if (postText != null) {
                output.writeln(",");
                output.write("      ");
                output.write(Utility.quote(postText));
            }
            output.writeln();
            output.write("    )");
            if (n + 1 < lines) {
                output.writeln(",");
            } else {
                output.writeln();
            }
        }
       output.writeln("  )");
    }


    /**
     * Write a {@link ProofLine} into an output.
     *
     * @param   line    write this proof line
     * @param   output  destination for resulting output
     */
    public final static void writeProofLine(final ProofLine line,
            final Output output) {
        output.writeln("          " + ModuleCreator.getName(ProofLine.class)
            + " (");
        writeFormula(6, line.getArgument(0), output);
        output.writeln(",");
        final Rule rule = (Rule) line.getArgument(1);
        output.write("            " + ModuleCreator.getName(rule.getClass())
            + " (");
        if (rule instanceof ModusPonens) {
            output.writeln(((Counter) rule.getArgument(0)).getNumber()
                + ", " + ((Counter) rule.getArgument(1)).getNumber()
                + ")");

        } else if (rule instanceof AddAxiom) {
            final String reference
                = ((LinkReference) rule.getArgument(0)).getReference();
            output.writeln(" " + ModuleCreator.getName(LinkReference.class)
                + " (" + Utility.quote(reference) + "))");
        } else if (rule instanceof AddSentence) {
            final String reference
                = ((LinkReference) rule.getArgument(0)).getReference();
            output.writeln(" " + ModuleCreator.getName(LinkReference.class)
                + " (" + Utility.quote(reference) + "))");
        } else if (rule instanceof Generalization) {
            output.write(((Counter) rule.getArgument(0)).getNumber() + ", ");
            writeFormula(0, rule.getArgument(1), output);
            output.writeln(")");
        } else if (rule instanceof Particularization) {
            output.write(((Counter) rule.getArgument(0)).getNumber() + ", ");
            writeFormula(0, rule.getArgument(1), output);
            output.writeln(")");
        } else if (rule instanceof RenameBoundSubjectVariable) {
            output.write(((Counter) rule.getArgument(0)).getNumber() + ", ");
            writeFormula(0, rule.getArgument(1), output);
            output.write(", ");
            writeFormula(0, rule.getArgument(2), output);
            output.write(", ");
            output.write(((Counter) rule.getArgument(3)).getNumber());
            output.writeln(")");
        } else if (rule instanceof RenameFreeSubjectVariable) {
            output.write(((Counter) rule.getArgument(0)).getNumber() + ", ");
            writeFormula(0, rule.getArgument(1), output);
            output.write(", ");
            writeFormula(0, rule.getArgument(2), output);
            output.writeln(")");
        } else if (rule instanceof ReplacePredicateVariable) {
            output.writeln();
            output.writeln("              "
                + ((Counter) rule.getArgument(0)).getNumber() + ", ");
            writeFormula(7, rule.getArgument(1), output);
            output.writeln(",");
            writeFormula(7, rule.getArgument(2), output);
            output.writeln();
            output.writeln("            )");
        } else if (rule instanceof ReplacePropositionVariable) {
            output.writeln();
            output.writeln("              "
                + ((Counter) rule.getArgument(0)).getNumber() + ", ");
            writeFormula(7, rule.getArgument(1), output);
            output.writeln(",");
            writeFormula(7, rule.getArgument(2), output);
            output.writeln();
            output.writeln("            )");
        } else if (rule instanceof ReverseAbbreviation) {
            output.write(((Counter) rule.getArgument(0)).getNumber() + ", ");
            final String reference
                = ((LinkReference) rule.getArgument(1)).getReference();
            output.write(ModuleCreator.getName(LinkReference.class)
                + " (" + Utility.quote(reference) + "), ");
            output.writeln(((Counter) rule.getArgument(2)).getNumber() + ")");
        } else if (rule instanceof UseAbbreviation) {
            output.write(((Counter) rule.getArgument(0)).getNumber() + ", ");
            final String reference
                = ((LinkReference) rule.getArgument(1)).getReference();
            output.write(ModuleCreator.getName(LinkReference.class)
                + " (" + Utility.quote(reference) + "), ");
            output.writeln(((Counter) rule.getArgument(2)).getNumber() + ")");
        } else {
            for (int i = 0; i < rule.getArgumentSize(); i++) {
                ModuleCreator.writeArgument(output, rule.getArgument(i));
                if (i + 1 < rule.getArgumentSize()) {
                    output.write(", ");
                }

            }
            output.writeln(")");
        }
        output.write("          )");
    }


    /**
     * Write a formula (or its sub arguments) into an output.
     *
     * @param   level       number of tabs before formula starts
     * @param   argument    convert this
     * @param   output      destination for resulting output
     */
    public final static void writeFormula(final int level,
            final Argument argument,
            final Output output) {
        for (int i = 0; i < level; i++) {
            output.write("  ");
        }
        ModuleCreator.writeArgument(output, argument);

    }


}
