/*
 * Copyright (C) 2022-2025 Seagull Developers
 *
 * Seagull is the legal property of its developers, whose names are too
 * numerous to list here. Please refer to the AUTHORS file distributed
 * with this source distribution
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#if !defined(SEAGULL_GLOBAL_HEADER_INSIDE) && !defined(SEAGULL_COMPILATION)
# error "only <seagull.h> may be included directly"
#endif

#ifndef SEAGULL_STATEMENT_H
#define SEAGULL_STATEMENT_H

#include <glib.h>
#include <gio/gio.h>

#include "seagulltypes.h"
#include "seagullversion.h"

G_BEGIN_DECLS

#define SEAGULL_STATEMENT_ERROR seagull_statement_error_quark()

/**
 * SeagullStatementError:
 * @SEAGULL_STATEMENT_ERROR_NO_DB_PROVIDED: The db property was not specified.
 * @SEAGULL_STATEMENT_ERROR_NO_SQL_PROVIDED: The sql property was not
 *  specified.
 * @SEAGULL_STATEMENT_ERROR_PREPARATION_FAILED: The statement failed to
 *  prepare.
 * @SEAGULL_STATEMENT_ERROR_MORE_THAN_ONE_STATEMENT: More than one SQL
 *  statement was provided.
 * @SEAGULL_STATEMENT_ERROR_BIND_PARAMETER_NOT_FOUND: The named column was not
 *  found.
 * @SEAGULL_STATEMENT_ERROR_BIND_FAILED: Binding a parameter failed.
 * @SEAGULL_STATEMENT_ERROR_CLEAR_BINDINGS_FAILED: Clearing the bindings
 *  failed.
 * @SEAGULL_STATEMENT_ERROR_RESET_FAILED: Resetting the statement failed.
 * @SEAGULL_STATEMENT_ERROR_STEP_FAILED: Stepping the statement failed.
 * @SEAGULL_STATEMENT_ERROR_WRONG_COLUMN_TYPE: The requested column was not of
 *  the requested type.
 *
 * Error codes returned by statements.
 *
 * Since: 0.3
 */
typedef enum {
	SEAGULL_STATEMENT_ERROR_NO_DB_PROVIDED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
	SEAGULL_STATEMENT_ERROR_NO_SQL_PROVIDED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
	SEAGULL_STATEMENT_ERROR_PREPARATION_FAILED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
	SEAGULL_STATEMENT_ERROR_MORE_THAN_ONE_STATEMENT SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
	SEAGULL_STATEMENT_ERROR_BIND_PARAMETER_NOT_FOUND SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
	SEAGULL_STATEMENT_ERROR_BIND_FAILED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
	SEAGULL_STATEMENT_ERROR_CLEAR_BINDINGS_FAILED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
	SEAGULL_STATEMENT_ERROR_RESET_FAILED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
	SEAGULL_STATEMENT_ERROR_STEP_FAILED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
	SEAGULL_STATEMENT_ERROR_WRONG_COLUMN_TYPE SEAGULL_AVAILABLE_ENUMERATOR_IN_0_3,
} SeagullStatementError;

