package de.duehl.mp3.unused;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;

import javax.sound.sampled.FloatControl;
import javax.sound.sampled.SourceDataLine;

import de.duehl.basics.system.SystemTools;
import de.duehl.mp3.player.VolumeCalculations;

import java.io.IOException;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.AudioDevice;
import javazoom.jl.player.FactoryRegistry;
import javazoom.jl.player.JavaSoundAudioDevice;
import javazoom.jl.player.advanced.AdvancedPlayer;

/**
 * Ein Player für MP3-Dateien.
 *
 * Nach Code von David J. Barnes und Michael Kölling (31.07.2011) und anderen, überarbeitet von
 * Christian Dühl.
 *
 * Siehe auch:
 * https://falconbyte.net/blog-java-mp3.php, hieß ursprünglich "MusikPlayer".
 *
 * https://github.com/Gikkman/JavaZoom-Volume-Controll/blob/
 *     master/src/main/java/com/gikk/javazoom/JLayerTest.java
 *
 * @version 1.01     2025-07-20
 * @author Christian Dühl
 */

public class MP3PlayerAlt {

    //private static final int UNLIMITED_END_FRAME = Integer.MAX_VALUE;

    private static final boolean DEBUG = false;


    /** Der Dateiname der MP3-Datei, die abgespielt wird. */
    private String filename;

    /** Der JavaZoom-Player. */
    private AdvancedPlayer player;

    private AudioDevice device;

    private FloatControl volumeControl;

    /** Konstruktor. */
    public MP3PlayerAlt() {
        player = null;
    }

//    /** Spielt die erste halbe Sekunde eines Musikstücks an. */
//    public void playFirstPart(String filename) {
//        try {
//            preparePlayer(filename);
//            player.play(500);
//        }
//        catch (JavaLayerException exception) {
//            reportProblem(filename);
//        }
//        finally {
//            killPlayer();
//        }
//    }

    /** Spielt das Musikstück in einem eigenen Tread ab. */
    public void playMP3InOwnThread(String filename) {
        this.filename = filename;
        startPlayInOwnThread();
    }

    private void startPlayInOwnThread() {
        try {
            tryToStartPlayInOwnThread();
        }
        catch (Exception exception) {
            reportProblem();
        }
    }

    private void tryToStartPlayInOwnThread() {
        preparePlayer();
        new Thread(() -> startPlay()).start();
    }

    private void preparePlayer() {
        try {
            InputStream stream = createInputStream();
            device = createAudioDevice();
            player = new AdvancedPlayer(stream, device);
        }
        catch (IOException exception) {
            reportProblem();
            killPlayer();
        }
        catch (JavaLayerException exception) {
            reportProblem();
            killPlayer();
        }
    }

    private InputStream createInputStream() throws IOException {
        return new BufferedInputStream(new FileInputStream(filename));
    }

    private AudioDevice createAudioDevice() throws JavaLayerException {
        return FactoryRegistry.systemRegistry().createAudioDevice();
    }

    private void startPlay() {
        try {
            //player.play(UNLIMITED_END_FRAME); // 5000
            player.play();
        }
        catch (JavaLayerException exception) {
            reportProblem();
        }
        finally {
            killPlayer();
        }
    }

    /**
     * Setzt die Lautstärke. Werte sind nur von 0 bis 100 gültig und sind in der Einheit.
     * Funktioniert nur, wenn die Musik bereits abgespielt wird.
     *
     * @param percent
     *            Die Lautstärke zwischen 0 und 100 Prozent.
     */
    public void setVolumeInPercent(int percent) {
        float volume = VolumeCalculations.volumePercentToVolumeDb(percent);
        say("Setze auf: " + VolumeCalculations.toCorrectVolumePercent(percent) + "%, also " + volume
                + " dB");
        setVolumeInDb(volume);
    }

    /**
     * Setzt die Lautstärke. Werte sind nur von -80.0 bis 6.0 gültig und sind in der Einheit dB.
     *
     * Funktioniert nur, wenn die Musik bereits abgespielt wird.
     *
     * @param volume
     *            Die Lautstärke in dB von -80.0 bis 6.0.
     */
    public void setVolumeInDb(float volume) {
        say("setVolume() gain = " + volume);
        if (volumeControl == null) {
            say("War null");
            initVolumeControl();
        }

        if (volumeControl == null) {
            say("Ist immer noch null");
        }
        else {
            float minimum = volumeControl.getMinimum();
            if (Math.abs(minimum - VolumeCalculations.MIN_VOLUME_DB) > 0.01f) {
                System.out.println("Abweichung vom erwarteten Lautstärke Minimum: " + minimum
                        + " statt " + VolumeCalculations.MIN_VOLUME_DB);
            }
            //say("Lautstärke Minimum: " + minimum);
            float maximum = volumeControl.getMaximum();
            if (Math.abs(maximum - VolumeCalculations.MAX_VOLUME_DB) > 0.01f) {
                System.out.println("Abweichung vom erwarteten Lautstärke Maximum: " + maximum
                        + " statt " + VolumeCalculations.MAX_VOLUME_DB);
            }
            //say("Lautstärke Maximum: " + maximum);
            float volumeToSet = Math.min(Math.max(volume, minimum), maximum);
            say("War: " + volumeControl.getValue() + ", wird nun gesetzt auf: " + volumeToSet);
            volumeControl.setValue(volumeToSet);
        }
    }

    private void initVolumeControl() {
        try {
            tryToInitVolumeControl();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void tryToInitVolumeControl() throws IllegalArgumentException, IllegalAccessException {
        Class<JavaSoundAudioDevice> clazz = JavaSoundAudioDevice.class;
        Field[] fields = clazz.getDeclaredFields();

        SourceDataLine source = null;
        for (Field field : fields) {
            if ("source".equals(field.getName())) {
                field.setAccessible(true);
                while (source == null) {
                    SystemTools.sleep(5);
                    source = (SourceDataLine) field.get(device);
                    if (!sourceHasMasterGain(source)) {
                        source = null;
                    }
                }
                field.setAccessible(false);
                volumeControl = (FloatControl) source.getControl(FloatControl.Type.MASTER_GAIN);
                break;
            }
        }
    }

    private boolean sourceHasMasterGain(SourceDataLine source) {
        try {
            source.getControl(FloatControl.Type.MASTER_GAIN);
            return true;
        }
        catch (Exception exception) {
            return false;
        }
    }

    /** Beendet das Abspielen des aktuell gespielten Musikstücks. */
    public void stop() {
        killPlayer();
    }

    private void killPlayer() {
        synchronized (this) {
            if (player != null) {
                player.stop();
                player = null;
            }
        }
    }

    private void reportProblem() {
        System.out.println("Es gab ein Problem beim Abspielen von: " + filename);
    }

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

}
