package de.duehl.twosidecommander.ui.list.element;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.move.SwitchableVerticalMoveButtons;
import de.duehl.swing.ui.move.data.VerticalMoveButtonsUser;
import de.duehl.twosidecommander.ui.list.data.ListElementDisplayerClickReactor;
import de.duehl.twosidecommander.ui.list.data.ListElementMoveReactor;

/**
 * Diese Klasse stellt die abstrakte Basis für die grafische Oberfläche eines Elements einer Liste
 * des Listen-Commanders dar, mit dem sich generisch zwischen zwei Listen von Dingen Listenelemente
 * kopieren, verschieben oder löschen lassen.
 *
 * @version 1.01     2025-07-08
 * @author Christian Dühl
 */

public abstract class ListElementDisplayer implements VerticalMoveButtonsUser {

    /** Reagiert auf die Buttons zum vertikalen Verschieben des Listenelements. */
    private final ListElementMoveReactor listElementMoveReactor;

    /** Der Panel auf dem das Element dargestellt wird. */
    private JPanel panel;

    /**
     * Gibt an, ob im Rechtsklick-Menü die Möglichkeit zur Anzeige von Details des Listenelements
     * angeboten werden sollen.
     */
    private boolean showDetailsInRightClickMenu;

    /** Der Name des Menüpunkts zum Anzeigen von Details des Listenelements. */
    private String showDetailsName;

    /** Das Objekt, das auf den unmodifizierten Linksklick auf ein Element reagiert. */
    private ListElementDisplayerClickReactor listElementDisplayerClickReactor;

    /** Das Objekt, das auf den Linksklick mit gedrückter Shift-Taste auf ein Element reagiert. */
    private ListElementDisplayerClickReactor listElementDisplayerShiftClickReactor;

    /** Der umschaltbare Panel mit Buttons zum Verschieben nach oben und unten. */
    private final SwitchableVerticalMoveButtons switchableMoveButtons;

    /**
     * Konstruktor.
     *
     * @param listElementMoveReactor
     *            Reagiert auf die Buttons zum vertikalen Verschieben des Listenelements.
     */
    public ListElementDisplayer(ListElementMoveReactor listElementMoveReactor) {
        this.listElementMoveReactor = listElementMoveReactor;

        panel = new JPanel();

        showDetailsInRightClickMenu = false;
        showDetailsName = "";
        switchableMoveButtons = new SwitchableVerticalMoveButtons((VerticalMoveButtonsUser) this);

        init();
    }

    private void init() {
        initPanel();
        addMouseListener();
    }

    private void initPanel() {
        panel.setLayout(new BorderLayout());

        //GuiTools.ignoreTabulatorInComponent(panel);
        // Der verhindert hoch / runter / home /end

        panel.setBorder(BorderFactory.createEmptyBorder(2, 3, 2, 3)); // top left bottom right
    }

    private void addMouseListener() {
        panel.addMouseListener(createMouseAdapter());
    }

