/**
 *  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.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;

import net.sf.jaxodraw.gui.JaxoDialogs;
import net.sf.jaxodraw.gui.swing.JaxoTitledBorder;
import net.sf.jaxodraw.io.JaxoPreview;
import net.sf.jaxodraw.plugin.JaxoPluginExecutionException;
import net.sf.jaxodraw.util.JaxoLanguage;
import net.sf.jaxodraw.util.JaxoLog;
import net.sf.jaxodraw.util.JaxoPrefs;
import net.sf.jaxodraw.util.JaxoUtils;


/** Create a postscript file from a Latex source file via Latex - dvips.
 * @since 2.0
 */
public class JaxoExportLatexPS extends JaxoExport {

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

    private static final String TMP_BASENAME = "Jaxo_tmp";
    private static final String PREVIEW_FILENAME = TMP_BASENAME + ".eps";
    private static final float MARGIN_IN_INCH = 2.f;

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

    /** {@inheritDoc} */
    public final String getFormatName() {
        return JaxoLanguage.translate("JaxoExportLatexPS.formatName");
    }

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

    /** {@inheritDoc} */
    public final String getFileExtensionDescription() {
        return JaxoLanguage.translate("JaxoExportLatexPS.fileDescription");
    }

    /** {@inheritDoc} */
    public String description() {
        return JaxoLanguage.translate("JaxoExportLatexPS.description");
    }

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

