package de.duehl.swing.text.calculations;

/*
 * 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
 */

/**
 * Diese Klasse berechnet nach bestimmten Vorgaben den prozentual passenden Wert zwischen
 * übergebenen Grenzen ("links" das Minimum, "rechts" das Maximum).
 *
 * Man möchte also zu einem Punkt innerhalb einer bestimmten Strecke einen anderen Punkt innerhalb
 * einer anderen Strecke so berechnen, dass beide Punkte an der prozentual gleichen Stelle auf
 * ihrer Strecke liegen.
 *
 * Anders gesagt geht es hier darum, aus einem gegebenen Tripel von Vergleichs-Minimum,
 * Vergleichs-Wert und Vergleichs-Maximum zu vorgegebener Wunsch-Minimum und Wunsch-Maximum den
 * Wunsch-Wert analog zu berechnen.
 *
 *
 * Grafische Darstellung der Größen:
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *             wanted                 wanted
 *             Minimum                Maximum
 *                 |                     |
 *             compare                compare
 *    0        Minimum                Maximum
 *    |            |                     |
 *    |------------|-------X-------------|
 *                         |
 *                      compare
 *                       Actual
 *
 *                 |-------|
 *                     a
 *                         |-------------|
 *                                 b
 *                 |---------------------|
 *                            c
 *
 * Berechnung des Prozent-Wertes von a an c:
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Prozent       a
 * -------  =   ---
 *   100         c
 *
 * => Prozent = 100 * a / c
 *
 *
 * a = compareActual - compareMinimum
 * c = compareMaximum - compareMinimum
 *
 * Also ist
 *
 * Prozent = 100 * (compareActual - compareMinimum) / (compareMaximum - compareMinimum)
 *
 *
 * Berechnung wantedActual:
 * ~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Um den Wert von wantedActual zu berechnen, also die Strecke von 0 bis wantedActual,
 * muss man zu wantedMinimum den prozentualen Anteil der Strecke von wantedMinimum bis
 * wantedMaximum addieren:
 *
 *  wantedActual - wantedMinimum     Prozent
 * ------------------------------- = -------
 *  wantedMaximum - wantedMinimum     100
 *
 * => wantedActual - wantedMinimum = (wantedMaximum - wantedMinimum) * Prozent / 100
 *
 * => wantedActual = wantedMinimum + (wantedMaximum - wantedMinimum) * Prozent / 100
 *
 *
 * @version 1.01     2025-07-22
 * @author Christian Dühl
 */

public class MinToMaxSizeCalculator {

    /** Legt fest, ob Debug-Ausgaben erfolgen sollen. */
    private static final boolean DEBUG = false;


    /** Der minimal erlaubte Wert des Vergleichswertes. */
    private final double compareMinimum;

    /** Der maximal erlaubte Wert des Vergleichswertes. */
    private final double compareMaximum;

    /** Der aktuelle Wert des Vergleichswertes. */
    private final double compareActual;

    /** Der minimal erlaubte Wert des Wunschswertes. */
    private final double wantedMaximum;

    /** Der maximal erlaubte Wert des Wunschswertes. */
    private final double wantedMinimum;

    /** Der hier berechnete aktuelle Wert des Wunschswertes. */
    private double wantedActual;

    /**
     * Der Prozentsatz vom aktuellen Vergleichswert an der Strecke vom Vergleichsminimum bis zum
     * Vergleichsmaximum.
     */
    private double percent;

    /**
     * Gibt an, ob der gewünschte Punkt nicht vom Minimum aus gesehen an der gleichen prozentualen
     * Stelle landen soll, sondern vom Maximum aus gesehen.
     */
    private boolean calculatePercentFromMaximumInsteadOfMinimum;

    /**
     * Konstruktor.
     *
     * @param compareMinimum
     *            Der minimal erlaubte Wert des Vergleichswertes.
     * @param compareMaximum
     *            Der maximal erlaubte Wert des Vergleichswertes.
     * @param compareActual
     *            Der aktuelle Wert des Vergleichswertes.
     * @param wantedMinimum
     *            Der minimal erlaubte Wert des Wunschswertes.
     * @param wantedMaximum
     *            Der maximal erlaubte Wert des Wunschswertes.
     */
    public MinToMaxSizeCalculator(int compareMinimum, int compareMaximum, int compareActual,
            int wantedMinimum, int wantedMaximum) {
        this.compareMinimum = (double) compareMinimum;
        this.compareMaximum = (double) compareMaximum;
        this.compareActual = (double) compareActual;
        this.wantedMaximum = (double) wantedMaximum;
        this.wantedMinimum = (double) wantedMinimum;

        calculatePercentFromMaximumInsteadOfMinimum = false;
    }

