////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////

package pp.battleship.client;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetLoadException;
import com.jme3.asset.AssetNotFoundException;
import com.jme3.audio.AudioData;
import com.jme3.audio.AudioNode;
import pp.battleship.notification.GameEventListener;
import pp.battleship.notification.SoundEvent;

import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.prefs.Preferences;

import static pp.util.PreferencesUtils.getPreferences;

/**
 * An application state that plays sounds.
 */
public class GameSound extends AbstractAppState implements GameEventListener {
    private static final Logger LOGGER = System.getLogger(GameSound.class.getName());
    private static final Preferences PREFERENCES = getPreferences(GameSound.class);
    private static final String ENABLED_PREF = "enabled"; //NON-NLS

    private AudioNode splashSound;
    private AudioNode shipDestroyedSound;
    private AudioNode explosionSound;

    /**
     * Checks if sound is enabled in the preferences.
     *
     * @return {@code true} if sound is enabled, {@code false} otherwise.
     */
    public static boolean enabledInPreferences() {
        return PREFERENCES.getBoolean(ENABLED_PREF, true);
    }

    /**
     * Toggles the game sound on or off.
     */
    public void toggleSound() {
        setEnabled(!isEnabled());
    }

    /**
     * Sets the enabled state of this AppState.
     * Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)}
     *
     * @param enabled {@code true} to enable the AppState, {@code false} to disable it.
     */
    @Override
    public void setEnabled(boolean enabled) {
        if (isEnabled() == enabled) return;
        super.setEnabled(enabled);
        LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); //NON-NLS
        PREFERENCES.putBoolean(ENABLED_PREF, enabled);
    }

    /**
     * Initializes the sound effects for the game.
     * Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
     *
     * @param stateManager The state manager
     * @param app          The application
     */
    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        shipDestroyedSound = loadSound(app, "Sound/Effects/sunken.wav"); //NON-NLS
        splashSound = loadSound(app, "Sound/Effects/splash.wav"); //NON-NLS
        explosionSound = loadSound(app, "Sound/Effects/explosion.wav"); //NON-NLS
    }

    /**
     * Loads a sound from the specified file.
     *
     * @param app  The application
     * @param name The name of the sound file.
     * @return The loaded AudioNode.
     */
    private AudioNode loadSound(Application app, String name) {
        try {
            final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
            sound.setLooping(false);
            sound.setPositional(false);
            return sound;
        }
        catch (AssetLoadException | AssetNotFoundException ex) {
            LOGGER.log(Level.ERROR, ex.getMessage(), ex);
        }
        return null;
    }

    /**
     * Plays the splash sound effect.
     */
    public void splash() {
        if (isEnabled() && splashSound != null)
            splashSound.playInstance();
    }

    /**
     * Plays the explosion sound effect.
     */
    public void explosion() {
        if (isEnabled() && explosionSound != null)
            explosionSound.playInstance();
    }

    /**
     * Plays sound effect when a ship has been destroyed.
     */
    public void shipDestroyed() {
        if (isEnabled() && shipDestroyedSound != null)
            shipDestroyedSound.playInstance();
    }

    @Override
    public void receivedEvent(SoundEvent event) {
        switch (event.sound()) {
            case EXPLOSION -> explosion();
            case SPLASH -> splash();
            case DESTROYED_SHIP -> shipDestroyed();
        }
    }
}