    /** {@inheritDoc} */
    public String getShortName() {
        return "latex-ps-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 + "<p></p>" + getColorSpaceWarningForLaTeX();
            }
        }

        return warning;
    }

    /** {@inheritDoc} */
    protected final void exportTo(final String fileName)
        throws JaxoPluginExecutionException {
        final int status = exportToWithStatus(fileName);

        if (status != 0) {
            JaxoLog.warn("Status in JaxoExportLatexPS: " + status);
            //throw new JaxoPluginExecutionException(null, this);
        }
    }

    /** Tries to export a postscript file to the given file.
     * @param fileName The name of the file to be exported.
     * @return Return status of the LaTex export, the latex or the dvips command;
     * zero if everything went fine.
     */
    public final int exportToWithStatus(final String fileName) {
        int status = 0;

        final String folder = new File(fileName).getParent();
        final String latexFileName = TMP_BASENAME + ".tex";
        final String dviFileName = TMP_BASENAME + ".dvi";
        final String logFileName = TMP_BASENAME + ".log";
        final String auxFileName = TMP_BASENAME + ".aux";
        final File latexFile = new File(folder, latexFileName);
        // FIXME: replace with try/finally
        latexFile.deleteOnExit();

        final File dviFile = new File(folder, dviFileName);
        dviFile.deleteOnExit();

        final File logFile = new File(folder, logFileName);
        logFile.deleteOnExit();

        final File auxFile = new File(folder, auxFileName);
        auxFile.deleteOnExit();

        final JaxoExportLatex lat = new JaxoExportLatex();
        lat.setParentComponent(getParentComponent());
        lat.setGraph(getGraph());

        try {
            lat.exportTo(latexFile.getAbsolutePath(), scaleToFitPage);
        } catch (JaxoPluginExecutionException e) {
            setFailure(lat.getFailure());
            JaxoLog.debug("Latex export failed: ", e);
            return 1;
        }

        // still necessary?
        if (lat.hasFailed()) { // no point in continuing
            setFailure(lat.getFailure());
            return 1;
        }

        final String latexExe = JaxoPrefs.getStringPref(JaxoPrefs.PREF_LATEXPATH);
        final String dvipsExe = JaxoPrefs.getStringPref(JaxoPrefs.PREF_DVIPSPATH);

        final String latexCommand =
            latexExe + " --interaction nonstopmode " + latexFile.getAbsolutePath();

        final String[] dvipsCommand =
            new String[]{dvipsExe, "-E", "-o", fileName, dviFile.getAbsolutePath()};

        int texStatus = 0;
        int dviStatus = 0;

        try {
            texStatus = executeCommand(latexCommand, folder);

            if (texStatus == 0) {
                dviStatus = executeCommand(dvipsCommand, folder);
            }
        } catch (IOException anIOException) {
            final String message =
                JaxoLanguage.translate("Cannot_execute_command!")
                + JaxoLanguage.translate(
                    "Please_try_'Export_LaTeX'_and_run_latex_manually.");
            JaxoLog.debug(anIOException);
            JaxoDialogs.showErrorDialog(getParentComponent(), message);
        } catch (InterruptedException intexc) {
            final String message =
                JaxoLanguage.translate("Cannot_execute_command!")
                + JaxoLanguage.translate(
                    "Please_try_'Export_LaTeX'_and_run_latex_manually.");
            JaxoLog.debug(intexc);
            JaxoDialogs.showErrorDialog(getParentComponent(), message);
        }

        status = texStatus + dviStatus;

        return status;
    }

    private static int executeCommand(final String command, final String workingDir)
        throws IOException, InterruptedException {
        final Runtime runtime = Runtime.getRuntime();
        final Process process = runtime.exec(command, null, new File(workingDir));

        int exit = 0;

        final JTextArea area = new JTextArea(20, 80);
        area.setMargin(new Insets(5, 5, 5, 5));

        final JScrollPane areaScrollPane = new JScrollPane(area);
        areaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        areaScrollPane.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createCompoundBorder(
                    new JaxoTitledBorder(JaxoLanguage.translate("LaTeX_output")),
                    BorderFactory.createEmptyBorder(5, 5, 5, 5)),
                areaScrollPane.getBorder()));

        // Why a frame and not a dialog? // What for?
        final JFrame frame = new JFrame();
        frame.setIconImage(JaxoUtils.newImage("frame.png"));
        frame.getContentPane().add(areaScrollPane, BorderLayout.CENTER);
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frame.setTitle(command);
        frame.pack();
        frame.setVisible(true);

        String lsStr;

        final BufferedReader lsIn =
            new BufferedReader(new InputStreamReader(process.getInputStream()));

        try {
            while ((lsStr = lsIn.readLine()) != null) {
                area.append(lsStr + JaxoUtils.LINE_SEPARATOR);
            }
        } catch (IOException e) {
            JaxoLog.debug(e);
        } finally {
            if (lsIn != null) {
                lsIn.close();
            }
        }

        exit = process.waitFor();

        if (exit == 0) {
            frame.dispose();
        } else {
            JaxoDialogs.showErrorDialog(null,
                JaxoLanguage.translate("Error:_Execution_of_LaTeX_failed!")
                + JaxoLanguage.translate(
                    "Please_check_the_output_window_for_further_information."));
            process.destroy();
        }

        return exit;
    }

    private static int executeCommand(final String[] command, final String workingDir)
        throws IOException, InterruptedException {
        final Runtime runtime = Runtime.getRuntime();
        final Process process = runtime.exec(command, null, new File(workingDir));

        int exit = 0;

        if (JaxoLog.isLoggable(JaxoLog.INFO)) {
            String lsStr;

            final BufferedReader lsIn =
                new BufferedReader(new InputStreamReader(
                        process.getErrorStream()));

            try {
                while ((lsStr = lsIn.readLine()) != null) {
                    JaxoLog.info(lsStr);
                }
            } catch (IOException e) {
                JaxoLog.debug(e);
            } finally {
                if (lsIn != null) {
                    lsIn.close();
                }
            }
        }

        exit = process.waitFor();

        if (exit != 0) {
            JaxoLog.warn("Error executing command: "
                + JaxoUtils.toString(command));
            JaxoDialogs.showErrorDialog(null,
                "Error:_Execution_of_dvips_failed!");
            process.destroy();
        }

        return exit;
    }

    /** {@inheritDoc} */
    public void preview(final JaxoPreview p, final boolean sameWindow) {
        if (p.canShowPostscript()) {
            final File previewFile = new File(JaxoPreview.TMP_DIR, PREVIEW_FILENAME);
            final boolean previewFileExists = previewFile.exists();
            // FIXME: replace with try/finally
            previewFile.deleteOnExit();

            final int status = exportToWithStatus(previewFile.getAbsolutePath());

            final boolean newWindow = !(sameWindow && previewFileExists);

            if ((status == 0) && newWindow) {
                // setTitle(.) -- postscript is still always external
                p.showPostscript(previewFile);
            }
        } else {
            p.showMessageForPostscriptViewer();
        }
    }

    /** {@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(
                    JaxoLanguage.translate("JaxoExportLatex.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(
                    JaxoLanguage.translate("JaxoExportLatex.rescaleLabel"));
        }
    }

    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;
    }

}
