// Copyright (c) 2026 Ourpad Network
// See LICENSE file in the project root for full license text.

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cassert>
#include "mixer/MixerEngine.hpp"
#include "mixer/MixerChannelStrip.hpp"
#include "mixer/MixerBus.hpp"

using namespace mixer;

// Counters for test results
static int testsPassed = 0;
static int testsFailed = 0;

#define TEST(name) \
    do { \
        printf("  TEST: %s ... ", name); \
        bool ok = true;

#define END_TEST \
        if (ok) { \
            printf("PASS\n"); \
            testsPassed++; \
        } else { \
            printf("FAIL\n"); \
            testsFailed++; \
        } \
    } while(0)

// ---------------------------------------------------------------------------
// Test: Channel strip volume control
// ---------------------------------------------------------------------------
void testChannelVolume()
{
    TEST("Channel strip volume control");
    MixerChannelStrip strip(0);
    strip.setVolume(0.0f); // unity
    assert(std::abs(strip.volume() - 0.0f) < 0.001f);
    strip.setVolume(-6.0f);
    assert(std::abs(strip.volume() - (-6.0f)) < 0.001f);
    strip.setVolume(12.0f); // max
    assert(std::abs(strip.volume() - 12.0f) < 0.001f);
    strip.setVolume(-999.0f); // clamp
    assert(std::abs(strip.volume() - (-96.0f)) < 0.001f);
    END_TEST;
}

// ---------------------------------------------------------------------------
// Test: Channel strip pan
// ---------------------------------------------------------------------------
void testChannelPan()
{
    TEST("Channel strip pan");
    MixerChannelStrip strip(0);
    strip.setPan(0.0f); // center
    assert(std::abs(strip.pan() - 0.0f) < 0.001f);
    strip.setPan(-1.0f); // full left
    assert(std::abs(strip.pan() - (-1.0f)) < 0.001f);
    strip.setPan(1.0f); // full right
    assert(std::abs(strip.pan() - 1.0f) < 0.001f);
    strip.setPan(-2.0f); // clamp
    assert(std::abs(strip.pan() - (-1.0f)) < 0.001f);
    END_TEST;
}

// ---------------------------------------------------------------------------
// Test: Channel strip mute and solo
// ---------------------------------------------------------------------------
void testChannelMuteSolo()
{
    TEST("Channel strip mute/solo");
    MixerChannelStrip strip(0);
    assert(strip.mute() == false);
    assert(strip.solo() == false);
    strip.setMute(true);
    assert(strip.mute() == true);
    strip.setSolo(true);
    assert(strip.solo() == true);
    END_TEST;
}

// ---------------------------------------------------------------------------
// Test: Channel strip HPF
// ---------------------------------------------------------------------------
void testChannelHpf()
{
    TEST("Channel strip HPF");
    MixerChannelStrip strip(0);
    strip.setHpEnabled(true);
    assert(strip.hpEnabled() == true);
    strip.setHpFrequency(120.0f);
    assert(std::abs(strip.hpFrequency() - 120.0f) < 0.001f);
    END_TEST;
}

// ---------------------------------------------------------------------------
// Test: Bus volume and mute
// ---------------------------------------------------------------------------
void testBusControl()
{
    TEST("Bus volume and mute");
    MixerBus bus(1, MixerBusType::Subgroup, "TestBus", 2);
    bus.allocateBuffers(256);
    bus.setVolume(0.0f);
    assert(std::abs(bus.volume() - 0.0f) < 0.001f);
    bus.setVolume(-12.0f);
    assert(std::abs(bus.volume() - (-12.0f)) < 0.001f);
    bus.setMute(true);
    assert(bus.mute() == true);
    END_TEST;
}

// ---------------------------------------------------------------------------
// Test: Bus accumulate and process
// ---------------------------------------------------------------------------
void testBusAccumulate()
{
    TEST("Bus accumulate and process");
    MixerBus bus(2, MixerBusType::Master, "Master", 2);
    bus.allocateBuffers(64);

    // Create a sine wave buffer
    float buffer[64];
    for (int i = 0; i < 64; ++i) {
        buffer[i] = sinf(2.0f * M_PI * i / 16.0f) * 0.5f;
    }

    const float* src[2] = { buffer, buffer };
    bus.accumulate(src, 64, 1.0f, 2);

    // After accumulation, buffers should contain the sine wave
    for (int i = 0; i < 64; ++i) {
        assert(std::abs(bus.buffer(0)[i] - buffer[i]) < 0.001f);
        assert(std::abs(bus.buffer(1)[i] - buffer[i]) < 0.001f);
    }

    // Process with unity gain — should keep values
    bus.process(64);

    // VU should show something (peak ~= 0.5 = -6dB)
    assert(bus.vuLeft() > -12.0f && bus.vuLeft() < 0.0f);

    // Clear and check zero
    bus.clear();
    for (int i = 0; i < 64; ++i) {
        assert(std::abs(bus.buffer(0)[i]) < 0.0001f);
    }
    END_TEST;
}

