package eu.midnightdust.yaytris.util.sound;

import eu.midnightdust.yaytris.Settings;

import javax.sound.sampled.*;
import java.io.IOException;

/**
 * Handle music in separate threads to not interrupt the main game
 */
public class MusicThread extends Thread {
    private static final int BUFFER_SIZE = 8192;
    private final boolean looped;
    private final String fileLocation;
    private boolean playing;

    public MusicThread(String fileLocation, boolean looped) {
        this.fileLocation = fileLocation;
        this.looped = looped;
        this.playing = true;
    }

    public void stopMusic() {
        this.playing = false;
    }

    @Override
    public void run() {
        do {
            playMusic(fileLocation);
        } while (looped && playing);
    }

    /**
     * INTERNAL!
     * Play the long audio file found at the specified location.
     * Fades out after calling {@link #stopMusic()}.
     * Adapted from: <a href="https://www.baeldung.com/java-play-sound">Baeldung</a>
     *
     * @see SoundUtil#playMusic(String, boolean)
     */
    private void playMusic(String fileLocation) {
        try (AudioInputStream stream = AudioSystem.getAudioInputStream(SoundUtil.getResource(fileLocation))) {
            AudioFormat format = stream.getFormat();
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

            SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(info);
            sourceDataLine.open(format);
            sourceDataLine.start();
            SoundUtil.setVolume(sourceDataLine, Settings.musicVolume);
            float fadeOut = 1.0f;

            byte[] bufferBytes = new byte[BUFFER_SIZE];
            int readBytes;
            while ((readBytes = stream.read(bufferBytes)) != -1) {
                if (!playing) {
                    fadeOut = (float) Math.sin(fadeOut - 0.01f); // Sinus fade-out
                    SoundUtil.setVolume(sourceDataLine, (int) (fadeOut * Settings.musicVolume));
                    if (fadeOut <= 0) break;
                }
                sourceDataLine.write(bufferBytes, 0, readBytes);
            }

            sourceDataLine.drain();
            sourceDataLine.close();
        } catch (LineUnavailableException | IOException | UnsupportedAudioFileException e) {
            throw new RuntimeException(e);
        }
    }
}
