/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.io.exports;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import java.util.Calendar;
import java.util.List;

import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import net.sf.jaxodraw.io.JaxoPreview;
import net.sf.jaxodraw.object.JaxoObject;
import net.sf.jaxodraw.object.text.JaxoLatexText;
import net.sf.jaxodraw.plugin.JaxoPluginExecutionException;
import net.sf.jaxodraw.util.JaxoColor;
import net.sf.jaxodraw.util.JaxoInfo;
import net.sf.jaxodraw.util.JaxoDictionary;
import net.sf.jaxodraw.util.JaxoPrefs;
import net.sf.jaxodraw.util.JaxoUtils;


/** The class that is responsible for creating LaTex source files that may
 * be used to produce a postscript file that is (should be) equivalent  to
 * the one produced by JaxoDraw. Not that you need J. Vermaseren's axodraw
 * package to process the LaTex source.
 * @since 2.0
 */
public class JaxoExportLatex extends JaxoExport {

    private JPanel panel;
    private JLabel scaleLabel;
    private JFrame previewFrame;
    private boolean scaleToFitPage = false;

    private static final JaxoDictionary LANGUAGE = new JaxoDictionary(JaxoExportLatex.class);

    private static final float MARGIN_IN_INCH = 2.f;
    private static final String TAB = "    ";

    /** The unique plugin id. */
    private static final String PLUGIN_ID = JaxoExportLatex.class.getName();

    /** {@inheritDoc} */
    public final String getFormatName() {
        return LANGUAGE.value("formatName");
    }

    /** {@inheritDoc} */
    public final String getFileExtension() {
        return "tex";
    }

    /** {@inheritDoc} */
    public final String getFileExtensionDescription() {
        return LANGUAGE.value("fileDescription");
    }

    /** {@inheritDoc} */
    public String description() {
        return LANGUAGE.value("description");
    }

    /** {@inheritDoc} */
    public String pluginId() {
        return PLUGIN_ID;
    }

    /** {@inheritDoc} */
    public String getShortName() {
        return "latex-export";
    }

    /** {@inheritDoc} */
    @Override
    public String getWarningForGraph() {
        String warning = null;

        if (getGraph().containsPSText()) {
            warning = getPSTextWarningForLaTeX();
        }

        if (JaxoPrefs.getIntPref(JaxoPrefs.PREF_COLORSPACE) == 2) {
            if (warning == null) {
                warning = getColorSpaceWarningForLaTeX();
            } else {
                warning = warning.concat("<p></p>").concat(getColorSpaceWarningForLaTeX());
            }
        }

        return warning;
    }

    /** {@inheritDoc} */
    protected final void exportTo(final String fileName)
        throws JaxoPluginExecutionException {
        PrintWriter texFile; // Output file for writing LaTex commands

        try {
            // Create the output file
            texFile = new PrintWriter(new FileWriter(fileName));
        } catch (IOException e) {
            throw new JaxoPluginExecutionException(
                errorDialogMessage(fileName, "write%0OpenFailed"),
                e, this);
        }

        texFile.println(latexFileAsString());

        texFile.close();

        if (texFile.checkError()) {
            throw new JaxoPluginExecutionException(
                errorDialogMessage("JaxoIO.write%0WriteFailed"),
                texFile);
        }
    }