    private MouseAdapter createMouseAdapter() {
        return new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent mouseEvent) {
                int clickCount = mouseEvent.getClickCount();

                boolean cltrDown = false;
                boolean altDown = false;
                boolean shiftDown = false;
                if (mouseEvent.isControlDown()) {
                    cltrDown = true;
                }
                if (mouseEvent.isAltDown()) {
                    altDown = true;
                }
                if (mouseEvent.isShiftDown()) {
                    shiftDown = true;
                }

                boolean leftMouseClick = false;
                boolean middleMouseClick = false;
                boolean rightMouseClick = false;
                if (SwingUtilities.isLeftMouseButton(mouseEvent)) {
                    leftMouseClick = true;
                }
                if (SwingUtilities.isMiddleMouseButton(mouseEvent)) {
                    middleMouseClick = true;
                }
                if (SwingUtilities.isRightMouseButton(mouseEvent)) {
                    rightMouseClick = true;
                }

                if (clickCount == 1
                        && leftMouseClick && !middleMouseClick && !rightMouseClick) {
                    if (!cltrDown && !altDown && !shiftDown) {
                        reactOnSingleLeftMouseClick();
                    }
                    else if (!cltrDown && !altDown && shiftDown) {
                        reactOnSingleShiftLeftMouseClick();
                    }
                }
            }
        };
    }

    private void reactOnSingleLeftMouseClick() {
        listElementDisplayerClickReactor.clickedOnListElement(this);
    }

    private void reactOnSingleShiftLeftMouseClick() {
        listElementDisplayerShiftClickReactor.clickedOnListElement(this);
    }

    /** Setter für das Objekt, das auf den unmodifizierten Linksklick auf ein Element reagiert. */
    public final void setListElementDisplayerClickReactor(
            ListElementDisplayerClickReactor listElementDisplayerClickReactor) {
        this.listElementDisplayerClickReactor = listElementDisplayerClickReactor;
    }

    /**
     * Setter für das Objekt, das auf den Linksklick mit gedrückter Shift-Taste auf ein Element
     * reagiert.
     */
    public final void setListElementDisplayerShiftClickReactor(
            ListElementDisplayerClickReactor listElementDisplayerShiftClickReactor) {
        this.listElementDisplayerShiftClickReactor = listElementDisplayerShiftClickReactor;
    }

    /**
     * Setzt den Namen für den Menüpunkt im Rechte-Maustaste-Menü zur Anzeige der Details des
     * Listenelements und legt damit fest, dass diese angezeigt werden können.
     */
    public final void setListElementShowDetailsName(String showDetailsName) {
        // z.B. "Details der Vokabel anzeigen ..."
        this.showDetailsName = showDetailsName;
        showDetailsInRightClickMenu = true;
    }

    /** Erstellt die Oberfläche des Elements. */
    protected final void createGui() {
        if (showDetailsInRightClickMenu) {
            showDetailsInRightClickMenu();
        }
        setUpAndDownButtonColorsAndEnabled();

        panel.add(createElementDisplay(), BorderLayout.CENTER);
        panel.add(GuiTools.centerVertical(switchableMoveButtons.getPanel()), BorderLayout.EAST);
    }

    private void showDetailsInRightClickMenu() {
        ListElementPopupMenu popupMenu = new ListElementPopupMenu(showDetailsName,
                () -> reactOnShowDetailsClickInRightClickMenu());
        panel.setComponentPopupMenu(popupMenu);
    }

    /** Im Rechtsklick-Menü des Listenelements wurde die Ansicht der Details ausgewählt. */
    protected void reactOnShowDetailsClickInRightClickMenu() {
        // Diese Methode muss überschrieben werden, wenn man ein Popup-Menü verwendet.
    }

    /** Erstellt die Ui-Komponente mit der Ansicht des Elements in der Liste. */
    protected abstract Component createElementDisplay();

    /** Getter für den Panel auf dem das Element dargestellt wird. */
    public final JPanel getPanel() {
        return panel;
    }

    /** Stellt das Element ausgewählt und hervorgehoben dar. */
    public abstract void showSelectedAndHighlighted();

    /** Stellt das Element ausgewählt dar. */
    public abstract void showSelected();

    /** Stellt das Element hervorgehoben dar. */
    public abstract void showHighlighted();

    /** Stellt das Element normal dar. */
    public abstract void showNormal();

    /** Gibt eine Beschreibung des Elements zur Anzeige in Dialogen zurück. */
    public abstract String getElementDescription();

    /** Zeigt die Buttons zum Verschieben an oder blendet wie aus. */
    public final void showMoveButtonsOnListElements(boolean showMoveButtonsOnListElements) {
        switchableMoveButtons.showMoveButtons(showMoveButtonsOnListElements);
        panel.repaint();
    }

    /** Gibt an, ob die Bar eines Vokabulars nach oben bewegt werden kann. */
    @Override
    public final boolean canMoveButtonsUserMoveUp() {
        return listElementMoveReactor.canListElementMoveUp(this);
    }

    /** Gibt an, ob die Bar eines Vokabulars nach unten bewegt werden kann. */
    @Override
    public final boolean canMoveButtonsUserMoveDown() {
        return listElementMoveReactor.canListElementMoveDown(this);
    }

    /** Verschiebt die Bar eines Vokabulars an die erste Stelle. */
    @Override
    public final void moveMoveButtonsUserToFirst() {
        listElementMoveReactor.moveListElementToFirst(this);
    }

    /** Verschiebt die Bar eines Vokabulars nach oben. */
    @Override
    public final void moveMoveButtonsUserUp() {
        listElementMoveReactor.moveListElementUp(this);
    }

    /** Verschiebt die Bar eines Vokabulars  nach unten. */
    @Override
    public final void moveMoveButtonsUserDown() {
        listElementMoveReactor.moveListElementDown(this);
    }

    /** Verschiebt die Bar eines Vokabulars an die letzte Stelle. */
    @Override
    public final void moveMoveButtonsUserToLast() {
        listElementMoveReactor.moveListElementToLast(this);
    }

    /**
     * Setzt die Farben und Darstellung der Buttons abhängig davon, ob sie verschoben werden
     * können.
     */
    public final void setUpAndDownButtonColorsAndEnabled() {
        switchableMoveButtons.setUpAndDownButtonColorsAndEnabled();
    }

    /** Gibt an, ob die Buttons zum Verschieben angezeigt werden. */
    public final boolean showsMoveButtons() {
        return switchableMoveButtons.isMoveButtonsShown();
    }

}
