// panel_audition.cpp — the audition/preview engine seam of the docked bank panel.
// Hot-path guardrail: the preview path stays a direct free-function call-through —
// no interface, no virtual dispatch, no added header->TU indirection.
//
// DAW-verified, not unit tested. main.cpp owns the API pointers; here they are extern.

#include <string>

#include "shell/panel/panel_state.h"

// PlayPreview/StopPreview (stock, not SWS-only) drive a caller-owned preview_register_t.
#define REAPERAPI_MINIMAL
#define REAPERAPI_WANT_PlayPreview
#define REAPERAPI_WANT_StopPreview
#define REAPERAPI_WANT_PCM_Source_CreateFromFile
#define REAPERAPI_WANT_PCM_Source_Destroy
#include "reaper_plugin_functions.h"

namespace reasampler::panel {

// Audition is preview playback only — never inserts into the arrange or mutates
// the project/bank. PlayPreview streams a caller-owned PCM_source through
// REAPER's preview bus.
//
// Runtime assumptions not documented in the SDK header (DAW-verify, not asserted):
//   1. The audio thread reads preview_register_t by pointer while active, so it
//      must outlive playback — held in g_panel (static), never on the stack.
//   2. StopPreview is assumed to detach the source before returning, so
//      PCM_Source_Destroy immediately after is safe (SWS' preview helpers rely on
//      the same contract). If a race ever surfaces, the fix is a StartPreviewFade
//      + deferred free.
//   3. m_out_chan == 0 routes to the first hardware output pair (stereo, not mono).

void initPreview() {
    if (g_panel.previewInited) return;
#ifdef _WIN32
    InitializeCriticalSection(&g_panel.preview.cs);
#else
    pthread_mutex_init(&g_panel.preview.mutex, nullptr);
#endif
    g_panel.previewInited = true;
}

void stopAudition() {
    if (g_panel.previewActive) {
        StopPreview(&g_panel.preview);
        g_panel.previewActive = false;
    }
    if (g_panel.previewSrc) {
        PCM_Source_Destroy(g_panel.previewSrc);
        g_panel.previewSrc = nullptr;
    }
    g_panel.preview.src = nullptr;
}

void deinitPreview() {
    if (!g_panel.previewInited) return;
#ifdef _WIN32
    DeleteCriticalSection(&g_panel.preview.cs);
#else
    pthread_mutex_destroy(&g_panel.preview.mutex);
#endif
    g_panel.previewInited = false;
}

// `idx` is a display-order (slot) ordinal, resolved through orderedIds, not a raw
// BankModel position.
void startAudition(int idx) {
    stopAudition();

    const BankModel* index = indexForRegion(g_panel.focusedRegion);
    if (!index) return;
    const RegionDisplay disp = focusedDisplay();
    if (idx < 0 || idx >= disp.occupiedCount()) return;
    const Sample* s = index->query(disp.orderedIds[static_cast<std::size_t>(idx)]);
    if (!s) return;

    const std::string projectDir = currentProjectDir();
    const std::string abs = resolveBankFile(projectDir, s->relativePath);
    if (abs.empty()) return;

    PCM_source* src = PCM_Source_CreateFromFile(abs.c_str());
    if (!src) return;

    g_panel.preview.src = src;
    g_panel.preview.m_out_chan = 0;
    g_panel.preview.curpos = 0.0;
    g_panel.preview.loop = false;
    g_panel.preview.volume = 1.0;
    g_panel.preview.peakvol[0] = 0.0;
    g_panel.preview.peakvol[1] = 0.0;
    g_panel.preview.preview_track = nullptr;

    if (PlayPreview(&g_panel.preview) != 0) {
        g_panel.previewSrc = src;
        g_panel.previewActive = true;
    } else {
        PCM_Source_Destroy(src);
        g_panel.preview.src = nullptr;
    }
}

}  // namespace reasampler::panel
