package de.duehl.swing.debug;

/*
 * Copyright 2025 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.Image;
import java.awt.Point;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.datetime.DateAndTime;
import de.duehl.basics.datetime.date.ImmutualDate;
import de.duehl.basics.datetime.time.ImmutualTime;
import de.duehl.swing.debug.data.DebugMessage;
import de.duehl.swing.debug.dialog.GuiDebugDialog;

/**
 * Diese Klasse stellt Möglichkeiten zum Eintragen von Debugausgaben in verschiedenen Kategorien
 * bereit sowie die Möglichkeit, sie die gesammelten Meldungen in einer grafischen Oberfläche
 * anschauen zu können.
 *
 * Im Projekt, das diese Klasse und die Debug-Möglichkeiten nutzen will, muss eine eigene Klasse
 * erstellen, welche diese verwendet, und dort statische Methoden anbieten, da man ein solches
 * Objekt nicht überall durch das Projekt weiterreichen möchte.
 *
 * Siehe z.B. de.duehl.mp3.reiko.logic.debug.ReikoDebug
 *
 * @version 1.01     2025-07-30
 * @author Christian Dühl
 */

public class GuiDebug {

    /**
     * Die Kategorie für alle Meldungen.
     *
     * Nur zur Benutzung im Dialog, nicht anderswo!
     */
    public static final String ALL_CATEGORY = "###_ALL_DO_NOT_USE_ELSEWHERE###";


    /** Die Kategorien zu denen Meldungen erfolgen. */
    private final List<String> categories;

    /** Die eingegangenen Meldungen nach Kategorie. */
    private final Map<String, List<DebugMessage>> messagesByCategory;

    /** Konstruktor. */
    public GuiDebug() {
        categories = new ArrayList<>();
        messagesByCategory = new HashMap<>();
    }

    /** Registriert die Kategorien zu denen Meldungen erfolgen. */
    public void registerCategories(List<String> debugCategories) {
        if (!this.categories.isEmpty()) {
            throw new RuntimeException(
                    "Die Kategorien können nur genau einmal registriert werden.");
        }
        if (debugCategories.contains(ALL_CATEGORY)) {
            throw new RuntimeException(
                    "Die Kategorie '" + ALL_CATEGORY + "' darf nicht verwendet werden.");
        }
        if (!CollectionsHelper.isDisjunct(debugCategories)) {
            throw new RuntimeException("Die Kategorien müssen disjunkt sein.");
        }
        this.categories.add(ALL_CATEGORY);
        this.categories.addAll(debugCategories);
        initialiseMessagesByCategory();
    }

    private void initialiseMessagesByCategory() {
        messagesByCategory.clear();
        for (String category : categories) {
            messagesByCategory.put(category, new ArrayList<>());
        }
    }

    /**
     * Trägt eine Debugmeldung ein.
     *
     * @param category
     *            Die Kategorie zu die die Debugmeldung gehört.
     * @param message
     *            Der Text der Debugmeldung.
     */
    public void debug(String category, String message) {
        if (!categories.contains(category)) {
            throw new RuntimeException(
                    "Meldung zu einer nicht registrierten Kategorie: " + category);
        }

        DateAndTime now = new DateAndTime();
        ImmutualDate date = now.getDate();
        ImmutualTime time = now.getTime();

        String className = "dummyClass";
        String methodName = "dummyMethod";
        String lineNumber = "x";

        StackTraceElement[] elements = Thread.currentThread().getStackTrace();
        int correctIndex = 4;
        if (elements.length > correctIndex) {
            StackTraceElement element = elements[correctIndex];
            className = element.getClassName();
            methodName = element.getMethodName();
            lineNumber = Integer.toString(element.getLineNumber());
        }

        DebugMessage debugMessage = new DebugMessage(date, time, category, className, methodName,
                lineNumber, message);
        List<DebugMessage> list = messagesByCategory.get(category);
        list.add(debugMessage);

        List<DebugMessage> allList = messagesByCategory.get(ALL_CATEGORY);
        allList.add(debugMessage);
    }

    /**
     * Zeigt einen Dialog mit den Meldungen an. Aus diesem kann man diese auch speichern.
     *
     * @param parentLocation
     *            Die Position des Rahmens der Oberfläche, vor der dieser Dialog erzeugt wird.
     * @param programImage
     *            Anzuzeigendes ProgrammIcon.
     */
    public void createAndShowDialog(Point parentLocation, Image programImage) {
        GuiDebugDialog dialog = new GuiDebugDialog(categories, messagesByCategory, parentLocation,
                programImage);
        dialog.setVisible(true);
    }

    /** Speichert alle Meldungen in eine chronologische Datei. */
    public void saveAll(String filename) {
        GuiDebugSaver saver = new GuiDebugSaver(categories, messagesByCategory);
        saver.saveAll(filename);
    }

    /** Speichert die Meldungen nach Kategorien in eine Datei mit Blöcken pro Kategorie. */
    public void saveByCategories(String filename) {
        GuiDebugSaver saver = new GuiDebugSaver(categories, messagesByCategory);
        saver.saveByCategories(filename);
    }

}
