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

typedef enum VerifyResult {
  Ok,
  Invalid,
  InvalidArgument,
} VerifyResult;

typedef struct ArrayU8 {
  uint8_t *ptr;
  uintptr_t len;
  uintptr_t cap;
} ArrayU8;

typedef struct Proof {
  uint32_t nonce;
  struct ArrayU8 indices;
  uint64_t k2_pow;
  uint64_t k3_pow;
} Proof;

/**
 * Scrypt algorithm parameters.
 *
 * Refer to <https://www.rfc-editor.org/rfc/rfc7914#section-2>
 */
typedef struct ScryptParams {
  /**
   * N = 1 << (nfactor + 1)
   */
  uint8_t nfactor;
  /**
   * r = 1 << rfactor
   */
  uint8_t rfactor;
  /**
   * p = 1 << pfactor
   */
  uint8_t pfactor;
} ScryptParams;

typedef struct Config {
  /**
   * K1 specifies the difficulty for a label to be a candidate for a proof.
   */
  uint32_t k1;
  /**
   * K2 is the number of labels below the required difficulty required for a proof.
   */
  uint32_t k2;
  /**
   * K3 is the size of the subset of proof indices that is validated.
   */
  uint32_t k3;
  /**
   * Difficulty for K2 proof of work. Lower values increase difficulty of finding
   * `k2_pow` for [Proof][crate::prove::Proof].
   */
  uint64_t k2_pow_difficulty;
  /**
   * Difficulty for K3 proof of work. Lower values increase difficulty of finding
   * `k3_pow` for [Proof][crate::prove::Proof].
   */
  uint64_t k3_pow_difficulty;
  /**
   * Scrypt parameters for the Proofs of Work
   */
  struct ScryptParams pow_scrypt;
  /**
   * Scrypt paramters for initilizing labels
   */
  struct ScryptParams scrypt;
} Config;

typedef struct ProofMetadata {
  uint8_t node_id[32];
  uint8_t commitment_atx_id[32];
  uint8_t challenge[32];
  uint32_t num_units;
  uint64_t labels_per_unit;
} ProofMetadata;

/**
 * Deallocate a proof obtained with generate_proof().
 * # Safety
 * `proof` must be a pointer to a Proof struct obtained with generate_proof().
 */
void free_proof(struct Proof *proof);

/**
 * Generates a proof of space for the given challenge using the provided parameters.
 * Returns a pointer to a Proof struct which should be freed with free_proof() after use.
 * If an error occurs, prints it on stderr and returns null.
 * # Safety
 * `challenge` must be a 32-byte array.
 */
struct Proof *generate_proof(const char *datadir,
                             const unsigned char *challenge,
                             struct Config cfg,
                             uintptr_t nonces,
                             uintptr_t threads);

/**
 * Verify a proof
 *
 * # Safety
 * `metadata` must be initialized and properly aligned.
 */
enum VerifyResult verify_proof(struct Proof proof,
                               const struct ProofMetadata *metadata,
                               struct Config cfg,
                               uintptr_t threads);
