package de.renew.imagenetdiff;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JScrollPane;

import org.apache.log4j.Logger;

import CH.ifa.draw.util.ColorMap;
import CH.ifa.draw.util.DrawingHelper;
import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureEnumeration;
import de.renew.draw.ui.ontology.StatusDisplayer;
import de.renew.export.io.exportFormats.PNGExportFormat;
import de.renew.plugin.PluginManager;
import de.renew.util.StringUtil;


/**
 * Command to Export images to png and run compare (ImageMagick) on them.
 * Needs installed ImageMagick in the system.
 * Produces png files from selected drawings in temp folder.
 * Also produces png image as difference image of both. Results receives the same
 * name as the first selected image with "-diff.png" extension.
 * Images remain temporarily in temp folder until reoved by the system.
 *
 * @author Lawrence Cabac
 *
 */
public class PNGDiffCommand extends AbstractDiffCommand {
    static final String IMAGE_EXTENSION = ".png";
    static final Logger LOGGER = Logger.getLogger(PNGDiffCommand.class);

    /**
     *  Constructor to create a new instance of PNGDiffCommand.
     */
    public PNGDiffCommand() {
        super("Simple PNG Diff");
        setImageExtension(IMAGE_EXTENSION);
    }

    @Override
    public File doDiff(StatusDisplayer sd, Drawing drawinga, Drawing drawingb, boolean quite) {
        if (drawinga != null && drawingb != null) {
            // try exporting drawings to png
            // and run compare on them
            PluginManager pluginManager = PluginManager.getInstance();
            boolean oldStyle = false;
            if (pluginManager != null) {
                ImageNetDiffPlugin diffPlugin =
                    (ImageNetDiffPlugin) pluginManager.getPluginByName("Renew ImageNetDiff");
                if (diffPlugin != null) {
                    oldStyle = diffPlugin.getProperties()
                        .getBoolProperty("de.renew.imagenetdiff.background");
                }
            }
            Drawing drawing1;
            Drawing drawing2;
            if (!oldStyle) {
                try {
                    drawing1 = removeBackground(drawinga);
                    drawing2 = removeBackground(drawingb);
                } catch (Exception e) {
                    LOGGER.error(e.getMessage());
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug(PNGDiffCommand.class.getSimpleName() + ": " + e);
                    }
                    drawing1 = drawinga;
                    drawing2 = drawingb;
                    oldStyle = true;
                }
            } else {
                drawing1 = drawinga;
                drawing2 = drawingb;
            }

            PNGExportFormat export = new PNGExportFormat();

            String fileName1 = drawing1.getName();
            String fileName2 = drawing2.getName();
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info(PNGDiffCommand.class.getSimpleName() + ": fileName1 " + fileName1);
                LOGGER.info(PNGDiffCommand.class.getSimpleName() + ": fileName2 " + fileName2);
            }

            Rectangle bounds1 = drawing1.getBounds();
            Rectangle bounds2 = drawing2.getBounds();
            int width = Math.max(bounds1.width + bounds1.x, bounds2.width + bounds2.x);
            int height = Math.max(bounds1.height + bounds1.y, bounds2.height + bounds2.y);
            Rectangle bounds = new Rectangle(0, 0, width, height);

            // create temporary files
            File tempFile1 = null;
            File tempFile2 = null;
            try {
                tempFile1 = File.createTempFile("export-", fileName1 + getImageExtension());
                tempFile2 = File.createTempFile("export-", fileName2 + getImageExtension());
            } catch (IOException e2) {
                LOGGER.error(e2.getMessage());
                LOGGER.debug(e2.getMessage(), e2);
            }
            if (tempFile1 != null && tempFile2 != null) {
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info(
                        PNGDiffCommand.class.getSimpleName() + ": tempFile1 "
                            + tempFile1.getAbsolutePath());
                }

                // do the export
                try {
                    export.internalExport(drawing1, tempFile1, bounds, false); // no clipping of white border (up/left)
                    export.internalExport(drawing2, tempFile2, bounds, false); //no clipping
                } catch (Exception e1) {
                    LOGGER.error(e1.getMessage());
                    LOGGER.debug(e1.getMessage(), e1);
                }

                String name1 = StringUtil.stripFilenameExtension(tempFile1.getAbsolutePath());
                String name2 = StringUtil.stripFilenameExtension(tempFile2.getAbsolutePath());

                String nameDiffNoExt = name1 + "-diff";
                String nameDiff = name1 + "-diff" + getImageExtension();

