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

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <signal.h>
#include <atomic>
#include <string>
#include <vector>
#include <jack/jack.h>

#include "mixer/MixerEngine.hpp"
#include "mixer/MixerControlServer.hpp"

// ---------------------------------------------------------------------------
// Globals (for JACK callback — no C++ context pointer in plain jack_process)
// ---------------------------------------------------------------------------
static mixer::MixerEngine* g_engine = nullptr;
static jack_client_t* g_jackClient = nullptr;
static std::vector<jack_port_t*> g_inputPorts;
static std::vector<jack_port_t*> g_outputPorts;
static std::atomic<bool> g_running{true};
static const char* kJackClientName = "mixer-daemon";

// ---------------------------------------------------------------------------
// Forward signal handler
// ---------------------------------------------------------------------------
static void signalHandler(int sig)
{
    (void)sig;
    g_running = false;
}

// ---------------------------------------------------------------------------
// JACK process callback (real-time audio thread)
// ---------------------------------------------------------------------------
static int jackProcessCallback(jack_nframes_t nframes, void* /*arg*/)
{
    if (!g_engine) return 0;

    uint32_t inputChannels = (uint32_t)g_inputPorts.size();
    uint32_t outputChannels = (uint32_t)g_outputPorts.size();

    // Build device input buffer array from JACK ports
    std::vector<float*> deviceInputs(inputChannels, nullptr);
    for (uint32_t i = 0; i < inputChannels; ++i) {
        deviceInputs[i] = (float*)jack_port_get_buffer(g_inputPorts[i], nframes);
    }

    // Build device output buffer array from JACK ports
    std::vector<float*> deviceOutputs(outputChannels, nullptr);
    for (uint32_t i = 0; i < outputChannels; ++i) {
        deviceOutputs[i] = (float*)jack_port_get_buffer(g_outputPorts[i], nframes);
        // Zero output buffers
        memset(deviceOutputs[i], 0, nframes * sizeof(float));
    }

    // Run mixer engine (reads inputs, processes, writes to outputs)
    g_engine->process(
        deviceInputs.data(),
        inputChannels,
        deviceOutputs.data(),
        outputChannels,
        nframes
    );

    return 0;
}

