// MIT License
// Copyright 2023--present rgpot developers
//
// Auto-generated by cbindgen — do not edit manually.


#ifndef RGPOT_H
#define RGPOT_H

/* Generated with cbindgen:0.27.0 */

/* Warning: this file is auto-generated by cbindgen. Do not modify this manually. */

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <dlpack/dlpack.h>

#define RGPOT_VERSION "2.0.0"
#define RGPOT_VERSION_MAJOR 2
#define RGPOT_VERSION_MINOR 0
#define RGPOT_VERSION_PATCH 0

/**
 * Status codes returned by all C API functions.
 */
typedef enum rgpot_status_t {
  /**
   * Operation completed successfully.
   */
  RGPOT_SUCCESS = 0,
  /**
   * An invalid parameter was passed (null pointer, wrong size, etc.).
   */
  RGPOT_INVALID_PARAMETER = 1,
  /**
   * An internal error occurred (e.g. a Rust panic was caught).
   */
  RGPOT_INTERNAL_ERROR = 2,
  /**
   * An RPC communication error occurred.
   */
  RGPOT_RPC_ERROR = 3,
  /**
   * A buffer was too small for the requested operation.
   */
  RGPOT_BUFFER_SIZE_ERROR = 4,
} rgpot_status_t;

#if defined(RGPOT_HAS_RPC)
/**
 * RPC client that connects to a remote rgpot server.
 */
typedef struct RpcClient RpcClient;
#endif

/**
 * Input configuration for a potential energy evaluation.
 *
 * All tensor fields are *borrowed* — the caller retains ownership and must
 * keep them alive for the lifetime of this struct.
 *
 * # Example (from C)
 *
 * ```c
 * double positions[] = {0.0, 0.0, 0.0,  1.0, 0.0, 0.0};
 * int    types[]     = {1, 1};
 * double cell[]      = {10.0,0,0, 0,10.0,0, 0,0,10.0};
 *
 * rgpot_force_input_t input = rgpot_force_input_create(2, positions, types, cell);
 * // ... use input ...
 * rgpot_force_input_free(&input);
 * ```
 */
typedef struct rgpot_force_input_t {
  /**
   * Positions tensor: `[n_atoms, 3]`, dtype f64, any device.
   */
  DLManagedTensorVersioned *positions;
  /**
   * Atomic numbers tensor: `[n_atoms]`, dtype i32, any device.
   */
  DLManagedTensorVersioned *atomic_numbers;
  /**
   * Box matrix tensor: `[3, 3]`, dtype f64, any device.
   */
  DLManagedTensorVersioned *box_matrix;
} rgpot_force_input_t;

/**
 * Results from a potential energy evaluation.
 *
 * The `forces` field starts as `NULL` and is set by the potential callback to
 * a callee-allocated DLPack tensor.  After the call, the caller owns the
 * tensor and must free it via `rgpot_tensor_free`.
 *
 * # Fields
 *
 * - `forces`: output force tensor, same shape/dtype as `positions`.
 * - `energy`: the calculated potential energy.
 * - `variance`: uncertainty estimate; zero when not applicable.
 */
typedef struct rgpot_force_out_t {
  /**
   * Forces tensor `[n_atoms, 3]`, f64 — set by the callback.
   */
  DLManagedTensorVersioned *forces;
  /**
   * Calculated potential energy.
   */
  double energy;
  /**
   * Variance / uncertainty of the calculation (0.0 when unused).
   */
  double variance;
} rgpot_force_out_t;

/**
 * Function pointer type for a potential energy calculation.
 *
 * The callback receives:
 * - `user_data`: opaque pointer to the C++ object (e.g. `LJPot*`)
 * - `input`: the atomic configuration (DLPack tensors)
 * - `output`: the buffer for results (callback sets `forces` tensor)
 *
 * Returns `RGPOT_SUCCESS` on success, or an error status code.
 */
typedef enum rgpot_status_t (*PotentialCallback)(void *user_data,
                                                 const struct rgpot_force_input_t *input,
                                                 struct rgpot_force_out_t *output);

/**
 * Superset potential: embeds `eindir_objective_t` as its first member.
 *
 * Because `base` is first and both structs are `#[repr(C)]`, casting a
 * `*mut rgpot_potential_t` to `*mut eindir_objective_t` is defined behaviour
 * at the C ABI.  No conversion method is needed.
 */
typedef struct rgpot_potential_t {
  /**
   * Embedded eindir base: the IS-A relationship lives here.
   * MUST remain the first field.
   */
  eindir_objective_t base;
  /**
   * The underlying rgpot force/energy callback.
   */
  PotentialCallback callback;
  /**
   * Opaque context for `callback` (distinct from `base.user_data`).
   */
  void *pot_user_data;
  /**
   * Optional destructor for `pot_user_data`.
   */
  void (*pot_free_fn)(void*);
  /**
   * Atom count; set by `rgpot_potential_new_eindir`.
   */
  uintptr_t n_atoms;
  /**
   * Atomic numbers, heap-allocated (len = n_atoms), owned by this struct.
   */
  int32_t *atomic_numbers;
  /**
   * Box matrix (column-major, 3x3), copied at construction.
   */
  double box_matrix[9];
} rgpot_potential_t;

