package de.duehl.swing.ui.dialogs.lists.ui;

/*
 * Copyright 2021 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.buttons.painted.DownButton;
import de.duehl.swing.ui.buttons.painted.EditButton;
import de.duehl.swing.ui.buttons.painted.PaintedButton;
import de.duehl.swing.ui.buttons.painted.UpButton;
import de.duehl.swing.ui.buttons.painted.XButton;
import de.duehl.swing.ui.colors.ColorTool;
import de.duehl.swing.ui.colors.NamedColorListFabric;
import de.duehl.swing.ui.dialogs.lists.logic.LogicalEditableList;
import de.duehl.swing.ui.dialogs.lists.logic.LogicalEditableListElement;

/**
 * Diese Klasse steht für die Basis eines Elements mit graphischer Repräsentation, welches in einem
 * EditableListDialog angezeigt, bearbeitet, gelöscht, umsortiert oder neu hinzugefügt werden kann.
 *
 * @version 1.02     2021-11-17
 * @author Christian Dühl
 */

public abstract class EditableListElement<LogicalElement extends LogicalEditableListElement> {

    public static final String TITLE_COLOR = NamedColorListFabric.DARKBLUE;
    private static final String DELETE_BUTTON_COLOR = NamedColorListFabric.RED;
    private static final String MOVE_BUTTON_COLOR = NamedColorListFabric.DARKGREEN;
    private static final String DISABLED_COLOR = NamedColorListFabric.DARKGRAY;

    /** Das logische Element, das hier dargestellt wird. */
    private final LogicalElement logicalElement;

    /** Die Liste mit den logischen Elementen. */
    private final LogicalEditableList<LogicalElement> logicalList;

    /** Objekt, das das Element aus der Anzeige entfernen kann. */
    private final UiElementListModifier<LogicalElement> uiListElementRemover;

    /**
     * Konstruktor.
     *
     * @param logicalElement
     *            Das logische Element, das hier dargestellt wird.
     * @param logicalList
     *            Die Liste mit den logischen Elementen.
     * @param uiListElementRemover
     *            Objekt, das das Element aus der Anzeige entfernen kann.
     */
    public EditableListElement(LogicalElement logicalElement,
            LogicalEditableList<LogicalElement> logicalList,
            UiElementListModifier<LogicalElement> uiListElementRemover) {
        this.logicalElement = logicalElement;
        this.logicalList = logicalList;
        this.uiListElementRemover = uiListElementRemover;
    }

    public final Component createPanel() {
        JPanel panel = new JPanel();

        panel.setBorder(new EmptyBorder(2, 0, 1, 0));
        panel.setLayout(new BorderLayout());

        panel.add(createLeftElementLineButtonsPart(), BorderLayout.WEST);
        panel.add(createElementComponent(), BorderLayout.CENTER);
        panel.add(createRightElementLineButtonsPart(), BorderLayout.EAST);

        return panel;
    }

    private JPanel createLeftElementLineButtonsPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout());

        panel.add(createMoveUpButton());
        panel.add(createMoveDownButton());

        return panel;
    }

    private Component createMoveUpButton() {
        boolean enabled = logicalList.canMoveUp(logicalElement);
        Color buttonColor = enabled
                ? ColorTool.getColorByName(MOVE_BUTTON_COLOR)
                : ColorTool.getColorByName(DISABLED_COLOR);
        PaintedButton button = new UpButton(buttonColor);
        button.forceSquare();
        button.addActionListener(e -> moveUp());
        button.setEnabled(enabled);
        return button;
    }

    private void moveUp() {
        logicalList.moveUp(logicalElement);
        updateElementsPanel();
    }

    private Component createMoveDownButton() {
        boolean enabled = logicalList.canMoveDown(logicalElement);
        Color buttonColor = enabled
                ? ColorTool.getColorByName(MOVE_BUTTON_COLOR)
                : ColorTool.getColorByName(DISABLED_COLOR);
        PaintedButton button = new DownButton(buttonColor);
        button.forceSquare();
        button.addActionListener(e -> moveDown());
        button.setEnabled(enabled);
        return button;
    }

    private void moveDown() {
        logicalList.moveDown(logicalElement);
        updateElementsPanel();
    }

    /** Erzeugt die grafische Darstellung des logischen Elements ohne die Buttons drumherum. */
    protected abstract Component createElementComponent();

    private Component createRightElementLineButtonsPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout());

        panel.add(createDeleteButton());
        panel.add(createEditButton());

        return panel;
    }

    private Component createDeleteButton() {
        PaintedButton button = new XButton(ColorTool.getColorByName(DELETE_BUTTON_COLOR));
        button.forceSquare();
        button.addActionListener(e -> deleteElement());
        return button;
    }

    private void deleteElement() {
        uiListElementRemover.removeElementFromList(getLogicalElement());
    }

    private Component createEditButton() {
        JButton button = new EditButton();
        button.addActionListener(e -> editElement());
        return button;
    }

    /** Bearbeitet das hinterlegte, logische Element in einem passenden Dialog. */
    public final void editElement() {
        editElement(uiListElementRemover.getComponent());
        if (logicalList.containedMoreThanOnce(getLogicalElement())) {
            uiListElementRemover.removeElementFromList(getLogicalElement());
            GuiTools.informUser(uiListElementRemover.getComponent(), "Dublette erzeugt!",
                    "Das Element gab es in nun doppelt in der Liste, daher wurde es entfernt!");
        }
    }

    /**
     * Bearbeitet das hinterlegte, logische Element in einem passenden Dialog.
     *
     * @param parentComponent
     *            Komponente, vor der die Bearbeitung angezeigt werden soll.
     */
    protected abstract void editElement(Component parentComponent);

    /** Getter für das logische Element, das hier dargestellt wird. */
    public final LogicalElement getLogicalElement() {
        return logicalElement;
    }

    protected final void updateElementsPanel() {
        uiListElementRemover.updateElementsPanel();
    }

    /** Erzeugt einen Button zur Auswahl des Elements im Select-Dialog. */
    public abstract JButton createSelectButton();

}
