#ifndef PORT_THREAD_WATCHDOG_H
#define PORT_THREAD_WATCHDOG_H

#include <stdint.h>

// Heartbeat watchdog over the revived decomp threads.
//
// Every serviced loop beats once per iteration and a watcher thread samples the
// counters. When a thread that has already proven alive stops beating while the
// wall clock keeps moving, the watcher logs the pipeline state: thread5's flag
// words and task rings, every queue depth, who is parked where, and the stalled
// thread's stack.

#ifdef __cplusplus
extern "C" {
#endif

typedef enum WatchdogThread {
    WATCHDOG_VI_TICKER = 0, // retrace source (OS_VI.cpp); everything below is paced by it
    WATCHDOG_MAIN_LOOP,     // window thread: event pump + RCP service (Game.cpp)
    WATCHDOG_GAME_TICK,     // push_frame loop (Game.cpp)
    WATCHDOG_THREAD5,       // decomp graphics thread (graphics_thread.c)
    WATCHDOG_VIMGR,         // decomp VI manager thread (vimgr.c)
    WATCHDOG_PFSMANAGER,    // controller thread (pfsmanager.c)
    WATCHDOG_AUDIO_MANAGER, // audio thread (audio_manager.c); idles during demo audio holds
    WATCHDOG_RUMBLE,        // motor thread (bamotor.c); one beat per retrace signal
    WATCHDOG_NUM_THREADS
} WatchdogThread;

// Called once per loop iteration; one relaxed atomic increment.
void ThreadWatchdog_Beat(WatchdogThread id);

// Started once the decomp threads exist, stopped before shutdown teardown.
void ThreadWatchdog_Start(void);
void ThreadWatchdog_Stop(void);

// Log the pipeline state immediately, stalled or not.
void ThreadWatchdog_DumpNow(void);

// Bracket a section that blocks a serviced thread on purpose.
void ThreadWatchdog_BeginExpectedStall(const char* reason);
void ThreadWatchdog_EndExpectedStall(void);

// Whether the watcher currently considers this thread stalled. The gui only
// draws inside serviced frames, so the main loop uses this to keep drawing
// while the tick is down instead of freezing ImGui along with it.
int ThreadWatchdog_IsStalled(WatchdogThread id);

// Diagnostic snapshots served by the decomp thread owners; plain structs so
// the C side fills them without knowing about the watchdog's internals.
typedef struct Thread5WatchdogState {
    int32_t unkFlag1; // 0x04 audio task, 0x08 gfx task, 0x10 idle, 0x20 yielded
    int32_t unkFlag2;
    int32_t unkFlag2Saved;
    int32_t syncCounter;
    int32_t task7Handled;
    int32_t gfxActiveId;
    int32_t gfxSelectedId;
    int32_t audioActiveId;
    int32_t audioSelectedId;
    int32_t taskQueueCount;
    int32_t taskQueueCap;
    int32_t syncQueueCount;
    int32_t syncQueueCap;
} Thread5WatchdogState;
void thread5_getWatchdogState(Thread5WatchdogState* out);

typedef struct ViMgrWatchdogState {
    int32_t retraceCount; // D_802808D8: retraces seen since the tick last consumed them
    int32_t q1Count;      // retrace event queue (viMgr_entry parks here)
    int32_t q2Count;      // frame token queue (the tick parks here)
    int32_t q3Count;      // tick retrace queue
} ViMgrWatchdogState;
void viMgr_getWatchdogState(ViMgrWatchdogState* out);

#ifdef __cplusplus
}

namespace Lighthouse {
class ExpectedStall {
public:
    explicit ExpectedStall(const char* reason) {
        ThreadWatchdog_BeginExpectedStall(reason);
    }
    ~ExpectedStall() {
        ThreadWatchdog_EndExpectedStall();
    }
    ExpectedStall(const ExpectedStall&) = delete;
    ExpectedStall& operator=(const ExpectedStall&) = delete;
};
} // namespace Lighthouse
#endif

#endif // PORT_THREAD_WATCHDOG_H