#include "SND_util.h"

#include "syscall.h"
#include "code32.h"

// TODO remove this extern once the static const definition of it is here
extern s8 sLfoSinTable[0x21];

u16 SND_CalcTimer(int timer, int pitch)
{
    int octave = 0;
    int pitch_normalized = -pitch;

    while (pitch_normalized < 0)
    {
        octave--;
        pitch_normalized += 768;
    }

    while (pitch_normalized >= 768)
    {
        octave++;
        pitch_normalized -= 768;
    }

    u64 result = SVC_GetPitchTable(pitch_normalized);

    result += 0x10000;
    result *= timer;

    int shift = octave - 16;

    if (shift <= 0)
    {
        shift = -shift;
        result >>= shift;
    }
    else if (shift < 32)
    {
        // clamp in case timer value overflows
        u64 tmp = result & ~0uLL << (32 - shift);
        if (tmp != 0)
            return 0xFFFF;
        result <<= shift;
    }
    else
    {
        return 0xFFFF;
    }

    if (result < 0x10)
        result = 0x10;
    else if (result > 0xFFFF)
        result = 0xFFFF;

    return (u16)result;
}

u16 SND_CalcChannelVolume(int value)
{
    if (value < SND_VOL_DB_MIN)
        value = SND_VOL_DB_MIN;
    else if (value > 0)
        value = 0;

    int result = SVC_GetVolumeTable(value + (-SND_VOL_DB_MIN));
    int div;

    if (value < -240)
        div = 3;
    else if (value < -120)
        div = 2;
    else if (value < -60)
        div = 1;
    else
        div = 0;

    return (u16)(result | (div << 8));
}

s8 SND_SinIdx(int x)
{
    // BUG: UB for out of range values

    if (x < 0x20)
    {
        return sLfoSinTable[x];
    }
    else if (x < 0x40)
    {
        return sLfoSinTable[0x40 - x];
    }
    else if (x < 0x60)
    {
        return (s8)(-sLfoSinTable[x - 0x40]);
    }
    else
    {
        return (s8)(-sLfoSinTable[0x20 - (x - 0x60)]);
    }
}

u16 SND_CalcRandom(void)
{
    static u32 state = 0x12345678;

    // values from "Numerical Recipes"
    state = state * 1664525u + 1013904223u;
    return (u16)(state >> 16u);
}
