# C FFI Bindings

The C FFI layer (`crates/quicproquo-ffi/`) provides synchronous C-callable functions that wrap the Rust client library. This is the foundation for language bindings in Python (CFFI), Swift, Kotlin/JNI, Java, and Ruby.

## Building

```sh
cargo build --release -p quicproquo-ffi
```

This produces:
- Linux: `target/release/libquicproquo_ffi.so`
- macOS: `target/release/libquicproquo_ffi.dylib`
- Windows: `target/release/quicproquo_ffi.dll`

## API

### Status Codes

```c
#define QPQ_OK             0
#define QPQ_ERROR          1
#define QPQ_AUTH_FAILED    2
#define QPQ_TIMEOUT        3
#define QPQ_NOT_CONNECTED  4
```

### Functions

```c
// Connect to a server. Returns opaque handle or NULL on failure.
QpqHandle* qpq_connect(
    const char* server,       // "host:port"
    const char* ca_cert,      // path to CA certificate PEM
    const char* server_name   // TLS SNI name
);

// Authenticate with OPAQUE. Returns status code.
int qpq_login(
    QpqHandle* handle,
    const char* username,
    const char* password
);

// Send a message to a recipient (by username).
int qpq_send(
    QpqHandle* handle,
    const char* recipient,    // recipient username
    const uint8_t* message,   // message bytes
    size_t message_len
);

// Receive pending messages. Blocks up to timeout_ms.
// On success, *out_json is a JSON array of strings.
int qpq_receive(
    QpqHandle* handle,
    uint32_t timeout_ms,
    char** out_json           // caller must free with qpq_free_string
);

// Disconnect and free the handle.
void qpq_disconnect(QpqHandle* handle);

// Get last error message (valid until next FFI call on this handle).
const char* qpq_last_error(const QpqHandle* handle);

// Free a string returned by qpq_receive.
void qpq_free_string(char* ptr);
```

## Usage Example (C)

```c
#include <stdio.h>
#include <string.h>

// Forward declarations (or include a header).
typedef struct QpqHandle QpqHandle;
extern QpqHandle* qpq_connect(const char*, const char*, const char*);
extern int qpq_login(QpqHandle*, const char*, const char*);
extern int qpq_send(QpqHandle*, const char*, const unsigned char*, size_t);
extern int qpq_receive(QpqHandle*, unsigned int, char**);
extern void qpq_disconnect(QpqHandle*);
extern const char* qpq_last_error(const QpqHandle*);
extern void qpq_free_string(char*);

int main(void) {
    QpqHandle* h = qpq_connect("127.0.0.1:5001", "ca.pem", "localhost");
    if (!h) {
        fprintf(stderr, "connect failed\n");
        return 1;
    }

    int rc = qpq_login(h, "alice", "password123");
    if (rc != 0) {
        fprintf(stderr, "login failed: %s\n", qpq_last_error(h));
        qpq_disconnect(h);
        return 1;
    }

    const char* msg = "hello from C!";
    qpq_send(h, "bob", (const unsigned char*)msg, strlen(msg));

    char* json = NULL;
    rc = qpq_receive(h, 5000, &json);
    if (rc == 0 && json) {
        printf("received: %s\n", json);
        qpq_free_string(json);
    }

    qpq_disconnect(h);
    return 0;
}
```

Compile with:
```sh
gcc -o demo demo.c -L target/release -lquicproquo_ffi
```

## Memory Management

- `qpq_connect` returns a heap-allocated handle. The caller **must** call `qpq_disconnect` to free it.
- `qpq_receive` writes a heap-allocated JSON string to `*out_json`. The caller **must** call `qpq_free_string` to free it.
- `qpq_last_error` returns a pointer owned by the handle. Do **not** free it. It is valid until the next FFI call on the same handle.

## Thread Safety

Each `QpqHandle` owns its own Tokio runtime. Concurrent calls on the **same** handle are not safe. Create separate handles for concurrent use.

## Internals

The FFI layer bridges synchronous C callers to the async Rust client:

```
C caller  ─── qpq_login() ───►  QpqHandle  ─── runtime.block_on() ───►  async Rust client
```

Each handle contains:
- A Tokio runtime for blocking on async operations
- Server connection parameters
- Login state and error buffer
