#include "SecureLineVoiceDSP.h"

#include <cutils/properties.h>

#include <atomic>
#include <cmath>
#include <cstdlib>
#include <stdint.h>

static std::atomic<bool> gEnabled(false);
static std::atomic<float> gPitch(1.0f);
static std::atomic<float> gFormant(1.0f);
static std::atomic<float> gVariation(0.0f);
static std::atomic<float> gWarmth(0.0f);
static std::atomic<float> gCompression(0.0f);
static std::atomic<float> gDrive(0.0f);
static std::atomic<float> gOutputGain(1.0f);
static std::atomic<float> gSpectralTilt(0.0f);
static std::atomic<float> gPresence(0.0f);

static bool propertyEnabled()
{
    char value[PROP_VALUE_MAX];

    property_get(
        "persist.audio.secureline.enabled",
        value,
        "0"
    );

    return atoi(value) == 1;
}


static float propertyFloat(
        const char* key,
        float def)
{
    char value[PROP_VALUE_MAX];

    property_get(
        key,
        value,
        ""
    );

    if (value[0] == 0)
        return def;

    return strtof(
        value,
        nullptr
    );
}

static void refreshProperties()
{
    gEnabled.store(
        propertyEnabled(),
        std::memory_order_relaxed
    );
gPitch.store(
        propertyFloat(
            "persist.audio.secureline.pitch",
            1.0f
        ),
        std::memory_order_relaxed
    );

    gFormant.store(
        propertyFloat(
            "persist.audio.secureline.formant",
            1.0f
        ),
        std::memory_order_relaxed
    );

    gVariation.store(
        propertyFloat(
            "persist.audio.secureline.variation",
            0.0f
        ),
        std::memory_order_relaxed
    );

    gWarmth.store(
        propertyFloat(
            "persist.audio.secureline.warmth",
            0.0f
        ),
        std::memory_order_relaxed
    );

    gCompression.store(
        propertyFloat(
            "persist.audio.secureline.compression",
            0.0f
        ),
        std::memory_order_relaxed
    );

    gDrive.store(
        propertyFloat(
            "persist.audio.secureline.drive",
            0.0f
        ),
        std::memory_order_relaxed
    );

    gOutputGain.store(
        propertyFloat(
            "persist.audio.secureline.output_gain",
            1.0f
        ),
        std::memory_order_relaxed
    );

    gSpectralTilt.store(
        propertyFloat(
            "persist.audio.secureline.spectral_tilt",
            0.0f
        ),
        std::memory_order_relaxed
    );

    gPresence.store(
        propertyFloat(
            "persist.audio.secureline.presence",
            0.0f
        ),
        std::memory_order_relaxed
    );
}

struct ThreadDspState
{
    SecureLineVoiceDSP dsp;

    float lastPitch = -1.0f;
    float lastFormant = -1.0f;
    float lastVariation = -1.0f;
    float lastWarmth = -1.0f;
    float lastCompression = -1.0f;
    float lastDrive = -1.0f;
    float lastOutputGain = -1.0f;
    float lastSpectralTilt = 99.0f;
    float lastPresence = 99.0f;
    bool lastSpeakerRoute = false;
    bool speakerRouteInitialized = false;
};

static thread_local ThreadDspState tState;

static inline void updateDSPState(bool speakerRoute)
{
    const float pitch =
        gPitch.load(
            std::memory_order_relaxed
        );

    const float formant =
        gFormant.load(
            std::memory_order_relaxed
        );

    const float variation =
        gVariation.load(
            std::memory_order_relaxed
        );

    const float warmth =
        gWarmth.load(
            std::memory_order_relaxed
        );

    const float compression =
        gCompression.load(
            std::memory_order_relaxed
        );

    const float drive =
        gDrive.load(
            std::memory_order_relaxed
        );

    const float outputGain =
        gOutputGain.load(
            std::memory_order_relaxed
        );

    const float spectralTilt =
        gSpectralTilt.load(
            std::memory_order_relaxed
        );

    const float presence =
        gPresence.load(
            std::memory_order_relaxed
        );

    if (fabsf(pitch - tState.lastPitch) > 0.0001f)
    {
        tState.dsp.setPitch(pitch);
        tState.lastPitch = pitch;
    }

    if (fabsf(formant - tState.lastFormant) > 0.0001f)
    {
        tState.dsp.setFormant(formant);
        tState.lastFormant = formant;
    }

    if (fabsf(variation - tState.lastVariation) > 0.00001f)
    {
        tState.dsp.setVariation(variation);
        tState.lastVariation = variation;
    }

    if (fabsf(warmth - tState.lastWarmth) > 0.0001f)
    {
        tState.dsp.setWarmth(warmth);
        tState.lastWarmth = warmth;
    }

    if (fabsf(compression - tState.lastCompression) > 0.0001f)
    {
        tState.dsp.setCompression(compression);
        tState.lastCompression = compression;
    }

    if (fabsf(drive - tState.lastDrive) > 0.0001f)
    {
        tState.dsp.setDrive(drive);
        tState.lastDrive = drive;
    }

    if (fabsf(outputGain - tState.lastOutputGain) > 0.0001f)
    {
        tState.dsp.setOutputGain(outputGain);
        tState.lastOutputGain = outputGain;
    }

    if (fabsf(spectralTilt - tState.lastSpectralTilt) > 0.0001f)
    {
        tState.dsp.setSpectralTilt(spectralTilt);
        tState.lastSpectralTilt = spectralTilt;
    }

    if (fabsf(presence - tState.lastPresence) > 0.0001f)
    {
        tState.dsp.setPresence(presence);
        tState.lastPresence = presence;
    }

    if (!tState.speakerRouteInitialized ||
        speakerRoute != tState.lastSpeakerRoute)
    {
        tState.dsp.setSpeakerRoute(speakerRoute);
        tState.lastSpeakerRoute = speakerRoute;
        tState.speakerRouteInitialized = true;
    }
}

extern "C" {

bool secureline_voice_is_enabled()
{
    static std::atomic<uint64_t>
        lastRefresh(0);

    char refresh[PROP_VALUE_MAX];

    property_get(
        "persist.audio.secureline.refresh",
        refresh,
        "0"
    );

    const uint64_t now =
        strtoull(
            refresh,
            nullptr,
            10
        );

    const uint64_t previous =
        lastRefresh.exchange(
            now,
            std::memory_order_relaxed
        );

    if (now != previous)
    {
        refreshProperties();
    }

    return gEnabled.load(
        std::memory_order_relaxed
    );
}


void secureline_voice_process_int16(
        int16_t* buffer,
        int frames,
        int channels,
        int sampleRate,
        bool speakerRoute)
{
    if (!buffer ||
        frames <= 0 ||
        channels <= 0 ||
        sampleRate <= 0)
    {
        return;
    }

    if (!gEnabled.load(
            std::memory_order_relaxed))
    {
        return;
    }

    updateDSPState(speakerRoute);

    tState.dsp.processInt16(
        buffer,
        frames,
        channels,
        sampleRate
    );
}

void secureline_voice_process_float(
        float* buffer,
        int frames,
        int channels,
        int sampleRate,
        bool speakerRoute)
{
    if (!buffer ||
        frames <= 0 ||
        channels <= 0 ||
        sampleRate <= 0)
    {
        return;
    }

    if (!gEnabled.load(
            std::memory_order_relaxed))
    {
        return;
    }

    updateDSPState(speakerRoute);

    tState.dsp.processFloat(
        buffer,
        frames,
        channels,
        sampleRate
    );
}

}
