#ifndef SOUFFLE_H
#define SOUFFLE_H
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C"
{
#endif

    // Opaque struct representing a Souffle program
    typedef struct souffle_interface souffle_t;
    // Opaque struct representing a Souffle relation
    typedef struct relation relation_t;
    // Opaque struct representing an iterator to a Souffle relation
    typedef struct relation_iterator relation_iterator_t;
    // Opaque struct representing a Souffle tuple (fact).
    typedef struct tuple tuple_t;

    /*
     * Initializes a Souffle program. The name of the program should be the
     * same as the filename (minus the .dl extension).
     *
     * The pointer that is returned can be NULL in case something went wrong.
     * If a valid pointer is returned, it needs to be freed by "souffle_free"
     * after it is no longer needed.
     */
    souffle_t *souffle_init(const char *progName);

    /*
     * Frees the memory in use by "program".
     * You need to check if the pointer is non-NULL before passing it to this
     * function. Not doing so results in undefined behavior.
     */
    void souffle_free(souffle_t *program);

    /*
     * Sets the number of cores this program should use.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     */
    void souffle_set_num_threads(souffle_t *program, size_t num_cores);

    /*
     * Gets the number of cores this program should use.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     * Returns the number of cores the program will use.
     */
    size_t souffle_get_num_threads(souffle_t *program);

    /*
     * Runs the Souffle program.
     * You need to check if the pointer is non-NULL before passing it to this
     * function. Not doing so results in undefined behavior.
     */
    void souffle_run(souffle_t *program);

    /*
     * Load all facts from files in a certain directory.
     * You need to check if both pointers are non-NULL before passing it to this
     * function. Not doing so results in undefined behavior.
     */
    void souffle_load_all(souffle_t *program, const char *input_directory);

    /*
     * Write out all facts of the program to CSV files
     * (as defined in the Souffle program).
     *
     * You need to check if the pointer is non-NULL before passing it to this
     * function. Not doing so results in undefined behavior.
     */
    void souffle_print_all(souffle_t *program, const char *output_directory);

    /*
     * Lookup a relation in the Souffle program.
     * You need to check if both passed pointers are non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * The returned pointer can be NULL if the relation is not found.
     * The pointer does not need to be freed, it is managed by the Souffle program.
     */
    relation_t *souffle_relation(souffle_t *program, const char *relation_name);

    /*
     * Gets the amount of tuples found in a relation.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Returns the amount of tuples found in a relation.
     */
    size_t souffle_relation_tuple_count(relation_t *relation);

    /*
     * Create an iterator for iterating over the facts of a relation.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * The returned pointer needs to be freed up with
     * "souffle_relation_iterator_free" after it is no longer needed.
     */
    relation_iterator_t *souffle_relation_iterator(relation_t *relation);

    /*
     * Frees a relation_iterator pointer.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     */
    void souffle_relation_iterator_free(relation_iterator_t *iterator);

    /*
     * Advances the relation iterator by 1 position.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     * Calling this function when there are no more tuples to be returned
     * will result in a crash.
     *
     * Returns a pointer to the next record. This pointer is not allowed to be freed.
     */
    tuple_t *souffle_relation_iterator_next(relation_iterator_t *iterator);

    /*
     * Checks if a relation contains a certain tuple.
     * You need to check if the passed pointers are non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Returns true if the tuple was found in the relation; otherwise false.
     */
    bool souffle_contains_tuple(relation_t *relation, tuple_t *tuple);

    /*
     * Allocates memory for a tuple to be added to a relation.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Returns a pointer to a new tuple. Use "souffle_tuple_free" when tuple
     * is no longer required.
     */
    tuple_t *souffle_tuple_alloc(relation_t *relation);

    /*
     * Frees memory of a tuple that was previously allocated.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     */
    void souffle_tuple_free(tuple_t *tuple);

    /*
     * Adds a tuple to a relation.
     * You need to check if both passed pointers are non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     */
    void souffle_tuple_add(relation_t *relation, tuple_t *tuple);

    /*
     * Pushes a 32 bit signed integer value into a tuple.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Pushing an integer value onto a tuple that expects another type results
     * in a crash.
     */
    void souffle_tuple_push_int32(tuple_t *tuple, int32_t value);

    /*
     * Pushes a 32 bit unsigned integer value into a tuple.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Pushing an integer value onto a tuple that expects another type results
     * in a crash.
     */
    void souffle_tuple_push_uint32(tuple_t *tuple, uint32_t value);

    /*
     * Pushes a float value into a tuple.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Pushing a float value onto a tuple that expects another type results
     * in a crash.
     */
    void souffle_tuple_push_float(tuple_t *tuple, float value);

    /*
     * Pushes a string value into a tuple.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Pushing a string value onto a tuple that expects another type results
     * in a crash.
     */
    void souffle_tuple_push_string(tuple_t *tuple, const char *value);

    /*
     * Extracts a 32 bit signed integer value from a tuple.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Extracting an integer value from a tuple that expects another type results
     * in a crash.
     * The popped integer will be stored in the result pointer.
     */
    void souffle_tuple_pop_int32(tuple_t *tuple, int32_t *result);

    /*
     * Extracts a 32 bit unsigned integer value from a tuple.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Extracting an integer value from a tuple that expects another type results
     * in a crash.
     * The popped integer will be stored in the result pointer.
     */
    void souffle_tuple_pop_uint32(tuple_t *tuple, uint32_t *result);

    /*
     * Extracts a float value from a tuple.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Extracting a float value from a tuple that expects another type results
     * in a crash.
     * The popped integer will be stored in the result pointer.
     */
    void souffle_tuple_pop_float(tuple_t *tuple, float *result);

    /*
     * Extracts a string value from a tuple.
     * You need to check if the passed pointer is non-NULL before passing it
     * to this function. Not doing so results in undefined behavior.
     *
     * Extracting a string value from a tuple that expects another type results
     * in a crash.
     * The popped string will be stored in the result pointer.
     */
    void souffle_tuple_pop_string(tuple_t *tuple, char **result);

#ifdef __cplusplus
}
#endif

#endif