#include <string>

#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#include <media-layer/audio.h>

#include "sound.h"
#include "../feature/feature.h"
#include "../override/override.h"
#include "../init/init.h"

// Resolve Source File Path
#define SOURCE_FILE_BASE "data/libminecraftpe.so"
std::string _sound_get_source_file() {
    static bool source_loaded = false;
    static std::string source;

    // Check
    if (source_loaded) {
        // Already Resolved
        return source;
    } else {
        // Resolve

        // Get Binary Directory
        char *binary_directory = get_mcpi_directory();
        // Get Full Path
        char *full_path = NULL;
        safe_asprintf(&full_path, "%s/" SOURCE_FILE_BASE, binary_directory);

        // Handle Overrides
        char *overridden_full_path = override_get_path(full_path);
        if (overridden_full_path != NULL) {
            free(full_path);
            full_path = overridden_full_path;
        }

        // Check If Sound Exists
        if (access(full_path, F_OK) == -1) {
            // Fail
            WARN("%s", "Audio Source File Doesn't Exist: " SOURCE_FILE_BASE);
            source.assign("");
        } else {
            // Set
            source.assign(full_path);
        }

        // Free
        free(full_path);

        // Mark As Loaded
        source_loaded = true;

        // Return
        return _sound_get_source_file();
    }
}

// Play Sound
// The pitch value is unsued because it causes glitchy sounds, it is seemingly unused in MCPE as well.
static void play(std::string name, float x, float y, float z, float volume, bool is_ui) {
    std::string source = _sound_get_source_file();
    std::string resolved_name = _sound_pick(name);
    if (source.size() > 0 && resolved_name.size() > 0) {
        media_audio_play(source.c_str(), resolved_name.c_str(), x, y, z, 1.0f, volume, is_ui);
    }
}
static void SoundEngine_playUI_injection(__attribute__((unused)) unsigned char *sound_engine, std::string const& name, __attribute__((unused)) float pitch, float volume) {
    play(name, 0, 0, 0, volume, true);
}
static void SoundEngine_play_injection(__attribute__((unused)) unsigned char *sound_engine, std::string const& name, float x, float y, float z, __attribute__((unused)) float pitch, float volume) {
    play(name, x, y, z, volume, false);
}

// Refresh Data
static void SoundEngine_update_injection(unsigned char *sound_engine, unsigned char *listener_mob, __attribute__((unused)) float listener_angle) {
    // Variables
    static float volume = 0;
    static float x = 0;
    static float y = 0;
    static float z = 0;
    static float yaw = 0;

    // SoundEngine Properties
    unsigned char *options = *(unsigned char **) (sound_engine + SoundEngine_options_property_offset);

    // Volume
    int32_t sound_enabled = *(int32_t *) (options + Options_sound_property_offset);
    volume = sound_enabled ? 1 : 0;

    // Position And Rotation
    if (listener_mob != NULL) {
        // Values
        x = *(float *) (listener_mob + Entity_x_property_offset);
        y = *(float *) (listener_mob + Entity_y_property_offset);
        z = *(float *) (listener_mob + Entity_z_property_offset);
        yaw = *(float *) (listener_mob + Entity_yaw_property_offset);
    }

    // Log
    media_audio_update(volume, x, y, z, yaw);
}

// Resolve All Sounds On Init
// SoundEngine::init Is Called After The Audio Engine Has Been Loaded
static void SoundEngine_init_injection(unsigned char *sound_engine, unsigned char *minecraft, unsigned char *options) {
    // Call Original Method
    (*SoundEngine_init)(sound_engine, minecraft, options);

    // Resolve Sounds
    _sound_resolve_all();
}

// Init
void init_sound() {
    // Implement Sound Engine
    if (feature_has("Implement Sound Engine", 0)) {
        overwrite_calls((void *) SoundEngine_playUI, (void *) SoundEngine_playUI_injection);
        overwrite_calls((void *) SoundEngine_play, (void *) SoundEngine_play_injection);
        overwrite_calls((void *) SoundEngine_update, (void *) SoundEngine_update_injection);
        overwrite_calls((void *) SoundEngine_init, (void *) SoundEngine_init_injection);
    }
}