                if (oldStyle) {
                    exchangeColor(name1, 10, "white", "white");
                    exchangeColor(name2, 10, "white", "white");
                    if (drawing1.getFilename() != null
                        && (drawing1.getFilename().getName().endsWith(".aip")
                            || drawing1.getFilename().getName().endsWith(".rnw"))) {
                        LOGGER.debug("Doing AIP/RNW conversion.");
                        exchangeColor(name1, 10, "lightgray", "white");
                        exchangeColor(name1, 10, "lightgreen", "white");
                        exchangeColor(name2, 10, "lightgray", "white");
                        exchangeColor(name2, 10, "lightgreen", "white");
                        exchangeColor(name1, 10, "seagreen1", "white");
                        exchangeColor(name2, 10, "seagreen1", "white");
                        exchangeColor(name1, 10, "yellow", "white");
                        exchangeColor(name2, 10, "yellow", "white");
                    }
                }

                // compare the two images with im
                Process process = null;
                try {
                    /*
                     * With standard settings of compare the net in the first argument becomes a
                     * darker red color (#d81030) and the net in the second argument becomes a lighter
                     * red color. The darker red is replaced later with green.
                     * When the first argument is considered "old" and the second "new", the net in
                     * second arguments should become green.
                     * Thus, the compare command is run in the order name2 name1
                     */
                    process = Runtime.getRuntime().exec(
                        "compare -alpha Remove " + name2 + getImageExtension() + " " + name1
                            + getImageExtension() + " " + nameDiff); // newer version of im knows options: -highlight-color and -lowlight-color
                } catch (Exception e1) {
                    LOGGER.error("Error while executing imagemagick compare: " + e1.getMessage());
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug(
                            PNGDiffCommand.class.getSimpleName() + ": " + e1.getMessage(), e1);
                    }
                }
                int exit;
                if (process != null) {
                    try {
                        exit = process.waitFor();
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug(
                                EPSDiffCommand.class.getSimpleName() + ": process' exit code = "
                                    + exit);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // inversed color
                exchangeColor(nameDiffNoExt, 10, "#d81030", "green");

                // Finally show diff image if exists, if no quite flag is set.
                File diffFile = new File(nameDiff);
                if (!diffFile.exists()) {
                    sd.showStatus("Could not create diff image. ImageMagick installed?");
                } else if (!quite) {
                    final ImageNetDiffPlugin plugin = (ImageNetDiffPlugin) PluginManager
                        .getInstance().getPluginByName("Renew ImageNetDiff");
                    plugin.addBlock();
                    final JFrame f = getjFrame(drawing1, drawing2, plugin);

                    BufferedImage img = null;
                    try {
                        img = ImageIO.read(diffFile);
                    } catch (IOException e1) {
                        LOGGER.error(e1.getMessage());
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug(EPSDiffCommand.class.getName() + ": ", e1);
                        }
                    }
                    ImageComponent comp = new ImageComponent(img, width, height);
                    JScrollPane pane = new JScrollPane(comp);
                    f.add(pane);
                    f.setSize(new Dimension(width, height));
                    f.pack();
                    f.setVisible(true);
                } else {
                    sd.showStatus("Diff image created successfully. Name: " + diffFile.getName());
                }
                return diffFile;
            }
        } else {
            sd.showStatus("Operation canceled.");
        }
        return null;
    }

    private static JFrame getjFrame(Drawing drawing1, Drawing drawing2, ImageNetDiffPlugin plugin) {
        final JFrame f = new JFrame(
            "Diff Image: " + drawing1.getName() + " (red) " + " and " + drawing2.getName()
                + " (green)");

        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                f.dispose();
                plugin.removeBlock();
            }
        });
        return f;
    }

    private static Drawing removeBackground(Drawing drawing) throws Exception {
        Drawing clone = DrawingHelper.cloneDrawing(drawing);
        FigureEnumeration figures = clone.figures();
        while (figures.hasMoreElements()) {
            Figure figure = figures.nextElement();
            figure.setAttribute("FillColor", ColorMap.NONE);
        }
        return clone;
    }

    // will only be used for the temporary displayed image

    /**
     * This component is used to render the diff image created by comparing two drawings.
     * @author Lawrence Cabac
     */
    public static class ImageComponent extends Component {
        /**
         * The image to be displayed.
         */
        private final BufferedImage _img;
        /**
         * The dimension of the image.
         */
        private final Dimension _dim;

        /**
         *  Constructor to create an ImageComponent with a given image and size.
         * @param image the image to be displayed
         * @param dx the width of the image
         * @param dy the height of the image
         */
        public ImageComponent(BufferedImage image, int dx, int dy) {
            _img = image;
            _dim = new Dimension(dx, dy);
        }

        @Override
        public Dimension getPreferredSize() {
            return _dim;
        }

        @Override
        public void paint(Graphics g) {
            g.drawImage(_img, 0, 0, null);
        }
    }
}