package io.gitlab.jfronny.ImgJava.util;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

/**
 * Der PictureViewer ist ein JFrame, der in der Lage ist ein
 * Objekt der Klasse Picture anzuzeigen. Zusätzlich können
 * mehrere Verarbeitungsschritte gespeichert werden, um ein
 * "zurück"-Funktion zu ermöglichen.
 *
 * @author Thomas Schaller
 * @version V1.2 vom 06.12.2019
 */
public class ImageStackViewer extends JFrame implements MouseWheelListener, KeyListener {
    public static final int FIT = -1;
    public static final int NORMAL = 1;

    private double zoom;
    private final List<BufferedImage> history = new ArrayList<>();
    private int currentIndex = 0;
    private final JLabel imagePane = new JLabel();
    private final JScrollPane scrollPane;

    public ImageStackViewer(Dimension imageSize) {
        this.zoom = NORMAL;
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        Container cp = getContentPane();
        cp.setLayout(new BorderLayout());
        imagePane.setPreferredSize(imageSize);
        scrollPane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scrollPane.setViewportView(imagePane);

        cp.add(scrollPane, BorderLayout.CENTER);
        pack();
        this.setVisible(true);

        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        int x = (d.width - getSize().width);
        int y = 0;
        setLocation(x, y);

        scrollPane.addMouseWheelListener(this);
        scrollPane.addKeyListener(this);
        this.addMouseWheelListener(this);
        this.addKeyListener(this);

        setTitle("ImageViewer");
    }

    /**
     * Speichert das übergebene Bild in der History.
     */
    public Picture push(Picture image) {
        BufferedImage base = image.getImage();
        BufferedImage bNew = new BufferedImage(base.getWidth(), base.getHeight(), base.getType());
        image.getImage().copyData(bNew.getRaster());
        history.add(bNew);
        if (history.size() == currentIndex + 2)
            currentIndex++;
        fixTitle();
        return image;
    }

    /**
     * Ruft das letzte abgespeicherte Bild aus der History wieder auf.
     */
    public void pop() {
        if (history.size() > 0) {
            history.remove(history.size() - 1);
            repaint();
        }
        fixTitle();
    }

    /**
     * Setzt den Zoom-Faktor für das Bild.
     * Als Zoomfaktor sind auch die Konstanten Bildanzeiger.FIT (auf Bildschirmgröße zoomen) und Bildanzeiger.NORMAL (100%) möglich.
     *
     * @param zoom Zoomfaktor (1.0 = 100%).
     */
    public void setZoom(double zoom) {
        if (zoom > 0.01 && zoom < 10.0) {
            this.zoom = zoom;
        }
        // else this.zoom = NORMAL;
        resize();
        repaint();
        fixTitle();
    }

    /**
     * Passt die Framegröße an das anzuzeigende Bild an.
     */
    public void resize() {
        BufferedImage pictureCurrent = getCurrent();
        int pref_x = (int) (pictureCurrent.getWidth() * zoom);
        int pref_y = (int) (pictureCurrent.getHeight() * zoom);
        imagePane.setPreferredSize(new Dimension(pref_x, pref_y));

        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        if ((pref_x > d.getWidth() - 30) || (pref_y > d.getHeight() - 100)) {
            pref_x = Math.min(pref_x, (int) d.getWidth() - 30);
            pref_y = Math.min(pref_y, (int) d.getHeight() - 100);
            scrollPane.setPreferredSize(new Dimension(pref_x, pref_y));
        } else scrollPane.setPreferredSize(null);

        imagePane.revalidate();
        pack();
    }

    /**
     * Setzt das angezeigt Bild neu und beachtet dabei den Zoomfaktor.
     */
    @Override
    public void repaint() {
        double f = zoom;

        BufferedImage pictureCurrent = getCurrent();
        if (pictureCurrent != null) {
            if (zoom == FIT) {
                double faktorw = (double) imagePane.getWidth() / pictureCurrent.getWidth();
                double faktorh = (double) imagePane.getHeight() / pictureCurrent.getHeight();
                f = Math.min(faktorw, faktorh);
            }
            int disp_width = (int) (pictureCurrent.getWidth() * f);
            int disp_height = (int) (pictureCurrent.getHeight() * f);

            BufferedImage image = new BufferedImage(disp_width, disp_height, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(pictureCurrent, 0, 0, disp_width, disp_height, 0, 0, pictureCurrent.getWidth(), pictureCurrent.getHeight(), null);
            g.setColor(new java.awt.Color(0, 0, 0));
            g.setStroke(new BasicStroke((float) 1));
            g.drawRect(0, 0, disp_width - 1, disp_height - 1);

            imagePane.setIcon(new ImageIcon(image));
        }

        imagePane.repaint();
        super.repaint();
    }

    private BufferedImage getCurrent() {
        if (currentIndex >= history.size())
            return null;
        return history.get(currentIndex);
    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {
        int notches = e.getWheelRotation();
        if (notches < 0) {
            setZoom(zoom * 1.1);
        } else {
            setZoom(zoom / 1.1);
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_DELETE -> pop();
            case KeyEvent.VK_LEFT -> {
                int w = currentIndex - 1;
                if (w >= 0)
                    currentIndex = w;
                repaint();
            }
            case KeyEvent.VK_RIGHT -> {
                int w = currentIndex + 1;
                if (w < history.size())
                    currentIndex = w;
                repaint();
            }
            default -> System.out.println(e.getKeyChar());
        }
        fixTitle();
    }

    private void fixTitle() {
        setTitle(getTitle());
    }

    @Override
    public void setTitle(String title) {
        super.setTitle(title + " @ " + (currentIndex + 1) + "/" + history.size() + " # " + Math.round(zoom * 10000) / 10000d);
    }

    @Override
    public String getTitle() {
        String t = super.getTitle();
        if (t.contains(" @ ")) {
            t = t.split(" @ ")[0];
        }
        return t;
    }
}
