/*
 * This file is a part of the CaosDB Project.
 * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
 * Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@indiscale.com>
 * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 *
 */
#ifdef __cplusplus
extern "C" {
#endif

/**
 * Return the constant caosdb::LIBCAOSDB_VERSION_MAJOR.
 */
int caosdb_constants_LIBCAOSDB_VERSION_MAJOR();
/**
 * Return the constant caosdb::LIBCAOSDB_VERSION_MINOR
 */
int caosdb_constants_LIBCAOSDB_VERSION_MINOR();
/**
 * Return the constant caosdb::LIBCAOSDB_VERSION_PATCH.
 */
int caosdb_constants_LIBCAOSDB_VERSION_PATCH();
/**
 * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_MAJOR.
 */
int caosdb_constants_COMPATIBLE_SERVER_VERSION_MAJOR();
/**
 * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_MINOR.
 */
int caosdb_constants_COMPATIBLE_SERVER_VERSION_MINOR();
/**
 * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_PATCH.
 */
int caosdb_constants_COMPATIBLE_SERVER_VERSION_PATCH();
/**
 * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_PRE_RELEASE.
 */
const char *caosdb_constants_COMPATIBLE_SERVER_VERSION_PRE_RELEASE();

/**
 * Return the status code reserved for errors in clients wrapping this
 * interface.
 */
int caosdb_status_code_OTHER_CLIENT_ERROR();

/**
 * A wrapper of the C++ Connection class.
 *
 * We use a wrapper for future extensibility and in order to have a minimal
 * capability for type checking in C even though the C++ class
 * Connection is opaque in C.
 */
typedef struct {
  void *wrapped_connection;
  bool _deletable = false;
} caosdb_connection_connection;

/**
 * A wrapper of the C++ ConnectionConfiguration class.
 *
 * We use a wrapper for future extensibility and in order to have a minimal
 * capability for type checking in C even though the C++ class
 * Connection is opaque in C.
 */
typedef struct {
  void *wrapped_connection_configuration;
  bool _deletable = false;
} caosdb_connection_connection_configuration;

/**
 * A wrapper of the C++ VersionInfo class.
 *
 * We use a wrapper for future extensibility and in order to have a minimal
 * capability for type checking in C even though the C++ class
 * Connection is opaque in C.
 */
typedef struct {
  int major;
  int minor;
  int patch;
  const char *pre_release;
  const char *build;
} caosdb_info_version_info;

typedef struct {
  void *wrapped_certificate_provider;
  bool _deletable = false;
} caosdb_connection_certificate_provider;

typedef struct {
  void *wrapped_authenticator;
  bool _deletable = false;
} caosdb_authentication_authenticator;

/**
 * Return the environment variable of the given name.
 *
 * If the environment variable is not set, return the fallback instead.
 */
const char *caosdb_utility_get_env_var(const char *name, const char *fallback);

/**
 * Return a description of the status code.
 */
const char *caosdb_get_status_description(int code);

/**
 * Create a pem-file certificate provider.
 *
 * Use the destructor function
 * `caosdb_connection_delete_certificate_provider` to free the wrapped
 * provider.
 *
 * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
 * Only use it when you know what you are doing.
 */
int caosdb_connection_create_pem_file_certificate_provider(
  caosdb_connection_certificate_provider *out, const char *path);

/**
 * Destructor function for a certificate provider.
 *
 * EXPERT USE ONLY.  Only use it when you know what you are doing.
 */
int caosdb_connection_delete_certificate_provider(
  caosdb_connection_certificate_provider *provider);

/**
 * Create a tls-secured connection configuration.
 *
 * The configuration is needed to instantiate a connection.
 *
 * Use the destructor function
 * `caosdb_connection_delete_connection_configuration` to free the wrapped
 * configuration.
 *
 * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
 * Only use it when you know what you are doing.
 */
int caosdb_connection_create_tls_connection_configuration(
  caosdb_connection_connection_configuration *out, const char *host,
  const int port, caosdb_authentication_authenticator *authenticator,
  caosdb_connection_certificate_provider *provider);

/**
 * Create a tls-secured connection configuration.
 *
 * The configuration is needed to instantiate a connection.
 *
 * Use `caosdb_connection_create_tls_connection_configuration` for a
 * tls-secured connection which also supports authentication.
 *
 * Use the destructor function
 * `caosdb_connection_delete_connection_configuration` to free the wrapped
 * configuration.
 *
 * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
 * Only use it when you know what you are doing.
 */
int caosdb_connection_create_insecure_connection_configuration(
  caosdb_connection_connection_configuration *out, const char *host,
  const int port);

/**
 * Destructor function for the caosdb_connection_connection_configuration
 * struct.
 *
 * EXPERT USE ONLY.  Only use it when you know what you are doing.
 */
int caosdb_connection_delete_connection_configuration(
  caosdb_connection_connection_configuration *configuration);

/**
 * Add a public certificate of a trusted certificate authority to an
 * existing, tls-enabled connection configuration.
 *
 * @param cacert path to a pem-file.
 */
int caosdb_connection_configuration_add_cacert(
  caosdb_connection_connection_configuration *configuration,
  const char *cacert);

/**
 * Create a plain password authenticator.
 *
 * Use the destructor function
 * `caosdb_authentication_delete_authenticator` to free the wrapped
 * authenticator.
 *
 * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
 * Only use it when you know what you are doing.
 */
int caosdb_authentication_create_plain_password_authenticator(
  caosdb_authentication_authenticator *out, const char *username,
  const char *password);

/**
 * Destructor function for the caosdb_authentication_authenticator struct.
 *
 * EXPERT USE ONLY.  Only use it when you know what you are doing.
 */
int caosdb_authentication_delete_authenticator(
  caosdb_authentication_authenticator *authenticator);

/**
 * Create a connection instance.
 *
 * The connection is needed to create transactions and to initiate any other
 * interaction with a CaosDB server.
 *
 * Use the destructor function
 * `caosdb_connection_delete_connection` to free the wrapped
 * connection.
 *
 * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
 * Only use it when you know what you are doing.
 */
int caosdb_connection_create_connection(
  caosdb_connection_connection *out,
  const caosdb_connection_connection_configuration *configuration);

/**
 * Destructor function for the caosdb_connection_connection struct.
 *
 * EXPERT USE ONLY.  Only use it when you know what you are doing.
 */
int caosdb_connection_delete_connection(
  caosdb_connection_connection *connection);

/**
 * Request the version of the server.
 */
int caosdb_connection_get_version_info(
  caosdb_info_version_info *out,
  const caosdb_connection_connection *connection);

/**
 * Get the default connection from the ConnectionManager.
 *
 * The default connection is to be specified in a configuration file.
 */
int caosdb_connection_connection_manager_get_default_connection(
  caosdb_connection_connection *out);

/**
 * Get a named connection from the ConnectionManager.
 *
 * The named connection is to be specified in a configuration file.
 */
int caosdb_connection_connection_manager_get_connection(
  caosdb_connection_connection *out, const char *name);

/****************************************************************************
 * ENTITY STUFF AND TRANSACTIONS
 ****************************************************************************/

// TODO(fspreck) implementations needed, and probably these declarations are
// not sufficient yet.
typedef struct {
  void *wrapped_transaction;
  bool _deletable = false;
} caosdb_transaction_transaction;

/**
 * Create a transaction on an existing connection.
 *
 * This transaction has to be deleted manually by
 * caosdb_transaction_delete_transaction() later on.
 */
int caosdb_connection_connection_create_transaction(
  caosdb_connection_connection *connection,
  caosdb_transaction_transaction *out);
int caosdb_transaction_delete_transaction(
  caosdb_transaction_transaction *transaction);
int caosdb_transaction_transaction_retrieve_by_id(
  caosdb_transaction_transaction *transaction, const char *id);
int caosdb_transaction_transaction_retrieve_by_ids(
  caosdb_transaction_transaction *transaction, const char *ids[], int length);
int caosdb_transaction_transaction_query(
  caosdb_transaction_transaction *transaction, const char *query);
int caosdb_transaction_transaction_execute(
  caosdb_transaction_transaction *transaction);
// TODO(fspreck) execute_asynchronously may be added as a separate
// function once we actually support asynchronous execution.

typedef struct {
  void *wrapped_result_set;
  bool _deletable = false;
} caosdb_transaction_result_set;

int caosdb_transaction_transaction_get_result_set(
  caosdb_transaction_transaction *transaction,
  caosdb_transaction_result_set *out);

int caosdb_transaction_transaction_get_count_result(
  caosdb_transaction_transaction *transaction, long *out);

typedef struct {
  void *wrapped_entity;
  bool _deletable = false;
} caosdb_entity_entity;

int caosdb_transaction_result_set_at(caosdb_transaction_result_set *result_set,
                                     caosdb_entity_entity *entity, int index);
int caosdb_transaction_result_set_size(
  caosdb_transaction_result_set *result_set, int *out);

int caosdb_transaction_transaction_insert_entity(
  caosdb_transaction_transaction *transaction, caosdb_entity_entity *entity);
int caosdb_transaction_transaction_update_entity(
  caosdb_transaction_transaction *transaction, caosdb_entity_entity *entity);
int caosdb_transaction_transaction_delete_by_id(
  caosdb_transaction_transaction *transaction, const char *id);

typedef struct {
  void *wrapped_property;
  bool _deletable = false;
} caosdb_entity_property;
typedef struct {
  void *wrapped_parent;
  bool _deletable = false;
} caosdb_entity_parent;
typedef struct {
  void *wrapped_message;
  bool _deletable = false;
} caosdb_entity_message;

// GETTERS FOR EVERYTHING
int caosdb_entity_entity_get_id(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_role(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_name(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_description(caosdb_entity_entity *entity,
                                         char *out);
int caosdb_entity_entity_get_datatype(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_unit(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_value(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_version_id(caosdb_entity_entity *entity,
                                        char *out);
int caosdb_entity_entity_get_errors_size(caosdb_entity_entity *entity,
                                         int *out);
int caosdb_entity_entity_get_error(caosdb_entity_entity *entity,
                                   caosdb_entity_message *out, int index);
int caosdb_entity_entity_get_warnings_size(caosdb_entity_entity *entity,
                                           int *out);
int caosdb_entity_entity_get_warning(caosdb_entity_entity *entity,
                                     caosdb_entity_message *out, int index);
int caosdb_entity_entity_get_infos_size(caosdb_entity_entity *entity, int *out);
int caosdb_entity_entity_get_info(caosdb_entity_entity *entity,
                                  caosdb_entity_message *out, int index);
int caosdb_entity_entity_get_properties_size(caosdb_entity_entity *entity,
                                             int *out);
int caosdb_entity_entity_get_property(caosdb_entity_entity *entity,
                                      caosdb_entity_property *out, int index);
int caosdb_entity_entity_get_parents_size(caosdb_entity_entity *entity,
                                          int *out);
int caosdb_entity_entity_get_parent(caosdb_entity_entity *entity,
                                    caosdb_entity_parent *out, int index);

int caosdb_entity_property_get_id(caosdb_entity_property *property, char *out);
int caosdb_entity_property_get_name(caosdb_entity_property *property,
                                    char *out);
int caosdb_entity_property_get_description(caosdb_entity_property *property,
                                           char *out);
int caosdb_entity_property_get_importance(caosdb_entity_property *property,
                                          char *out);
int caosdb_entity_property_get_datatype(caosdb_entity_property *property,
                                        char *out);
int caosdb_entity_property_get_unit(caosdb_entity_property *property,
                                    char *out);
int caosdb_entity_property_get_value(caosdb_entity_property *property,
                                     char *out);

int caosdb_entity_parent_get_id(caosdb_entity_parent *parent, char *out);
int caosdb_entity_parent_get_name(caosdb_entity_parent *parent, char *out);
int caosdb_entity_parent_get_description(caosdb_entity_parent *parent,
                                         char *out);

int caosdb_entity_message_get_code(caosdb_entity_message *message, int *out);
int caosdb_entity_message_get_description(caosdb_entity_message *message,
                                          char *out);

// CONSTRUCTORS AND DESTRUCTORS
int caosdb_entity_create_entity(caosdb_entity_entity *out);
int caosdb_entity_delete_entity(caosdb_entity_entity *out);
int caosdb_entity_create_property(caosdb_entity_property *out);
int caosdb_entity_delete_property(caosdb_entity_property *out);
int caosdb_entity_create_parent(caosdb_entity_parent *out);
int caosdb_entity_delete_parent(caosdb_entity_parent *out);

// SETTERS FOR EVERYTHING THAT MAY BE SET
int caosdb_entity_entity_set_role(caosdb_entity_entity *entity,
                                  const char *role);
int caosdb_entity_entity_set_name(caosdb_entity_entity *entity,
                                  const char *name);
int caosdb_entity_entity_set_description(caosdb_entity_entity *entity,
                                         const char *description);
int caosdb_entity_entity_set_datatype(caosdb_entity_entity *entity,
                                      const char *datatype);
int caosdb_entity_entity_set_unit(caosdb_entity_entity *entity,
                                  const char *unit);
int caosdb_entity_entity_set_value(caosdb_entity_entity *entity,
                                   const char *value);
int caosdb_entity_entity_append_parent(caosdb_entity_entity *entity,
                                       caosdb_entity_parent *parent);
int caosdb_entity_entity_remove_parent(caosdb_entity_entity *entity, int index);
int caosdb_entity_entity_append_property(caosdb_entity_entity *entity,
                                         caosdb_entity_property *property);
int caosdb_entity_entity_remove_property(caosdb_entity_entity *entity,
                                         int index);

int caosdb_entity_property_set_id(caosdb_entity_property *property,
                                  const char *id);
int caosdb_entity_property_set_name(caosdb_entity_property *property,
                                    const char *name);
int caosdb_entity_property_set_datatype(caosdb_entity_property *property,
                                        const char *datatype);
int caosdb_entity_property_set_importance(caosdb_entity_property *property,
                                          const char *importance);
int caosdb_entity_property_set_unit(caosdb_entity_property *property,
                                    const char *unit);
int caosdb_entity_property_set_value(caosdb_entity_property *property,
                                     const char *value);

int caosdb_entity_parent_set_id(caosdb_entity_parent *parent, const char *id);
int caosdb_entity_parent_set_name(caosdb_entity_parent *parent,
                                  const char *name);

#ifdef __cplusplus
}
#endif