package de.duehl.swing.ui.elements.progress;

/*
 * Copyright 2024 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.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import de.duehl.basics.system.SystemTools;
import de.duehl.swing.ui.dialogs.base.ModalDialogBase;

/**
 * Diese Klasse stellt einen Dialog zur Anzeige mehrerer Fortschrittsbalken zusammen mit ein paar
 * Angaben zum jeweiligen Fortschritt dar.
 *
 * Es wird erwartet, dass die eigentlichen Task in eigenen Threads ablaufen, um die Gui nicht
 * einfrieren zu lassen, daher wird hier und im verwendeten StandardColoredProgressPanel mit
 * invokeLater() gearbeitet.
 *
 * Verwendung siehe de.duehl.swing.ui.start.progress.MultipleProgressDialogDemo
 *
 * @version 1.01     2024-11-20
 * @author Christian Dühl
 */

public class MultipleProgressDialog extends ModalDialogBase {

    private static final int DIALOG_WIDTH = 800;

    /** Die Liste mit den Identifiern der Fortschrittsbalken. */
    private final List<String> identifiers;

    /** Der Fortschrittsbalken. */
    private final Map<String, StandardColoredProgressPanel> progressPanelMap;

    /**
     * Konstruktor.
     *
     * @param title
     *            Der Titel des Dialogs.
     * @param parentLocation
     *            Die Position des Rahmens der Oberfläche, vor der dieser Dialog erzeugt wird.
     * @param programImage
     *            Das Icon für das Programm.
     */
    public MultipleProgressDialog(String title, Point parentLocation, Image programImage) {
        super(parentLocation, programImage, title);
        addClosingWindowListener(() -> {});

        identifiers = new ArrayList<>();
        progressPanelMap = new HashMap<>();
    }

    /** Fügt einen Fortschrittbalken hinzu, der dem übergebenen Identifier zugeordnet ist. */
    public MultipleProgressDialog addProgress(String identifier) {
        if (identifiers.contains(identifier) || progressPanelMap.containsKey(identifier)) {
            throw new IllegalArgumentException("Der Indentifier 'identifier' ist bereits bekannt.");
        }

        identifiers.add(identifier);
        StandardColoredProgressPanel progresPanel = new StandardColoredProgressPanel();
        progressPanelMap.put(identifier, progresPanel);

        return this;
    }

    private StandardColoredProgressPanel getProgressPanel(String identifier) {
        if (!progressPanelMap.containsKey(identifier)) {
            throw new IllegalArgumentException("Der Indentifier 'identifier' ist unbekannt.");
        }

        return progressPanelMap.get(identifier);
    }

    /**
     * Setter für den Titel über dem Fortschrittsbalken, beispielsweise "Fortschritt des
     * Importierens:".
     */
    public MultipleProgressDialog setProgressTitle(String identifier, String progressTitle) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.setProgressTitle(progressTitle);
        return this;
    }

    /** Setter für den Text vor dem Zähler, beispielsweise "Anzahl importierter XML-Dateien: ". */
    public MultipleProgressDialog setCountPrefix(String identifier, String countPrefix) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.setCountPrefix(countPrefix);
        return this;
    }

    /** Setter für den Text vor der Stoppuhr, beispielsweise "Laufzeit: ". */
    public MultipleProgressDialog setTimerPrefix(String identifier, String timerPrefix) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.setTimerPrefix(timerPrefix);
        return this;
    }

    /**
     * Setter für den Text in der Beschreibung des aktuellen Schrittes, beispielsweise
     * "füge ein: ".
     */
    public MultipleProgressDialog setActualElementPrefix(String identifier,
            String actualElementPrefix) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.setActualElementPrefix(actualElementPrefix);
        return this;
    }

    /**
     * Setter für den Text in der Beschreibung des aktuellen Schrittes vor Start der Ausführung,
     * beispielsweise "noch nichts in die DB geschrieben".
     */
    public MultipleProgressDialog setActualElementPrefixBeforeStart(String identifier,
            String actualElementPrefixBeforeStart) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.setActualElementPrefixBeforeStart(actualElementPrefixBeforeStart);
        return this;
    }

    /**
     * Setter für den Text in der Beschreibung des aktuellen Schrittes nach Abarbeitung aller
     * Schritte, beispielsweise "Alle Daten wurden in die Datenbank geschrieben."
     */
    public MultipleProgressDialog setActualElementWhenDone(String identifier,
            String actualElementWhenDone) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.setActualElementWhenDone(actualElementWhenDone);
        return this;
    }

    /** Erzeugt den ProgressPanel. Muss als letztes nach den Settern stehen. */
    public MultipleProgressDialog createProgressPanel(String identifier) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.createProgressPanel();
        return this;
    }

    /** Erzeugt die grafische Oberfläche und zeigt den Dialog an. */
    public void createUi() {
        SwingUtilities.invokeLater(() -> createUiInEdt());
    }

    private void createUiInEdt() {
        fillDialog();
        setVisible(true);
    }

    @Override
    protected void populateDialog() {
        add(createDummyLabelForDialogWidth(), BorderLayout.NORTH);
        add(createProgressBarsPart(), BorderLayout.CENTER);
    }

    private Component createDummyLabelForDialogWidth() {
        JLabel label = new JLabel();
        label.setPreferredSize(new Dimension(DIALOG_WIDTH, 0));
        return label;
    }

    private Component createProgressBarsPart() {
        JPanel panel = new JPanel();
        int rows = Math.min(4, identifiers.size());
        panel.setLayout(new GridLayout(rows, 0, 3, 3));

        for (String identifier : identifiers) {
            StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
            panel.add(progressPanel.getComponent());
        }

        return panel;
    }

    /** Initialisiert die Anzahl der auszuführenden Schritte. Erst nach der Erzeugung aufrufen! */
    public void initNumberOfTasksToDo(String identifier, int numberOfTasksToDo) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.initNumberOfTasksToDo(numberOfTasksToDo); // nutzt invokeLater
    }

    /** Informiert darüber, dass mit der Arbeit als ganzer begonnen wird. */
    public void startingWithTask(String identifier) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.startingWithTask(); // nutzt invokeLater
    }

    /** Informiert darüber, dass ein Task nun ausgeführt wird. */
    public void aboutToExceuteOneTaskSoon(String identifier, String description) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.aboutToExceuteOneTaskSoon(description); // nutzt invokeLater
    }

    /** Informiert darüber, dass ein Task ausgeführt wurde. */
    public void oneTaskDone(String identifier, String description) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.oneTaskDone(description); // nutzt invokeLater
    }

    /** Hält die Stoppuhr an, falls die noch läuft. */
    public void quit(String identifier) {
        StandardColoredProgressPanel progressPanel = getProgressPanel(identifier);
        progressPanel.quit(); // nutzt invokeLater
    }

    /** Beendet den Dialog, wenn der zuletzt hinzugefügte Identifier beendet wird. */
    public void closeUi(String identifier) {
        if (identifier.equals(identifiers.get(identifiers.size() - 1))) {
            SystemTools.sleep(250); // Damit man das Grün am Ende noch kurz sieht.
            SwingUtilities.invokeLater(() -> closeUiInEdt());
        }
    }

    private void closeUiInEdt() {
        closeDialog();
    }

}
