package de.duehl.swing.ui.tabs.bars.generic;

/*
 * 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 javax.swing.JTabbedPane;
import javax.swing.event.ChangeListener;

import java.awt.Component;
import java.util.ArrayList;
import java.util.List;

import de.duehl.swing.ui.tabs.Tabulator;
import de.duehl.swing.ui.tabs.close.CloseButtonReaktor;
import de.duehl.swing.ui.tabs.elements.TabElements;

/**
 * Diese Klasse stellt ein Reiterelement dar, welches eine Reihe von festen, benannten Komponenten
 * aufnimmt.
 *
 * Dabei wird in jeden Reiter oben ein eigenes Tabulator-Objekt eingefügt, das anzeigen kann,
 * ob der Inhalt des Reiters geändert wurde oder unverändert ist.
 *
 * @param <T>
 *            Die Klasse T steht für den Typ der Inhalte in den Reitern.
 *
 * @version 1.01     2021-11-30
 * @author Christian Dühl
 */

public class TabBar<T extends Component> {

    /** Gibt an, ob der Button zum Löschen angezeigt werden soll. */
    private final boolean showCloseButton;

    /** Die Reiterverwaltung. */
    private final JTabbedPane tabs;

    /** Liste der Elemente zu den angezeigten Reitern. */
    private final List<TabElements<T>> tabElementsList;

    /** Konstruktor für Reiter mit Schalter zum Schließen. */
    public TabBar() {
        this(true);
    }

    /**
     * Konstruktor.
     *
     * @param tabs
     *            Die Reiterverwaltung.
     */
    protected TabBar(JTabbedPane tabs) {
        this(true, tabs);
    }

    /**
     * Konstruktor.
     *
     * @param showCloseButton
     *            Gibt an, ob der Button zum Löschen angezeigt werden soll.
     */
    public TabBar(boolean showCloseButton) {
        this(showCloseButton, createTabs());
    }

    /**
     * Konstruktor.
     *
     * @param showCloseButton
     *            Gibt an, ob der Button zum Löschen angezeigt werden soll.
     * @param tabs
     *            Die Reiterverwaltung.
     */
    protected TabBar(boolean showCloseButton, JTabbedPane tabs) {
        this.showCloseButton = showCloseButton;
        this.tabs = tabs;
        tabElementsList = new ArrayList<>();
    }

    /** Erzeugt eine Standard-Reiterverwaltung. */
    private static JTabbedPane createTabs() {
        return new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
    }

    /**
     * Gibt den Index des aktuell angezeigten Reiters zurück.
     *
     * Gibt -1 zurück, wenn es keinen aktuell ausgewählten Reiter gibt.
     */
    protected final int getSelectedIndex() {
        return tabs.getSelectedIndex();
    }

    /** Gibt die Anzahl der Reiter zurück. */
    protected final int getTabCount() {
        return tabs.getTabCount();
    }

    /**
     * Fügt einen Tabulator hinzu.
     *
     * @param title
     *            Titel des Reiters.
     * @param tabContents
     *            Der Inhalt des Reiters.
     * @return Die erzeugten Elemente des Tabs.
     */
    public TabElements<T> addTab(String title, T tabContents) {
        TabElements<T> tabElements = createNewTabElements(title, tabContents);
        addTab(tabElements);
        return tabElements;
    }

    private TabElements<T> createNewTabElements(String title, T tabContents) {
        Tabulator tabulator = createTabulator(title);
        return new TabElements<T>(title, tabulator, tabContents);
    }

    private Tabulator createTabulator(String title) {
        String identifier = "Title: " + title;
        if (showCloseButton) {
            return new Tabulator(identifier, title, createCloseReaktor(title), showCloseButton);
        }
        else {
            return new Tabulator(identifier, title, showCloseButton);
        }
    }

    private CloseButtonReaktor createCloseReaktor(String title) {
        return new CloseButtonReaktor() {
            @Override
            public void closeButtonPressed(String tabUserOrGroupIdentifier) {
                removeTab(title);
            }
        };
    }

