#pragma once

#include <stddef.h>
#include <stdint.h>

#define com_proxy_downcast(self) ((struct com_proxy *) self)

struct com_proxy {
    /* Pointer to vtable filled with trampolines. Edit these as you please.
       Each com_proxy has its own independent vtable. */
    void *vptr;

    /* Interface pointer wrapped by this proxy. */
    void *real;

    /* Context pointer for use by hook code. */
    void *ctx;

    /* Optional cleanup callback, will be called during com_proxy deallocation
       to clean up *ctx. */
    void (*cleanup_ctx)(void *ctx);

    /* Dynamically generated x86 trampoline code. The initial vtable entries
       all point into code located here. */
    uint8_t *tramps;
};

/* Wrap a COM interface pointer in a proxy. This is an object that acts just
   like the object that it wraps but has a freely editable vtable, which you
   can modify in order to intercept a subset of the interface's method calls.

   By default, all the vtable slots contain dynamically generated trampolines
   which pass method calls onwards to the corresponding methods in the
   underlying object's vtable.

   NOTE! This does not AddRef the underlying interface.

   NOTE! This function wraps COM POINTERS, not COM OBJECTS (since the latter
   is, in general, impossible). Consequences of this distinction include the
   following:

   1. Do not wrap IUnknown pointers with this function. This will break the
      IUnknown::QueryInterface contract. This refers to _the_ unique
      IUnknown* for the object, not for any other interface (which necessarily
      extends IUnknown). Wrapping the unique IUnknown* for an object will
      cause it to no longer be unique.

   2. Callers can "jailbreak" your wrapper using IUnknown::QueryInterface
      unless you provide a custom QueryInterface implementation to prevent them
      from doing so. */

HRESULT com_proxy_wrap(struct com_proxy **out, void *real, size_t vtbl_size);
