#include "scheme48.h"

#include <stdio.h>

/* ODBC header files */
#include <sql.h>
#include <sqlext.h>

#define ERROR_MSG_BUFFER_LEN          255
#define ODBC_MAX_DRIVER_NAME_LEN      255
#define ODBC_GET_INFO_MAX_LEN         255
#define ODBC_GET_CONNECT_ATTR_MAX_LEN 255
#define ODBC_GET_STMT_ATTR_MAX_LEN    255
#define ODBC_GET_DATA_MAX_STR_LEN     255

#define ODBC_DEBUG_MSGS            1

#define ODBC_RAISE_EXCEPTION(MSG)  s48_raise_string_os_error(MSG)

#ifdef ODBC_DEBUG_MSGS
#define ODBC_DEBUG_DIAGREC(ht, h) odbc_debug_msgs(ht, h);
#else
#define ODBC_DEBUG_DIAGREC(ht, h) ;
#endif

#ifdef ODBC_DEBUG_MSGS
#define ODBC_DEBUG_PRINTF(str) printf(str);
#define ODBC_DEBUG_PRINTF_ARG1(str, arg) printf(str, arg);
#define ODBC_DEBUG_PRINTF_ARG2(str, arg1, arg2) printf(str, arg1, arg2);
#else
#define ODBC_DEBUG_PRINTF(str) ;
#define ODBC_DEBUG_PRINTF_ARG1(str, arg) ;
#define ODBC_DEBUG_PRINTF_ARG2(str, arg1, arg2) ;
#endif

/* offsets for scheme records */

/* corresponds to sql-date */
static s48_value sql_date_record_type = S48_FALSE;

#define SR_SQL_DATE_YEAR         0
#define SR_SQL_DATE_MONTH        1
#define SR_SQL_DATE_DAY          2

/* corresponds to sql-time */
static s48_value sql_time_record_type = S48_FALSE;

#define SR_SQL_TIME_HOUR         0
#define SR_SQL_TIME_MINUTE       1
#define SR_SQL_TIME_SECOND       2

/* corresponds to sql-timestamp */
static s48_value sql_timestamp_record_type = S48_FALSE;

#define SR_SQL_TIMESTAMP_YEAR     0
#define SR_SQL_TIMESTAMP_MONTH    1
#define SR_SQL_TIMESTAMP_DAY      2
#define SR_SQL_TIMESTAMP_HOUR     3
#define SR_SQL_TIMESTAMP_MINUTE   4
#define SR_SQL_TIMESTAMP_SECOND   5
#define SR_SQL_TIMESTAMP_FRACTION 6

/* corresponds to sql-numeric */
#define SR_SQL_NUMERIC_PRECISION 0
#define SR_SQL_NUMERIC_SCALE     1
#define SR_SQL_NUMERIC_SIGN      2
#define SR_SQL_NUMERIC_VALUE     3

/*
 *
 * PART 1
 *
 * Connecting to a data source
 *
 */

/* Call SQLAllocHandle and get an environment handle. After that
 * call odbc_set_environment to set the ODBC version */
s48_value odbc_alloc_environment_handle();

/* given a valid environment handle (type SQLHENV) this function
 * sets the environment attributes. This needs to be done before
 * allocating a connection handle */
void odbc_sql_set_env_attr(SQLHENV env_handle);

/* Given a valid environment handle get a connection handle */
s48_value odbc_alloc_connection_handle(s48_value env_handle);

/* Given a valid connection handle get a statement handle */
s48_value odbc_alloc_statement_handle(s48_value db_handle);

/* Connect to a server */
s48_value odbc_sql_connect(s48_value connection_handle,
									s48_value server_name,
									s48_value user_name,
									s48_value authentication);

/*
 *
 * PART 2
 *
 * Obtaining information about a driver and data source
 *
 */

/* Returns a list of available data sources. */
s48_value odbc_sql_data_sources(s48_value env_handle, s48_value direction);

/* Returns the list of installed drivers and their attributes. */
s48_value odbc_sql_drivers(s48_value env_handle);

/* Returns information about a specific driver and data source. 
 * (use if the information is an integer) */
s48_value odbc_sql_get_info_int(s48_value conn_handle, s48_value info_key);

/* Returns information about a specific driver and data source. 
 * (use if the information is a string) */
s48_value odbc_sql_get_info_string(s48_value conn_handle, s48_value info_key);

/* Returns supported driver functions. */
s48_value odbc_sql_get_func_exists(s48_value conn_handle, s48_value fun_id);

/* Returns information about supported data types. */
s48_value odbc_sql_get_type_info(s48_value stmt_handle, s48_value data_type);