    /**
     * Fügt einen Tabulator hinzu.
     *
     * @param tabElements
     *            Elemente des hinzuzufügenden Tabs..
     */
    public final void addTab(TabElements<T> tabElements) {
        checkForAlreadyUsedTitles(tabElements.getTitle());
        tabElementsList.add(tabElements);

        populateOneTab(tabElements);
    }

    private void checkForAlreadyUsedTitles(String title) {
        if (isTitleAlreadyInUse(title)) {
            throw new IllegalArgumentException(
                    "Der Titel '" + title + "' wird bereites verwendet!");
        }
    }

    /** Gibt an, ob der übergebene Title bereits für einen Reite verwendet wird. */
    public final boolean isTitleAlreadyInUse(String title) {
        for (TabElements<T> tabElements : tabElementsList) {
            String tabTitle = tabElements.getTitle();
            if (title.equals(tabTitle)) {
                return true;
            }
        }
        return false;
    }

    private void populateOneTab(TabElements<T> tabElements) {
        String title = tabElements.getTitle();
        tabs.add(title, tabElements.getShownComponent());
        int panelIndex = tabs.indexOfTab(title);

        Tabulator tabulator = tabElements.getTabulator();
        tabs.setTabComponentAt(panelIndex, tabulator);
    }

    /** Entfernt den Reiter mit dem angegebenen Namen. */
    public void removeTab(String title) {
        for (TabElements<T> tabElements : tabElementsList) {
            String tabTitle = tabElements.getTitle();
            if (title.equals(tabTitle)) {
                tabElementsList.remove(tabElements);
                tabs.remove(tabElements.getShownComponent());
                return;
            }
        }
    }

    /** Entfernt alle Reiter. */
    public void removeAllTabs() {
        tabs.removeAll();
        tabElementsList.clear();
    }

    /** Getter für die Komponente zum Einfügen in die grafische Oberfläche. */
    public final Component getComponent() {
        return tabs;
    }

    /** Validiert die Reiter. */
    public final void validate() {
        tabs.validate();
    }

    /** Liste der Elemente zu den angezeigten Reitern. */
    public final List<TabElements<T>> getTabElementsList() {
        return tabElementsList;
    }

    /** Gibt die Tab-Elemente zu dem Reiter mit dem angegebene Namen zurück. */
    private TabElements<T> getTabElementsListByTitle(String title) {
        for (TabElements<T> tabElements : tabElementsList) {
            String tabTitle = tabElements.getTitle();
            if (title.equals(tabTitle)) {
                return tabElements;
            }
        }

        throw new IllegalArgumentException(
                "Zum Titel '" + title + "' wurden keine Tabulator-Elemente gefunden!");
    }

    /** Gibt den Tabulator zu dem Reiter mit dem angegebene Namen zurück. */
    public final Tabulator getTabulatorByTitle(String title) {
        TabElements<T> tabElements = getTabElementsListByTitle(title);
        return tabElements.getTabulator();
    }

    /** Fügt einen ChangeListener hinzu. */
    public final void addChangeListener(ChangeListener listener) {
        tabs.addChangeListener(listener);
    }

    /**
     * Gibt die Tab-Elemente des aktuell angezeigten Reiters zurück.
     * Null falls nicht ermittelbar.
     */
    public TabElements<T> getSelectedTabElements() {
        int index = tabs.getSelectedIndex();
        if (-1 != index) {
            TabElements<T> tabElements = tabElementsList.get(index);
            return tabElements;
        }

        return null;
    }

    /** Zeigt die gewünschten Elemente an. */
    public final void select(TabElements<Component> tabElement) {
        tabs.setSelectedComponent(tabElement.getShownComponent());
    }

    /** Zeigt die gewünschten Elemente an. */
    public final void select(String title) {
        TabElements<T> tabElement = getTabElementsListByTitle(title);
        tabs.setSelectedComponent(tabElement.getShownComponent());
    }

    /** Erzeugt aus dem gegebenen Titel einen noch nicht vergebenen Titel. */
    public String generateNotUsedTitle(String title) {
        String notUsedTitle = title;
        while (isTitleAlreadyInUse(notUsedTitle)) {
            notUsedTitle = notUsedTitle + "_";
        }
        return notUsedTitle;
    }

}
