package de.renew.export.io.exportFormats;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.exporting.AbstractDrawingExportFormat;
import de.renew.io.api.FileApi;
import de.renew.ioontology.FileType;
import de.renew.plugin.PluginProperties;

/**
 * PNGExportFormat is a class that implements the export functionality for PNG format.
 * It extends ExportFormatAbstract and provides methods to export drawings to PNG files.
 */
public class PNGExportFormat extends AbstractDrawingExportFormat {
    private static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(PNGExportFormat.class);

    //Hotkey property names
    public static final String PNG_EXPORT_HOTKEY_PROP_NAME =
        "de.renew.hotkeymap.export.exportcurrentdrawingpng";

    private PluginProperties _plugincfg;
    // Attributes
    // Constructor

    /**
     * Constructs a PNGExportFormat with the name "PNG" and a PNGFileFilter.
     */
    public PNGExportFormat() {
        super("PNG", FileApi.createExtensionFileFilter(FileType.PNG));
    }

    public PNGExportFormat(PluginProperties plugincfg) {
        super("PNG", FileApi.createExtensionFileFilter(FileType.PNG));
        this._plugincfg = plugincfg;
    }

    // Methods

    /**
     * Determines whether the export format supports exporting multiple drawings to a single file.
     *
     * @return {@code false} since PNG export does not support multiple drawings in one file.
     */
    @Override
    public boolean canExportNto1() {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public File export(Drawing[] drawings, File path) throws Exception {
        File result = null;
        assert (result != null) : "Failure in PNGExportFormat: result == null";
        return result;
    }

    /**
     * Exports a single drawing to a PNG image file, including the entire canvas.
     *
     * @param drawing The drawing to be exported.
     * @param file The file to contain the exported image.
     * @return The File object representing the exported PNG file.
     * @throws Exception If any error occurs during the export process.
     */
    @Override
    public File export(Drawing drawing, File file) throws Exception {
        Rectangle bounds = drawing.getBounds();

        return internalExport(drawing, file, bounds, true);
    }

    /**
     * Exports a single drawing to a PNG image file, including the entire canvas.
     *
     * @param drawing The drawing to be exported.
     * @return The ByteArrayOutputStream representing the exported PNG image.
     * @throws Exception If any error occurs during the export process.
     */
    public ByteArrayOutputStream export(Drawing drawing) throws Exception {
        Rectangle bounds = drawing.getBounds();

        return internalExport(drawing, bounds, true);
    }

    /**
     * Internal PNG export.
     *
     * @param drawing The drawing to be exported.
     * @param file The file to contain the exported image.
     * @param bounds The bounds of the drawing.
     * @param removeWhiteSpace Flag indicating whether to remove white space around the exported image.
     * @return The exported file.
     * @throws IOException If any error occurs during the export process.
     */
    public File internalExport(
        Drawing drawing, File file, Rectangle bounds, boolean removeWhiteSpace) throws IOException
    {
        File pngOutput = new File(file.getAbsolutePath());

        BufferedImage netImage = createImage(drawing, bounds, removeWhiteSpace);

        ImageIO.write(netImage, "png", pngOutput);

        return pngOutput;
    }

    /**
     * Internal PNG export.
     *
     * @param drawing The drawing to be exported.
     * @param bounds The bounds of the drawing.
     * @param removeWhiteSpace Flag indicating whether to remove white space around the exported image.
     * @return The exported PNG as ByteArrayOutputStream.
     * @throws IOException If any error occurs during the export process.
     */
    public ByteArrayOutputStream internalExport(
        Drawing drawing, Rectangle bounds, boolean removeWhiteSpace) throws IOException
    {
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();

        BufferedImage netImage = createImage(drawing, bounds, removeWhiteSpace);

        ImageIO.write(netImage, "png", byteStream);

        return byteStream;
    }

    /**
     * Converts a drawing to a buffered image.
     *
     * @param drawing The drawing to be converted.
     * @param bounds The bounds of the drawing.
     * @param removeWhiteSpace Flag indicating whether to remove white space around the exported image.
     * @return The image as BufferedImage.
     */
    private BufferedImage createImage(Drawing drawing, Rectangle bounds, boolean removeWhiteSpace) {
        int x = bounds.x;
        int y = bounds.y;
        int width = bounds.width;
        int height = bounds.height;

        // png-generation
        LOGGER.info(
            PNGExportFormat.class.getName() + ": creating image. Size: " + width + " " + height);
        // create raster-image
        BufferedImage netImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics = netImage.createGraphics();
        // set renderer to render fast, quality as in Renew
        graphics.setRenderingHint(
            RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
        graphics
            .setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        graphics.setRenderingHint(
            RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
        graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
        graphics.setRenderingHint(
            RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);

        graphics.setComposite(AlphaComposite.Clear);
        graphics.fillRect(0, 0, width, height);

        if (removeWhiteSpace) {
            graphics.translate(x * -1, y * -1);
        }
        // render drawing
        graphics.setComposite(AlphaComposite.SrcOver);
        drawing.draw(graphics);
        return netImage;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean canExportDrawing(Drawing drawing) {
        boolean result = false;
        result = true;
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getShortCut() {
        // KeyEvent.VK_0 is used for resetting the zoom settings.
        // KeyEvent.VK_1 and KeyEvent.VK_3 are used for the NetComponents plugin
        if (_plugincfg != null) {
            return PluginProperties.getUserProperties()
                .getHotkeyProperty(
                    PNG_EXPORT_HOTKEY_PROP_NAME,
                    _plugincfg.getHotkeyProperty(PNG_EXPORT_HOTKEY_PROP_NAME))
                .getKeyCode();
        }
        return PluginProperties.getUserProperties().getHotkeyProperty(PNG_EXPORT_HOTKEY_PROP_NAME)
            .getKeyCode();
    }

    @Override
    public int getModifier() {
        if (_plugincfg != null) {
            return PluginProperties.getUserProperties()
                .getHotkeyProperty(
                    PNG_EXPORT_HOTKEY_PROP_NAME,
                    _plugincfg.getHotkeyProperty(PNG_EXPORT_HOTKEY_PROP_NAME))
                .getModifiers();
        }
        return PluginProperties.getUserProperties().getHotkeyProperty(PNG_EXPORT_HOTKEY_PROP_NAME)
            .getModifiers();
    }

}