/*
 *
 * PART 3
 *
 * Setting and retrieving driver attributes
 *
 */

s48_value odbc_sql_set_connect_attr_int(s48_value conn_handle,
													 s48_value attribute,
													 s48_value value);

s48_value odbc_sql_set_connect_attr_string(s48_value conn_handle,
														 s48_value attribute,
														 s48_value value);

s48_value odbc_sql_get_connect_attr_string(s48_value conn_handle,
														 s48_value attribute);

s48_value odbc_sql_get_connect_attr_int(s48_value conn_handle,
													 s48_value attribute);

s48_value odbc_sql_set_env_attr_int(s48_value env_handle,
												s48_value attribute,
												s48_value value);

s48_value odbc_sql_get_env_attr_int(s48_value env_handle,
												s48_value attribute,
												s48_value value);

s48_value odbc_sql_set_stmt_attr_int(s48_value stmt_handle,
												 s48_value attribute,
												 s48_value value);

s48_value odbc_sql_set_stmt_attr_string(s48_value stmt_handle,
													 s48_value attribute,
													 s48_value value);

s48_value odbc_sql_get_stmt_attr_int(s48_value stmt_handle, 
												 s48_value attribute);

s48_value odbc_sql_get_stmt_attr_string(s48_value stmt_handle,
													 s48_value attribute);

/*
 *
 * part 4
 *
 * Setting and retrieving descriptor fields
 *
 */


/*
 *
 * PART 5
 *
 * Preparing SQL requests
 *
 */

/* Prepare a SQL statement for execution */
s48_value odbc_sql_prepare(s48_value stmt_handle, s48_value stmt_txt);

s48_value odbc_sql_bind_parameter_exec_out(s48_value stmt_handle,
														 s48_value param_vals);

/*
 *
 * PART 6
 *
 * Submitting requests
 *
 */
s48_value odbc_sql_execute(s48_value stmt_handle);

s48_value odbc_sql_execute_direct(s48_value stmt_handle, s48_value stmt);

/*
 *
 * PART 7
 *
 * Retrieving results and information about results
 *
 */

s48_value odbc_sql_get_data(s48_value stmt_handle,
									 s48_value column_number,
									 s48_value target_type);

void check_sql_get_data_result(SQLRETURN retval, SQLHSTMT stmt_handle);

s48_value odbc_sql_fetch(s48_value stmt_handle);


/*
 *
 * PART 8
 *
 * Obtaining information about the data source's
 * system tables (catalog functions)
 *
 */

/*
 *
 * PART 9
 *
 * Terminating a statement
 *
 */

/* Ends statement processing, discards pending resilt, and, 
 * optionally, frees all resources associated with the
 * statement handle */ 
s48_value odbc_sql_free_statement(s48_value stmt_handle, s48_value option);

/* Closes a cursor that has been opened on a statement handle */
s48_value odbc_sql_close_cursor(s48_value stmt_handle);

/* Cancels an SQL statement */
s48_value odbc_sql_cancel(s48_value stmt_handle);

/*
 *
 * PART 10
 *
 * Terminating a connection
 *
 */

/* Closes the connection */
s48_value odbc_sql_disconnect(s48_value conn_handle);

/* Free a handle */
s48_value odbc_sql_free_handle(s48_value handle_type, s48_value handle);


/*
 *
 * PART 11
 *
 * misc. functions
 *
 */

#ifdef ODBC_DEBUG_MSGS
/* print detailed debug information */
void odbc_debug_msgs(SQLSMALLINT handle_type, SQLHANDLE handle);
#endif

/* convert Scheme sql-date record to SQL_DATE_STRUCT */
void sql_date_record_to_struct(s48_value sql_date, SQL_DATE_STRUCT *ds);

/* convert SQL_DATE_STRUCT to Scheme sql-date record */
s48_value struct_to_sql_date_record(SQL_DATE_STRUCT *ds);

/* convert Scheme sql-time record to SQL_TIME_STRUCT */
void sql_time_record_to_struct(s48_value sql_time, SQL_TIME_STRUCT *ts);

/* convert SQL_TIME_STRUCT to Scheme sql-time record */
s48_value struct_to_sql_time_record(SQL_TIME_STRUCT *ts);

/* convert SQL_TIME_STRUCT to Scheme sql-time record */
s48_value struct_to_sql_time_record(SQL_TIME_STRUCT *ts);

/* convert Scheme sql-timestamp record to SQL_TIMESTAMP_STRUCT */ 
void sql_timestamp_record_to_struct(s48_value sql_timestamp, 
												SQL_TIMESTAMP_STRUCT *ts);

void s48_init_odbc(void);