// reaper_bridge.h — the REAPER VST-host bridge. Thin shell: resolves REAPER API functions
// by name over the host context and reads the live "reasampler" project ext-state. The
// fiddly decode lives in bridge_marshal (pure).
//
// Bridge mechanism: REAPER passes an IHostApplication as `context` to
// IComponent::initialize; querying it for IReaperHostApplication yields getReaperApi
// (resolve a REAPER API function pointer by name) and getReaperParent(3) (the host
// ReaProject*; 1=track, 2=take, 3=project, 4=fxdsp, 5=trackchan) — not VST2 hostcb opcodes.

#pragma once

#include <optional>
#include <string>

#include "pluginterfaces/base/funknown.h"

namespace reasampler::vst {

// Wraps the REAPER host bridge for a single plugin instance. Constructed cheaply;
// connect() must be called with the initialize() context before any read. All reads
// degrade to nullopt (never crash) when the host is not REAPER or a symbol is absent —
// the instrument must load in non-REAPER hosts too, just without live state.
class ReaperBridge {
public:
    ReaperBridge() = default;

    // Binds to the host (`context` is the FUnknown* IComponent::initialize hands us).
    // Returns true when the host is REAPER and the ext-state API resolved; safe to call
    // with a null or non-REAPER context (returns false).
    bool connect(Steinberg::FUnknown* context);

    bool isConnected() const { return getProjExtState_ != nullptr; }

    // Reads a "reasampler" ext-state value by key from the host's active project.
    // Returns nullopt when unconnected, unresolvable, or the key is absent.
    //
    // NOT REAL-TIME SAFE (allocates + calls into REAPER): audio-thread callers MUST NOT
    // invoke this. The instrument reads on the main/UI thread and hands a snapshot to
    // the process path.
    std::optional<std::string> readReasamplerExtState(const std::string& key);

    // The active project's directory (forward-slashed, no trailing slash) — the same
    // convention persist uses to place the bank alongside the .rpp. Empty for an unsaved
    // project or when unconnected. Not RT-safe.
    std::string activeProjectDir();

    // Writes THIS INSTANCE's usage record: the ONE sanctioned instrument-side ext-state
    // write. `usageKey` MUST carry the "rsusage_" prefix (ext_keys.h's usageKeyFor); any
    // other key is refused, enforcing the read-only-bank invariant structurally (banks/
    // view/tail/assign stay unwritable from the instrument). Returns true iff written
    // (the SetProjExtState return is checked). NOT RT-safe — publish sites are the
    // off-audio-thread reload path only. Deliberately does NOT mark the project dirty: a
    // usage change always rides a component-state change that already does.
    bool writeUsageExtState(const std::string& usageKey, const std::string& value);

    // The canonical GUID string of the track hosting this FX instance (same rendering as
    // the extension's track_guid::guidString, so usage records compare byte-equal
    // against its live-FX enumeration). Empty when unconnected or no track context (the
    // usage reader then falls back to any-instance liveness). Not RT-safe.
    std::string currentTrackGuid();

private:
    // Resolved REAPER API function pointers (by name via getReaperApi). Signatures
    // verified against reaper_plugin_functions.h.
    using GetProjExtStateFn = int (*)(void* proj, const char* extname, const char* key,
                                      char* valOutNeedBig, int valOutNeedBig_sz);
    using EnumProjExtStateFn = bool (*)(void* proj, const char* extname, int idx,
                                        char* keyOut, int keyOut_sz, char* valOut,
                                        int valOut_sz);
    // EnumProjects(-1, projfnOut, sz) -> active project + its .rpp path. idx=-1 (current
    // tab) follows the active project, same convention as the persist shell.
    using EnumProjectsFn = void* (*)(int idx, char* projfnOut, int projfnOut_sz);
    // Used ONLY by writeUsageExtState (prefix-guarded) — see the read-only-bank note there.
    using SetProjExtStateFn = int (*)(void* proj, const char* extname, const char* key,
                                      const char* value);
    // Opaque-pointer signatures so the header stays SDK-type-free; the GUID* is passed
    // straight through, never dereferenced here.
    using GetTrackGuidFn = void* (*)(void* tr);
    using GuidToStringFn = void (*)(const void* g, char* destNeed64);

    void* hostApp_ = nullptr;  // IReaperHostApplication* (opaque here; used in .cpp)
    GetProjExtStateFn getProjExtState_ = nullptr;
    EnumProjExtStateFn enumProjExtState_ = nullptr;
    EnumProjectsFn enumProjects_ = nullptr;
    SetProjExtStateFn setProjExtState_ = nullptr;
    GetTrackGuidFn getTrackGuid_ = nullptr;
    GuidToStringFn guidToString_ = nullptr;
};

}  // namespace reasampler::vst
