package de.duehl.swing.ui.filter.project.gateway;

/*
 * 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.util.ArrayList;

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

import de.duehl.swing.ui.filter.exceptions.FilterException;
import de.duehl.swing.ui.filter.method.combination.CombinationElementList;
import de.duehl.swing.ui.filter.project.gateway.data.DescriptionAndFilterType;

/**
 * Diese abstrakt Klasse ist die Basis für eine Klasse, welche eindeutige Beschreibungen der
 * Methoden zum Filtern liefert.
 *
 * Diese müssen eindeutig sein, denn später wird andersherum wieder auf die wirklichen Methoden
 * geschlossen.
 *
 * @version 1.01     2025-05-07
 * @author Christian Dühl
 */

public abstract class FilterGateway<Data, Type> implements DescriptionToTypeTranslater<Type> {

    /** Liste mit eindeutigen Beschreibungen der parameterlosen Methoden zum Filtern. */
    private final List<String> methods;

    /** Liste mit eindeutigen Beschreibungen der parametrisierten Methoden zum Filtern. */
    private final List<String> paramisedMethods;

    /** Verzeichnis der Typen nach den eindeutigen Beschreibungen. */
    private final Map<String, Type> descriptionToType;

//    /**
//     * Konstruktor.
//     *
//     * @param methods
//     *            Liste mit eindeutigen Beschreibungen der parameterlosen Methoden zum Filtern.
//     * @param methodTypes
//     *            Liste mit Typen der parameterlosen Methoden zum Filtern.
//     * @param paramisedMethods
//     *            Liste mit eindeutigen Beschreibungen der parametrisierten Methoden zum Filtern.
//     * @param paramisesMethodTypes
//     *            Liste mit Typen der parametrisierten Methoden zum Filtern.
//     */
//    @Deprecated
//    protected FilterGateway(List<String> methods, List<Type> methodTypes,
//            List<String> paramisedMethods, List<Type> paramisesMethodTypes) {
//        this.methods = methods;
//        this.paramisedMethods = paramisedMethods;
//
//        checkMethodsAndParamisedMethods();
//        checkSizes(methods, methodTypes);
//        checkSizes(paramisedMethods, paramisesMethodTypes);
//
//        descriptionToType = new HashMap<>();
//        for (int index = 0; index < methods.size(); ++index) {
//            String description = methods.get(index);
//            Type type = methodTypes.get(index);
//            descriptionToType.put(description, type);
//        }
//        for (int index = 0; index < paramisedMethods.size(); ++index) {
//            String description = paramisedMethods.get(index);
//            Type type = paramisesMethodTypes.get(index);
//            descriptionToType.put(description, type);
//        }
//    }

    /**
     * Konstruktor.
     *
     * @param methods
     *            Liste mit eindeutigen Beschreibungen und Typen der parameterlosen Methoden zum
     *            Filtern.
     * @param paramisedMethods
     *            Liste mit eindeutigen Beschreibungen und Typen der parametrisierten Methoden zum
     *            Filtern.
     */
    protected FilterGateway(List<DescriptionAndFilterType<Type>> methods,
            List<DescriptionAndFilterType<Type>> paramisedMethods) {
        this.methods = new ArrayList<>();
        this.paramisedMethods = new ArrayList<>();

        for (DescriptionAndFilterType<Type> method : methods) {
            String description = method.getDescription();
            this.methods.add(description);
        }
        for (DescriptionAndFilterType<Type> method : paramisedMethods) {
            String description = method.getDescription();
            this.paramisedMethods.add(description);
        }

        checkMethodsAndParamisedMethods();

        descriptionToType = new HashMap<>();
        for (DescriptionAndFilterType<Type> method : methods) {
            String description = method.getDescription();
            Type type = method.getFilterType();
            descriptionToType.put(description, type);
        }
        for (DescriptionAndFilterType<Type> method : paramisedMethods) {
            String description = method.getDescription();
            Type type = method.getFilterType();
            descriptionToType.put(description, type);
        }
    }

    private void checkMethodsAndParamisedMethods() {
        checkNotSelfContained(methods);
        checkNotSelfContained(paramisedMethods);
        checkNotContainedByOther(methods, paramisedMethods);
        checkNotContainedByOther(paramisedMethods, methods);
    }

    private static void checkNotSelfContained(List<String> list) {
        for (int firstIndex = 0; firstIndex < list.size() - 1; ++firstIndex) {
            String firstElement = list.get(firstIndex);
            for (int secondIndex = firstIndex + 1; secondIndex < list.size(); ++secondIndex) {
                String secondElement = list.get(secondIndex);
                if (firstElement.equals(secondElement)) {
                    throw new FilterException("Nicht eindeutig: In der Liste der Methoden oder "
                            + "der parametrisierten Methoden kommt die Beschreibung '"
                            + firstElement + "' zweimal vor, nämlich an Index " + firstIndex
                            + " und an Index " + secondIndex + "!");
                }
            }
        }
    }

    private void checkNotContainedByOther(List<String> list1, List<String> list2) {
        for (String element : list1) {
            if (list2.contains(element)) {
                throw new RuntimeException("Nicht eindeutig: In der Liste der Methoden oder "
                        + "der parametrisierten Methoden kommt die Beschreibung '"
                        + element + "' sowohl in der Liste mit den parameterlöosen als auch in der "
                        + "Liste mit den parametrisierbaren Methoden vor!");
            }
        }
    }

//    private void checkSizes(List<String> descriptions, List<Type> types) {
//        if (descriptions.size() != types.size()) {
//            throw new FilterException("Die Anzahlen an Beschreibungen und Typen der Filter sind "
//                    + "verschieden!\n"
//                    + "\t" + "Anzahl Beschreibungen: " + descriptions.size() + "\n"
//                    + "\t" + "Anzahl Typen         : " + types.size() + "\n");
//        }
//    }

    /** Gibt an, ob es parameterlosen Methoden zum Filtern gibt. */
    public final boolean hasMethods() {
        return !methods.isEmpty();
    }

    /**
     * Gibt eine Liste mit eindeutigen Beschreibungen der parameterlosen Methoden zum Filtern
     * zurück.
     */
    public final List<String> getMethods() {
        return methods;
    }

    /** Gibt an, ob es parametrisierte Methoden zum Filtern gibt. */
    public final boolean hasParamisedMethods() {
        return !paramisedMethods.isEmpty();
    }

    /**
     * Gibt eine Liste mit eindeutigen Beschreibungen der parametrisierten Methoden zum Filtern
     * zurück.
     */
    public final List<String> getParamisedMethods() {
        return paramisedMethods;
    }

    /** Liefert zu einer eindeutigen Beschreibung den passenden Typen. */
    @Override
    public Type getTypeForDescription(String description) {
        if (!descriptionToType.containsKey(description)) {
            throw new FilterException(
                    "Zur Beschreibung '" + description + "' ist kein FilterTyp bekannt!");
        }

        return descriptionToType.get(description);
    }

    /** Gibt an, ob im übergeordneten Projekt ein kombinierter Filter angezeigt wird. */
    public abstract boolean isFilterCombinedMethods();

    /** Setzt im Übergeordneten Projekt den realen Filter. */
    public abstract void setRealFilter(CombinationElementList<Data> elements);

}