#if (defined(RGPOT_HAS_RPC) && defined(RGPOT_HAS_RPC))
/**
 * Opaque RPC client handle.
 */
typedef struct RpcClient rgpot_rpc_client_t;
#endif

/**
 * Create a non-owning 2-D f64 tensor on CPU wrapping an existing buffer.
 *
 * The returned tensor borrows `data` — the caller must keep `data` alive
 * for the lifetime of the tensor.  Call `rgpot_tensor_free` when done.
 *
 * # Safety
 * `data` must point to at least `rows * cols` contiguous `f64` values.
 */
DLManagedTensorVersioned *rgpot_tensor_cpu_f64_2d(double *data, int64_t rows, int64_t cols);

/**
 * Create a non-owning 1-D i32 tensor on CPU wrapping an existing buffer.
 *
 * # Safety
 * `data` must point to at least `len` contiguous `c_int` values.
 */
DLManagedTensorVersioned *rgpot_tensor_cpu_i32_1d(int *data, int64_t len);

/**
 * Create a non-owning 2-D f64 tensor on CPU for a 3x3 matrix.
 *
 * Convenience wrapper — equivalent to `rgpot_tensor_cpu_f64_2d(data, 3, 3)`.
 *
 * # Safety
 * `data` must point to at least 9 contiguous `f64` values.
 */
DLManagedTensorVersioned *rgpot_tensor_cpu_f64_matrix3(double *data);

/**
 * Create an **owning** 2-D f64 tensor on CPU by copying data.
 *
 * The returned tensor owns a copy of the data — the caller may free the
 * original buffer after this call.  Call `rgpot_tensor_free` when done.
 *
 * # Safety
 * `data` must point to at least `rows * cols` contiguous `f64` values.
 */
DLManagedTensorVersioned *rgpot_tensor_owned_cpu_f64_2d(const double *data,
                                                        int64_t rows,
                                                        int64_t cols);

/**
 * Free a DLPack tensor by invoking its deleter.
 *
 * If `tensor` is `NULL`, this is a no-op.
 *
 * # Safety
 * `tensor` must have been obtained from one of the `rgpot_tensor_*` creation
 * functions, or be a valid `DLManagedTensorVersioned` with a deleter.
 */
void rgpot_tensor_free(DLManagedTensorVersioned *tensor);

/**
 * Get the device of a DLPack tensor.
 *
 * # Safety
 * `tensor` must be a valid, non-null `DLManagedTensorVersioned*`.
 */
DLDevice rgpot_tensor_device(const DLManagedTensorVersioned *tensor);

/**
 * Get the raw data pointer of a DLPack tensor.
 *
 * # Safety
 * `tensor` must be a valid, non-null `DLManagedTensorVersioned*`.
 */
const void *rgpot_tensor_data(const DLManagedTensorVersioned *tensor);

/**
 * Get the shape array and number of dimensions of a DLPack tensor.
 *
 * Writes the number of dimensions to `*ndim_out` and returns a pointer to
 * the shape array (length `*ndim_out`).
 *
 * # Safety
 * Both `tensor` and `ndim_out` must be valid, non-null pointers.
 */
const int64_t *rgpot_tensor_shape(const DLManagedTensorVersioned *tensor, int32_t *ndim_out);

/**
 * Retrieve a pointer to the last error message for the current thread.
 *
 * The pointer is valid until the next call to any `rgpot_*` function
 * on the same thread.
 *
 * # Safety
 * This is intended to be called from C. The returned pointer must not
 * be freed by the caller.
 */
const char *rgpot_last_error(void);

/**
 * Create a potential that is ALSO a valid eindir objective.
 *
 * The returned pointer is simultaneously a `rgpot_potential_t*` and (via
 * zero-cost cast to the first member) an `eindir_objective_t*`.
 *
 * - `callback`: the rgpot force/energy callback.
 * - `user_data` / `free_fn`: opaque context for the rgpot callback.
 * - `n_atoms`, `atomic_numbers`, `box_matrix`: molecular context.
 * - `bounds_low`, `bounds_high`: f64 arrays of length `n_atoms * 3`
 *   (may be NULL; defaults to [-50, 50] per dimension).
 *
 * The caller must eventually pass the returned pointer to
 * [`rgpot_potential_free`].
 */
struct rgpot_potential_t *rgpot_potential_new_eindir(PotentialCallback callback,
                                                     void *user_data,
                                                     void (*free_fn)(void*),
                                                     uintptr_t n_atoms,
                                                     const int32_t *atomic_numbers,
                                                     const double *box_matrix,
                                                     const double *bounds_low,
                                                     const double *bounds_high);