/**
 * seagull_statement_error_quark:
 *
 * The error domain to identify errors with statements.
 *
 * Returns: The error domain.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
GQuark seagull_statement_error_quark(void);

#define SEAGULL_TYPE_STATEMENT (seagull_statement_get_type())

/**
 * SeagullStatement:
 *
 * A wrapper around sqlite3_stmt to make it easier to work with named
 * parameters.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
G_DECLARE_FINAL_TYPE(SeagullStatement, seagull_statement, SEAGULL, STATEMENT,
                     GObject)

/**
 * seagull_statement_bind_double:
 * @name: the name of the parameter
 * @value: the value.
 * @error: (out) (optional): a return address for a #GError
 *
 * Binds a double value to the named parameter.
 *
 * @name must be the exact name as specified in the query.
 *
 * Returns: true on success; otherwise false with error set.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
gboolean seagull_statement_bind_double(SeagullStatement *statement, const char *name, double value, GError **error);

/**
 * seagull_statement_bind_int:
 * @name: the name of the parameter
 * @value: the value.
 * @error: (out) (optional): a return address for a #GError
 *
 * Binds an integer value to the named parameter.
 *
 * @name must be the exact name as specified in the query.
 *
 * Returns: true on success; otherwise false with error set.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
gboolean seagull_statement_bind_int(SeagullStatement *statement, const char *name, int value, GError **error);

/**
 * seagull_statement_bind_null:
 * @name: the name of the parameter
 * @error: (out) (optional): a return address for a #GError
 *
 * Binds a NULL value to the named parameter.
 *
 * @name must be the exact name as specified in the query.
 *
 * Returns: true on success; otherwise false with error set.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
gboolean seagull_statement_bind_null(SeagullStatement *statement, const char *name, GError **error);

/**
 * seagull_statement_bind_text:
 * @name: the name of the parameter
 * @value: the value.
 * @len: the length of the value in bytes, or -1 for NULL terminated
 * @destroy_notify: (nullable): a function to be called to free the value
 * @error: (out) (optional): a return address for a #GError
 *
 * Binds a text value to the named parameter.
 *
 * @name must be the exact name as specified in the query.
 *
 * Returns: true on success; otherwise false with error set.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
gboolean seagull_statement_bind_text(SeagullStatement *statement, const char *name, const char *value, int len, GDestroyNotify destroy_notify, GError **error);

/**
 * seagull_statement_clear_bindings:
 * @error: (out) (optional): a return address for a #GError
 *
 * Clears all of the bindings.
 *
 * Returns: true on success.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
gboolean seagull_statement_clear_bindings(SeagullStatement *statement, GError **error);

/**
 * seagull_statement_column_double:
 * @name: the name of the column
 * @error: (out) (optional): a return address for a #GError
 *
 * Gets the value of a column from the current result of a statement.
 *
 * @name must be the exact name or alias of the column as specified in the
 * query.
 *
 * Returns: The value on success or 0 on error with error set.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
double seagull_statement_column_double(SeagullStatement *statement, const char *name, GError **error);

/**
 * seagull_statement_column_int:
 * @name: the name of the column
 * @error: (out) (optional): a return address for a #GError
 *
 * Gets the value of a column from the current result of a statement.
 *
 * @name must be the exact name or alias of the column as specified in the
 * query.
 *
 * Returns: The value on success or 0 on error with error set.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
int seagull_statement_column_int(SeagullStatement *statement, const char *name, GError **error);

/**
 * seagull_statement_column_text:
 * @name: the name of the column
 * @error: (out) (optional): a return address for a #GError
 *
 * Gets the value of a column from the current result of a statement.
 *
 * @name must be the exact name or alias of the column as specified in the
 * query.
 *
 * Returns: (nullable): The value on success or null on error with error set.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
const char *seagull_statement_column_text(SeagullStatement *statement, const char *name, GError **error);

/**
 * seagull_statement_get_db:
 *
 * Gets the database.
 *
 * Returns: (transfer none): The database.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
SeagullSqlite3 *seagull_statement_get_db(SeagullStatement *statement);

/**
 * seagull_statement_get_expanded_sql:
 *
 * Gets the SQL statement with parameters expanded.
 *
 * If all parameters have not yet been bound, they will appears as their names.
 *
 * Returns: (transfer full): The expanded SQL statement which should be freed
 *          by calling [func@free].
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
char *seagull_statement_get_expanded_sql(SeagullStatement *statement);

/**
 * seagull_statement_get_sql:
 *
 * Gets the SQL statement.
 *
 * Returns: (transfer none): The SQL statement.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
const char *seagull_statement_get_sql(SeagullStatement *statement);

/**
 * seagull_statement_get_stmt:
 *
 * Gets the underlying SQLite3 statement.
 *
 * Returns: (transfer none): The statement.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
SeagullSqlite3Stmt *seagull_statement_get_stmt(SeagullStatement *statement);

/**
 * seagull_statement_new:
 * @db: the database instance
 * @sql: the SQL statement
 * @error: (out) (nullable): a return address for a #GError
 *
 * Creates a new prepared statement.
 *
 * Returns: (transfer full): The new instance.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
SeagullStatement *seagull_statement_new(SeagullSqlite3 *db, const char *sql, GError **error);

/**
 * seagull_statement_reset:
 * @error: (out) (optional): a return address for a #GError
 *
 * Resets the statement to its initial state.
 *
 * This does not clear any bindings, use [method@Statement.clear_bindings] to
 * do that.
 *
 * Returns: true on success; other false with @error set.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
gboolean seagull_statement_reset(SeagullStatement *statement, GError **error);

/**
 * seagull_statement_step:
 * @error: (out) (optional): a return address for a #GError
 *
 * Evaluates the statement.
 *
 * If there is no data to read, %false will be returned, but @error will not be
 * set.
 *
 * Returns: true if there is data to read.
 *
 * Since: 0.3
 */
SEAGULL_AVAILABLE_IN_0_3
gboolean seagull_statement_step(SeagullStatement *statement, GError **error);

G_END_DECLS

#endif /* SEAGULL_STATEMENT_H */