    /**
     * Returns the exported latex file as a String.
     *
     * @return the LaTeX file as a String.
     */
    public final String latexFileAsString() {
        final StringBuffer buffer = new StringBuffer(1024);
        String latexCommand;
        String latexColor;
        String latexWidth;
        String defaultColor = "\\SetColor{Black}";
        String defaultWidth = "\\SetWidth{0.5}";
        final String nl = JaxoUtils.LINE_SEPARATOR;

        Rectangle bBox = getGraph().getBounds();

        if (bBox == null) {
            bBox = new Rectangle();
        }

        final int bBoxWidth = bBox.width;
        final int bBoxHeight = bBox.height;
        final Dimension bBoxDim = new Dimension(bBoxWidth, bBoxHeight);

        // 1 java point = 1/72 inch
        final float boxWidthInInch = bBoxWidth / 72f;
        final float boxHeightInInch = bBoxHeight / 72f;
        final float paperWidthInInch = (21.0f / 2.54f) - MARGIN_IN_INCH;
        final float paperHeightInInch = (29.7f / 2.54f) - MARGIN_IN_INCH;

        float scaleFactor = 1.f;
        final float scaleX = boxWidthInInch / paperWidthInInch;
        final float scaleY = boxHeightInInch / paperHeightInInch;

        if (scaleToFitPage) {
            scaleFactor = Math.max(1, Math.max(scaleX, scaleY));
        }

        buffer.append(getHeader()).append(nl);

        final StringBuffer comment = new StringBuffer(getGraph().getDescription());
        if (comment.length() > 0) {
            comment.append(' ');
        }

        buffer.append("\\begin{document}").append(nl).append(nl);
        buffer.append("%%JaxoComment:").append(comment).append(nl);
        buffer.append("%%JaxoScale{").append(scaleFactor).append('}').append(nl).append(nl).append(nl);
        buffer.append("\\begin{center}").append(nl);
        buffer.append("\\fcolorbox{white}{white}{").append(nl);
        buffer.append("  \\begin{picture}(")
            .append((int) (bBoxWidth / scaleFactor)).append(',')
            .append((int) (bBoxHeight / scaleFactor)).append(") (")
            .append((int) ((float) bBox.x / scaleFactor)).append(',')
            .append((int) (((float) -bBox.y) / scaleFactor)).append(')').append(nl);

        final List<JaxoObject> oblist = getGraph().getObjectList();
        for (int i = 0; i < oblist.size(); i++) {
            final JaxoObject ob = oblist.get(i);
            latexCommand = ob.latexCommand(scaleFactor, bBoxDim);

            latexColor = JaxoColor.getLatexColorCommand(ob.getColor());
            latexWidth = ob.latexWidth();

            // for the first element, we always set color and width
            if (i == 0) {
                // check that first object is not a Latex text
                // because those don't have a width
                if (ob instanceof JaxoLatexText) {
                    buffer.append(TAB).append(defaultWidth).append(nl);
                } else {
                    buffer.append(TAB).append(latexWidth).append(nl);
                }

                buffer.append(TAB).append(latexColor).append(nl);
                buffer.append(TAB).append(latexCommand).append(nl);
                defaultColor = latexColor;
                defaultWidth = latexWidth;

                // for the remaining elements, we only set color and width
                // if they differ from the previous ones
            } else {
                if (!latexWidth.equals(defaultWidth)
                        && !(ob instanceof JaxoLatexText)) {
                    buffer.append(TAB).append(latexWidth).append(nl);
                    defaultWidth = latexWidth;
                }

                if (!latexColor.equals(defaultColor)
                        && !(ob instanceof JaxoLatexText)) {
                    buffer.append(TAB).append(latexColor).append(nl);
                    defaultColor = latexColor;
                }

                buffer.append(TAB).append(latexCommand).append(nl);
            }
        }

        buffer.append("  \\end{picture}").append(nl);
        buffer.append('}').append(nl);
        buffer.append("\\end{center}").append(nl).append(nl);
        buffer.append("\\end{document}").append(nl);

        return buffer.toString();
    }

    /**
     * Export the graph to the given nonempty file name.
     *
     * @param fileName The file to export to.
     * @param rescale true if the graph should be rescaled to fit on a page
     * before experting, false otherwise.
     * @throws JaxoPluginExecutionException if exporting fails.
     * The exception message sould be displayable in error dialogs,
     * so it should be an internationalized string.
     */
    protected final void exportTo(final String fileName, final boolean rescale)
        throws JaxoPluginExecutionException {
        this.scaleToFitPage = rescale;
        exportTo(fileName);
    }