/**
 * Free a potential created by [`rgpot_potential_new_eindir`].
 *
 * Calls `pot_free_fn(pot_user_data)` if provided, frees all owned arrays,
 * then frees the struct.  Do NOT call `eindir_objective_free` on the embedded
 * base; call this function.
 */
void rgpot_potential_free_eindir(struct rgpot_potential_t *pot);

/**
 * Create a `rgpot_force_input_t` from raw CPU arrays.
 *
 * Internally creates non-owning DLPack tensors wrapping the raw pointers.
 * The caller must call `rgpot_force_input_free` to free the tensor metadata
 * when done (this does NOT free the underlying data arrays).
 *
 * # Safety
 * All pointers must be valid for the lifetime of the returned struct.
 * `pos` must point to at least `n_atoms * 3` doubles.
 * `atmnrs` must point to at least `n_atoms` ints.
 * `box_` must point to at least 9 doubles.
 */
struct rgpot_force_input_t rgpot_force_input_create(uintptr_t n_atoms,
                                                    double *pos,
                                                    int *atmnrs,
                                                    double *box_);

/**
 * Free the DLPack tensor metadata in a `rgpot_force_input_t`.
 *
 * This frees the `DLManagedTensorVersioned` wrappers created by
 * `rgpot_force_input_create`, but does NOT free the underlying data arrays
 * (which are borrowed).
 *
 * After this call, all tensor pointers in `input` are set to NULL.
 *
 * If `input` is NULL, this is a no-op.
 *
 * # Safety
 * `input` must be NULL or point to a valid `rgpot_force_input_t` whose
 * tensors were created by `rgpot_force_input_create`.
 */
void rgpot_force_input_free(struct rgpot_force_input_t *input);

/**
 * Create a `rgpot_force_out_t` with null forces and zeroed scalars.
 *
 * The `forces` field starts as NULL — the potential callback is responsible
 * for setting it to a valid DLPack tensor.
 */
struct rgpot_force_out_t rgpot_force_out_create(void);

/**
 * Create a new potential handle from a callback function pointer.
 *
 * Creates a `rgpot_potential_t` with no molecular context (n_atoms = 0) and
 * no eindir bounds.  The potential evaluates through `callback` directly.
 * Use `rgpot_potential_new_eindir` when the IS-A eindir embedding is needed.
 *
 * Returns a heap-allocated `rgpot_potential_t*`, or `NULL` on failure.
 * The caller must eventually pass the returned pointer to `rgpot_potential_free`.
 */
struct rgpot_potential_t *rgpot_potential_new(PotentialCallback callback,
                                              void *user_data,
                                              void (*free_fn)(void*));

/**
 * Perform a force/energy calculation using the potential handle.
 *
 * Routes through the direct rgpot callback (`pot.callback`), bypassing the
 * eindir evaluation path.
 */
enum rgpot_status_t rgpot_potential_calculate(const struct rgpot_potential_t *pot,
                                              const struct rgpot_force_input_t *input,
                                              struct rgpot_force_out_t *output);

/**
 * Free a potential handle previously obtained from `rgpot_potential_new` or
 * `rgpot_potential_new_eindir`.
 *
 * Calls `pot_free_fn(pot_user_data)` if provided, frees all owned arrays,
 * then frees the struct.
 */
void rgpot_potential_free(struct rgpot_potential_t *pot);

#if (defined(RGPOT_HAS_RPC) && defined(RGPOT_HAS_RPC))
/**
 * Create a new RPC client connected to `host:port`.
 *
 * Returns a heap-allocated handle, or `NULL` on failure.
 * The caller must eventually call `rgpot_rpc_client_free`.
 */
rgpot_rpc_client_t *rgpot_rpc_client_new(const char *host, uint16_t port);
#endif

#if (defined(RGPOT_HAS_RPC) && defined(RGPOT_HAS_RPC))
/**
 * Perform a remote force/energy calculation.
 *
 * Returns `RGPOT_SUCCESS` on success, or an error status code.
 */
enum rgpot_status_t rgpot_rpc_calculate(rgpot_rpc_client_t *client,
                                        const struct rgpot_force_input_t *input,
                                        struct rgpot_force_out_t *output);
#endif

#if (defined(RGPOT_HAS_RPC) && defined(RGPOT_HAS_RPC))
/**
 * Free an RPC client handle.
 *
 * If `client` is `NULL`, this is a no-op.
 */
void rgpot_rpc_client_free(rgpot_rpc_client_t *client);
#endif

#if defined(RGPOT_HAS_RPC)
/**
 * Start an RPC server listening on `host:port`, dispatching to `pot`.
 *
 * This function blocks the current thread. It creates its own tokio runtime.
 *
 * # Safety
 * `pot` must be a valid pointer obtained from `rgpot_potential_new`.
 * The potential and its user_data must remain valid for the lifetime
 * of the server.
 */
enum rgpot_status_t rgpot_rpc_server_start(const struct rgpot_potential_t *pot,
                                           const char *host,
                                           uint16_t port);
#endif

#endif  /* RGPOT_H */
