// McpMarshal.h: marshalling glue for the BKEmu MCP server.
//
// MCP tool calls arrive on HTTP worker threads, but the emulator's debug
// functions (CPlatform / CDebugger / CMainFrame) are written to run on the MFC
// main (UI) thread and several of them post/send window messages. To keep that
// contract, every MCP operation is hopped onto the UI thread via the
// WM_MCP_EXEC window message and the worker thread blocks until it completes.
//
// See mcp/README.md for the overall design.

#pragma once

#include <functional>
#include <exception>

#ifndef _WINDOWS_
#include <windows.h>
#endif

// Custom window message used to marshal an MCP command onto the UI thread.
// Posted by worker threads, handled by CMainFrame::OnMcpExec.
// Sits above the WM_USER+1xx range already used in Config.h (current max +132).
#define WM_MCP_EXEC (WM_USER + 140)

namespace mcp
{
	// A unit of work to run on the UI thread. The worker thread fills in `fn`
	// (which captures its own inputs and outputs by reference), submits it, and
	// blocks until the UI thread has run it. Because Run() blocks for the whole
	// duration, by-reference captures in `fn` stay valid until completion.
	struct McpCommand
	{
		std::function<void()> fn;               // executed on the UI thread
		std::exception_ptr    err{};            // set if fn() threw
		HANDLE                hDone{ nullptr };  // auto-reset event, signaled when done
	};

	// Registers the window that MCP commands are marshalled to, and remembers
	// the calling thread as the UI thread. Call once from CMainFrame after its
	// window exists; call again with nullptr on teardown.
	void SetTargetWindow(HWND hWnd);

	// True once a target window is registered (i.e. marshalling is possible).
	bool IsReady();

	// Runs `fn` on the UI thread and returns once it has completed. If `fn`
	// throws on the UI thread, the exception is rethrown here. Returns false
	// only if no target window is registered.
	//
	// Must be called from a non-UI thread; if called on the UI thread it runs
	// `fn` inline to avoid self-deadlock.
	bool Run(std::function<void()> fn);

	// Executes a marshalled command. Called by CMainFrame::OnMcpExec; lives here
	// so the run/catch/signal logic stays next to McpCommand.
	void Dispatch(McpCommand *pCmd);
}