// ---------------------------------------------------------------------------
// JACK shutdown callback
// ---------------------------------------------------------------------------
static void jackShutdownCallback(void* /*arg*/)
{
    fprintf(stderr, "JACK server shutdown.\n");
    g_running = false;
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
    const char* socketPath = "/tmp/mixer-daemon.sock";
    int inputChannels = 2;
    int outputChannels = 2;
    uint32_t sampleRate = 48000;
    uint32_t bufferSize = 256;

    // Parse command line args
    for (int i = 1; i < argc; ++i) {
        if (strcmp(argv[i], "--socket") == 0 && i + 1 < argc) {
            socketPath = argv[++i];
        } else if (strcmp(argv[i], "--inputs") == 0 && i + 1 < argc) {
            inputChannels = atoi(argv[++i]);
        } else if (strcmp(argv[i], "--outputs") == 0 && i + 1 < argc) {
            outputChannels = atoi(argv[++i]);
        } else if (strcmp(argv[i], "--sample-rate") == 0 && i + 1 < argc) {
            sampleRate = (uint32_t)atoi(argv[++i]);
        } else if (strcmp(argv[i], "--buffer-size") == 0 && i + 1 < argc) {
            bufferSize = (uint32_t)atoi(argv[++i]);
        } else if (strcmp(argv[i], "--help") == 0) {
            printf("Usage: mixer-daemon [options]\n");
            printf("  --socket <path>       Unix socket path (default: /tmp/mixer-daemon.sock)\n");
            printf("  --inputs <N>          Number of JACK input channels (default: 2)\n");
            printf("  --outputs <N>         Number of JACK output channels (default: 2)\n");
            printf("  --sample-rate <N>     Sample rate (default: 48000)\n");
            printf("  --buffer-size <N>     Buffer size in frames (default: 256)\n");
            printf("  --help                Show this help\n");
            return 0;
        }
    }

    fprintf(stderr, "mixer-daemon starting...\n");
    fprintf(stderr, "  Socket: %s\n", socketPath);
    fprintf(stderr, "  Channels: %d in / %d out\n", inputChannels, outputChannels);
    fprintf(stderr, "  Sample rate: %u, Buffer: %u\n", sampleRate, bufferSize);

    // --- Setup signal handlers ---
    signal(SIGINT, signalHandler);
    signal(SIGTERM, signalHandler);

    // --- Create MixerEngine ---
    mixer::MixerEngine engine;
    engine.setSampleRate(sampleRate);
    engine.setMaxBufferSize(bufferSize);
    engine.autoCreateChannels(inputChannels, outputChannels);
    engine.Prepare();

    // --- Open JACK client ---
    jack_status_t jackStatus;
    g_jackClient = jack_client_open(kJackClientName, JackNullOption, &jackStatus);
    if (!g_jackClient) {
        fprintf(stderr, "Failed to open JACK client (status=0x%x). Is JACK running?\n", jackStatus);

        // Fallback: run without JACK (dry/headless mode)
        fprintf(stderr, "Running in headless mode (no JACK audio I/O).\n");
        g_engine = &engine;

        // Still create the mixer control server
        mixer::MixerControlServer server(&engine, socketPath);
        if (!server.start()) {
            fprintf(stderr, "Failed to start control server at %s\n", socketPath);
            return 1;
        }

        fprintf(stderr, "Control server running at %s. Press Ctrl+C to stop.\n", socketPath);

        // Wait for shutdown signal
        while (g_running) {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }

        server.stop();
        return 0;
    }

    // Get actual sample rate and buffer size from JACK
    sampleRate = jack_get_sample_rate(g_jackClient);
    bufferSize = jack_get_buffer_size(g_jackClient);
    fprintf(stderr, "JACK active — sample rate: %u, buffer: %u\n", sampleRate, bufferSize);

    // Reconfigure engine with actual JACK parameters
    engine.setSampleRate(sampleRate);
    engine.setMaxBufferSize(bufferSize);
    engine.autoCreateChannels(inputChannels, outputChannels);
    engine.Prepare();

    g_engine = &engine;

    // --- Register JACK ports ---
    for (int i = 0; i < inputChannels; ++i) {
        char name[32];
        snprintf(name, sizeof(name), "input_%d", i + 1);
        jack_port_t* port = jack_port_register(
            g_jackClient, name, JACK_DEFAULT_AUDIO_TYPE,
            JackPortIsInput, 0);
        if (!port) {
            fprintf(stderr, "Failed to register input port %s\n", name);
        } else {
            g_inputPorts.push_back(port);
        }
    }

    for (int i = 0; i < outputChannels; ++i) {
        char name[32];
        snprintf(name, sizeof(name), "output_%d", i + 1);
        jack_port_t* port = jack_port_register(
            g_jackClient, name, JACK_DEFAULT_AUDIO_TYPE,
            JackPortIsOutput, 0);
        if (!port) {
            fprintf(stderr, "Failed to register output port %s\n", name);
        } else {
            g_outputPorts.push_back(port);
        }
    }

    fprintf(stderr, "Registered %zu input ports and %zu output ports.\n",
            g_inputPorts.size(), g_outputPorts.size());

    // --- Set JACK callbacks ---
    jack_set_process_callback(g_jackClient, jackProcessCallback, nullptr);
    jack_on_shutdown(g_jackClient, jackShutdownCallback, nullptr);

    // --- Activate JACK client ---
    if (jack_activate(g_jackClient) != 0) {
        fprintf(stderr, "Failed to activate JACK client.\n");
        jack_client_close(g_jackClient);
        return 1;
    }

    // --- Start control server ---
    mixer::MixerControlServer server(&engine, socketPath);
    if (!server.start()) {
        fprintf(stderr, "Failed to start control server at %s\n", socketPath);
        jack_deactivate(g_jackClient);
        jack_client_close(g_jackClient);
        return 1;
    }

    fprintf(stderr, "mixer-daemon running. Socket at %s. Press Ctrl+C to stop.\n", socketPath);

    // --- Main loop: wait for shutdown ---
    while (g_running) {
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }

    fprintf(stderr, "\nShutting down...\n");

    // --- Cleanup ---
    server.stop();
    jack_deactivate(g_jackClient);
    jack_client_close(g_jackClient);

    fprintf(stderr, "mixer-daemon stopped.\n");
    return 0;
}