    /**
     * Konstruktor.
     *
     * @param compareMinimum
     *            Der minimal erlaubte Wert des Vergleichswertes.
     * @param compareMaximum
     *            Der maximal erlaubte Wert des Vergleichswertes.
     * @param compareActual
     *            Der aktuelle Wert des Vergleichswertes.
     * @param wantedMinimum
     *            Der minimal erlaubte Wert des Wunschswertes.
     * @param wantedMaximum
     *            Der maximal erlaubte Wert des Wunschswertes.
     */
    public MinToMaxSizeCalculator(double compareMinimum, double compareMaximum, double compareActual,
            double wantedMinimum, double wantedMaximum) {
        this.compareMinimum = compareMinimum;
        this.compareMaximum = compareMaximum;
        this.compareActual = compareActual;
        this.wantedMaximum = wantedMaximum;
        this.wantedMinimum = wantedMinimum;

        calculatePercentFromMaximumInsteadOfMinimum = false;

    }

    /**
     * Legt fest, dass der gewünschte Punkt nicht vom Minimum aus gesehen an der gleichen
     * prozentualen Stelle landen soll, sondern vom Maximum aus gesehen.
     */
    public void calculatePercentFromMaximumInsteadOfMinimum() {
        say("MinToMaxSizeCalculator.calculatePercentFromMaximumInsteadOfMinimum()");
        calculatePercentFromMaximumInsteadOfMinimum = true;
    }

    /** Führt die Berechnung durch. */
    public void calculate() {
        checkLegalValues();
        calculatePercent();
        calculateWantedActual();
    }

    /** Überprüft ob die gegebenen Werte den Voraussetzungen genügen. */
    private void checkLegalValues() {
        if (compareMaximum < compareMinimum) {
            throw new RuntimeException("compareMaximum < compareMinimum\n"
                    + "\t" + "compareMinimum = " + compareMinimum + "\t"
                    + "\t" + "compareMaximum = " + compareMaximum + "\t"
                    );
        }
        if (compareActual < compareMinimum) {
            throw new RuntimeException("compareActual < compareMinimum)\n"
                    + "\t" + "compareMinimum = " + compareMinimum + "\t"
                    + "\t" + "compareActual = " + compareActual + "\t"
                    );
        }
        if (compareActual > compareMaximum) {
            throw new RuntimeException("compareActual > compareMaximum\n"
                    + "\t" + "compareActual = " + compareActual + "\t"
                    + "\t" + "compareMaximum = " + compareMaximum + "\t"
                    );
        }
        if (wantedMaximum < wantedMinimum) {
            throw new RuntimeException("wantedMaximum < wantedMinimum\n"
                    + "\t" + "wantedMinimum = " + wantedMaximum + "\t"
                    + "\t" + "wantedMinimum = " + wantedMaximum + "\t"
                    );
        }
    }

    /**
     * Berechnet den Prozentsatz vom aktuellen Vergleichswert an der Strecke vom Vergleichsminimum
     * bis zum Vergleichsmaximum.
     *
     * Prozent von a an c:
     *
     * Prozent       a
     * -------  =   ---
     *   100         c
     *
     * => Prozent = 100 * a / c
     *
     * Genaueres siehe Darstellung im Kopf der Klasse.
     */
    private void calculatePercent() {
        say("MinToMaxSizeCalculator.calculatePercent()");
        double a = compareActual - compareMinimum;
        say("    a = " + a);
        double c = compareMaximum - compareMinimum;
        say("    c = " + c);
        percent = (100.0d * a) / c;
        say("    percent = " + percent);
        if (calculatePercentFromMaximumInsteadOfMinimum) {
            say("    calculatePercentFromMaximumInsteadOfMinimum ist wahr, daher:");
            percent = 100.0 - percent;
            say("    percent = " + percent);
        }
    }

    /**
     * Berechnet den gesuchten Wert.
     *
     * Um den Wert von wantedActual zu berechnen, also die Strecke von 0 bis wantedActual,
     * muss man zu wantedMinimum den prozentualen Anteil der Strecke von wantedMinimum bis
     * wantedMaximum addieren:
     *
     *  wantedActual - wantedMinimum     Prozent
     * ------------------------------- = -------
     *  wantedMaximum - wantedMinimum     100
     *
     * => wantedActual - wantedMinimum = (wantedMaximum - wantedMinimum) * Prozent / 100
     *
     * => wantedActual = wantedMinimum + (wantedMaximum - wantedMinimum) * Prozent / 100
     *
     * Weitere Details siehe Kopf der Klasse.
     */
    private void calculateWantedActual() {
        say("MinToMaxSizeCalculator.calculateWantedActual()");
        wantedActual = wantedMinimum + (wantedMaximum - wantedMinimum) * percent / 100.0d;
        say("    wantedActual = " + wantedActual);
    }

    /** Getter für den hier berechneten aktuelle Wert des Wunschswertes. */
    public double getWantedActual() {
        return wantedActual;
    }

    /** Getter für den hier berechneten aktuelle Wert des Wunschswertes als Integer. */
    public int getWantedActualAsInt() {
        return (int) wantedActual;
    }

    private static void say(String message) {
        if (DEBUG) {
            System.out.println(message);
        }
    }

}
