/*
    * Mixer.hpp
    * Software audio mixer: routes multiple per-process audio streams to a single
    * hardware output (Intel HDA), with per-stream volume, mute, and pause.
    * Copyright (c) 2026 Daniel Hammer
*/

#pragma once
#include <cstdint>
#include <Api/Syscall.hpp>

namespace Drivers::Audio::Mixer {

    // Maximum simultaneous virtual streams. Each open audio handle owned by a
    // process consumes one slot. Slot index doubles as the user-visible handle.
    constexpr int MAX_STREAMS = 8;

    // Fixed hardware mix format. Streams opened at other rates / channel
    // counts / bit depths are converted at write time.
    constexpr uint32_t MIX_RATE     = 48000;
    constexpr uint8_t  MIX_CHANNELS = 2;
    constexpr uint8_t  MIX_BITS     = 16;

    // Lazy-init: opens the underlying HDA stream on first virtual Open().
    int Open(uint32_t sampleRate, uint8_t channels, uint8_t bitsPerSample,
             int ownerPid, const char* ownerName);
    void Close(int handle);
    int Write(int handle, const uint8_t* data, uint32_t size);
    int Control(int handle, int cmd, int value);

    // Enumerate all currently-active streams.
    int List(Montauk::AudioStreamInfo* buf, int maxCount);

    // Close every stream owned by `pid`. Called from the scheduler on process
    // exit / kill so leaked handles do not occupy mixer slots.
    void CleanupProcess(int pid);

    // Master-volume control (applies post-mix, before HW DAC).
    void SetMasterVolume(int percent);
    int  GetMasterVolume();
    void SetMasterMute(bool muted);
    bool GetMasterMute();

    // Called from the HDA BCIS interrupt: a buffer segment finished playing,
    // refill the HW ring so audio doesn't loop stale data.
    void OnHdaBufferComplete();

    // Event-driven sync for clients (panel popup, Audio app, level meters).
    //
    // A monotonically increasing serial is bumped on every state change
    // (open/close, per-stream and master volume/mute, pause). Wait() blocks
    // until the serial differs from `prevSerial` or `timeoutMs` elapses, and
    // returns the current serial. timeoutMs == 0 returns immediately
    // (non-blocking poll). Callers should refresh their local snapshot
    // whenever the serial changes.
    uint64_t GetSerial();
    uint64_t Wait(uint64_t prevSerial, uint64_t timeoutMs);
};