// ---------------------------------------------------------------------------
// Test: MixerEngine lifecycle
// ---------------------------------------------------------------------------
void testEngineLifecycle()
{
    TEST("MixerEngine lifecycle");
    MixerEngine engine;

    // Start with master bus only
    assert(engine.channelCount() == 0);
    assert(engine.busIds().size() == 1); // master

    // Add channels
    engine.setSampleRate(48000);
    engine.setMaxBufferSize(256);
    auto* ch0 = engine.addChannel(0);
    assert(ch0 != nullptr);
    assert(engine.channelCount() == 1);

    auto* ch1 = engine.addChannel(1);
    assert(ch1 != nullptr);
    assert(engine.channelCount() == 2);

    // Add a subgroup bus
    int64_t sgId = engine.addBus(MixerBusType::Subgroup, "Drums", 2);
    assert(engine.getBus(sgId) != nullptr);

    // Route channel 0 to subgroup
    engine.routeChannelToBus(0, sgId, 0.0f);
    engine.Prepare();
    engine.Activate();

    // Capture and restore snapshot
    auto snap = engine.captureSnapshot();
    assert(snap.channels.size() == 2);
    assert(snap.buses.size() == 2); // master + subgroup

    // Modify, then restore
    ch0->setVolume(-12.0f);
    engine.applySnapshot(snap);
    // After applySnapshot, volume should be back to snapshot value (-96.0f default)
    assert(std::abs(ch0->volume() - (-96.0f)) < 0.001f);

    // Actually, applySnapshot should reset to the captured state
    // Let's check: we captured BEFORE changing volume, so snapshot has volume=0
    // After apply, volume should be 0 (the snapshot value) not -12
    // But we set volume after capture... wait, let me re-read the test
    // Volume was set to default (-96), then snapshot captured (volume=-96)
    // Then set to -12, then apply snapshot -> should go back to -96

    engine.Deactivate();
    END_TEST;
}

// ---------------------------------------------------------------------------
// Test: MixerEngine processing
// ---------------------------------------------------------------------------
void testEngineProcess()
{
    TEST("MixerEngine processing (simple passthrough)");
    MixerEngine engine;
    engine.setSampleRate(48000);
    engine.setMaxBufferSize(128);

    // Create 1 channel
    engine.addChannel(0);
    engine.getChannel(0)->setVolume(0.0f); // unity
    engine.Prepare();
    engine.Activate();

    // Create input buffer with a ramp
    float input0[128];
    float output0[128] = {0};
    float output1[128] = {0};
    for (int i = 0; i < 128; ++i) {
        input0[i] = (float)i / 128.0f;
    }

    float* deviceInputs[] = { input0 };
    float* deviceOutputs[] = { output0, output1 };

    engine.process(deviceInputs, 1, deviceOutputs, 2, 128);

    // Output should match input (unity gain, pan center = -3dB each)
    for (int i = 0; i < 128; ++i) {
        // Center pan = -3dB per channel = ~0.707
        float expected = input0[i] * 0.707f;
        assert(std::abs(output0[i] - expected) < 0.01f);
        assert(std::abs(output1[i] - expected) < 0.01f);
    }

    engine.Deactivate();
    END_TEST;
}

// ---------------------------------------------------------------------------
// Test: MixerEngine autoCreateChannels
// ---------------------------------------------------------------------------
void testAutoCreateChannels()
{
    TEST("MixerEngine autoCreateChannels");
    MixerEngine engine;
    engine.autoCreateChannels(4, 2);

    assert(engine.channelCount() == 4);
    assert(engine.physicalInputCount() == 4);
    assert(engine.physicalOutputCount() == 2);

    // Each channel should have a default label
    for (size_t i = 0; i < engine.channelCount(); ++i) {
        auto* ch = engine.getChannel(i);
        assert(ch != nullptr);
        assert(ch->label().find("Input") != std::string::npos);
    }

    // Master bus should be there
    assert(engine.masterBus() != nullptr);

    // Default output route should route master to 1-2
    assert(engine.outputRoutes().size() == 1);
    assert(engine.outputRoutes()[0].sourceBusId == engine.masterBus()->id());
    END_TEST;
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
int main()
{
    printf("OPLabs Mixer Engine Standalone — Core Tests\n");
    printf("=============================================\n\n");

    testChannelVolume();
    testChannelPan();
    testChannelMuteSolo();
    testChannelHpf();
    testBusControl();
    testBusAccumulate();
    testEngineLifecycle();
    testEngineProcess();
    testAutoCreateChannels();

    printf("\n=============================================\n");
    printf("Results: %d passed, %d failed out of %d\n",
           testsPassed, testsFailed, testsPassed + testsFailed);

    return testsFailed > 0 ? 1 : 0;
}