    private StringBuffer getHeader() {
        final StringBuffer buffer = new StringBuffer(1024);
        final String nl = JaxoUtils.LINE_SEPARATOR;
        final Calendar rightNow = Calendar.getInstance();
        final String creationDate =
            "CreationDate: " + rightNow.get(Calendar.DAY_OF_MONTH) + "/"
            + (rightNow.get(Calendar.MONTH) + 1) + "/"
            + rightNow.get(Calendar.YEAR);

        buffer.append(
            "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
        buffer.append(nl);
        buffer.append("%%%\t\tLaTex file generated by ").append(JaxoInfo.VERSION);
        buffer.append(nl);
        buffer.append("%%%\t\t\t").append(creationDate).append(nl);
        buffer.append(
            "%%%\tMake sure you have the axodraw4j package installed in order to proceed!");
        buffer.append(nl);
        buffer.append(
            "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
        buffer.append(nl).append(nl);
        buffer.append("\\documentclass[a4paper]{article}").append(nl).append(nl);

        final List<String> packageList = getGraph().getPackageList();

        for (int j = 0; j < packageList.size(); j++) {
            buffer.append("\\usepackage{").append(packageList.get(j)).append('}').append(nl);
        }

        buffer.append(nl);

        buffer.append("\\setlength{\\oddsidemargin}{0pt}").append(nl);
        buffer.append("\\setlength{\\evensidemargin}{0pt}").append(nl);
        buffer.append("\\setlength{\\topmargin}{0pt}").append(nl);
        buffer.append("\\setlength{\\headheight}{0pt}").append(nl);
        buffer.append("\\setlength{\\headsep}{0pt}").append(nl);
        buffer.append("\\setlength{\\topskip}{0pt}").append(nl);
        buffer.append("\\setlength{\\footskip}{0pt}").append(nl);
        buffer.append("\\setlength{\\textwidth}{\\paperwidth}").append(nl);
        buffer.append("\\addtolength{\\textwidth}{-2in}").append(nl);
        buffer.append("\\setlength{\\textheight}{\\paperheight}").append(nl);
        buffer.append("\\addtolength{\\textheight}{-2in}").append(nl);
        buffer.append(nl);
        buffer.append("\\pagestyle{empty}").append(nl).append(nl).append(nl);

        return buffer;
    }

    /** {@inheritDoc} */
    public void preview(final JaxoPreview p, final boolean sameWindow) {

        if (!sameWindow || (previewFrame == null)) {
            previewFrame = new JFrame();
        }

        p.setTitle(LANGUAGE.message("preview%0Title", getShortGraphName()));

        try {
            p.showText(latexFileAsString(), "Jaxo_tmp.tex", previewFrame);
        } catch (IOException ex) {
            setFailure(ex);
            showErrorDialog(errorDialogMessage("Jaxo_tmp.tex",
                    "write%0WriteFailed"));
        }
    }

    /** {@inheritDoc} */
    public void commitConfiguration() {
        // by default: do nothing.
    }

    /** {@inheritDoc} */
    public JComponent getConfigurationPanel() {
        if (panel == null && isRescalingNeeded()) {
            panel = new JPanel(new GridBagLayout(), false);

            final GridBagConstraints labels = new GridBagConstraints();
            final GridBagConstraints components = new GridBagConstraints();

            labels.anchor = GridBagConstraints.LINE_END;
            labels.gridx = 0;
            labels.gridy = 0;
            components.anchor = GridBagConstraints.LINE_START;
            components.gridx = 1;
            components.gridy = 0;
            components.weightx = 1;

            scaleLabel = new JLabel(LANGUAGE.value("rescaleLabel"));
            panel.add(scaleLabel, labels);

            final JCheckBox scaleBox = new JCheckBox();
            scaleBox.setSelected(scaleToFitPage);

            scaleBox.addActionListener(new ActionListener() {
                    public void actionPerformed(final ActionEvent e) {
                        scaleToFitPage = scaleBox.isSelected();
                    }
                });

            panel.add(scaleBox, components);

            labels.gridy++;
            components.gridy++;
       }
        return panel;
    }

    /** {@inheritDoc} */
    public final void updateLanguage() {
        if (scaleLabel != null) {
            scaleLabel.setText(LANGUAGE.value("rescaleLabel"));
        }
    }

    private String errorDialogMessage(final String fileName, final String key) {
        return LANGUAGE.message(key, new File(fileName));
    }

    private boolean isRescalingNeeded() {
        boolean rneeded = false;
        final Rectangle bBox = getGraph().getBounds();

        if (bBox == null) {
            return false;
        }

        final int bBoxWidth = bBox.width;
        final int bBoxHeight = bBox.height;

        // 1 java point = 1/72 inch
        final float boxWidthInInch = bBoxWidth / 72f;
        final float boxHeightInInch = bBoxHeight / 72f;
        final float paperWidthInInch = (21.0f / 2.54f) - MARGIN_IN_INCH;
        final float paperHeightInInch = (29.7f / 2.54f) - MARGIN_IN_INCH;

        final float scaleX = boxWidthInInch / paperWidthInInch;
        final float scaleY = boxHeightInInch / paperHeightInInch;

        if (scaleX > 1.f || scaleY > 1.f) {
            rneeded = true;
        }

        return rneeded;
    }

}
