/*
  vim:ts=2 sts=2 sw=2:et ai:

   Copyright (c) 2008   Patrick Galbraith
   Copyright (c) 2009   Clint Byrum <clint@fewbar.com>

   You may distribute under the terms of either the GNU General Public
   License or the Artistic License, as specified in the Perl README file.

*/

#include <stdlib.h>
#include <libdrizzle/drizzle_client.h>
#include "dbdimp.h"
#include "constants.h"


DBISTATE_DECLARE;


MODULE = DBD::drizzle	PACKAGE = DBD::drizzle

INCLUDE: drizzle.xsi

MODULE = DBD::drizzle	PACKAGE = DBD::drizzle

double
constant(name, arg)
    char* name
    char* arg
  CODE:
    RETVAL = drizzle_constant(name, arg);
  OUTPUT:
    RETVAL

MODULE = DBD::drizzle	PACKAGE = DBD::drizzle::dr

void
_ListDBs(drh, host=NULL, port=NULL, user=NULL, password=NULL)
    SV *        drh
    char *	host
    char *      port
    char *      user
    char *      password
  PPCODE:
{
  drizzle_return_t ret;
  drizzle_st drizzle;
  drizzle_con_st con;

  (void)drizzle_create(&drizzle);
  (void)drizzle_con_create(&drizzle, &con);

  (void)drizzle_con_add_tcp(&drizzle, &con, host, atoi(port), user, password, NULL, DRIZZLE_CON_NONE);
  ret = drizzle_con_connect(&con);

  if (ret != DRIZZLE_RETURN_OK)
  {
    do_error(drh, drizzle_errno(&drizzle), drizzle_error(&drizzle), NULL);
  }
  else
  {
    drizzle_row_t cur;
    drizzle_result_st res;

    (void) drizzle_result_create(&con, &res);

    (void) drizzle_query_str(&con, &res, "SHOW DATABASES", &ret);
    if(ret != DRIZZLE_RETURN_OK) {
      do_error(drh, drizzle_result_error_code(&res), drizzle_result_error(&res), drizzle_result_sqlstate(&res));
    }
    else
    {
      ret = drizzle_result_buffer(&res);
      EXTEND(sp, drizzle_result_row_count(&res));
      while ((cur = drizzle_row_next(&res)))
      {
        PUSHs(sv_2mortal((SV*)newSVpv(cur[0], strlen(cur[0]))));
      }
      drizzle_result_free(&res);
    }
    drizzle_con_close(&con);
  }
}


void _admin_internal(drh,dbh,command,dbname=NULL,host=NULL,port=NULL,user=NULL,password=NULL)
  SV* drh
  SV* dbh
  char* command
  char* dbname
  char* host
  char* port
  char* user
  char* password
  PPCODE:
{
  drizzle_return_t retval;
  drizzle_st *drizzle;
  drizzle_con_st *con = NULL;
  drizzle_result_st res;

  /*
   *  Connect to the database, if required.
 */

  if (SvOK(dbh)) {
    D_imp_dbh(dbh);
    drizzle = imp_dbh->drizzle;
    con = imp_dbh->con;
  }
  else
  {
    if (drizzle == NULL)
    {
      do_error(drh, -1, "error allocating memory for core drizzle structure", NULL);
      XSRETURN_NO;
    }
    con = drizzle_con_add_tcp(drizzle, con, host, atoi(port), user, password, NULL, DRIZZLE_CON_NONE);
    if (con == NULL)
    {
      do_error(drh, drizzle_errno(drizzle), drizzle_error(drizzle), NULL);
      XSRETURN_NO;
    }
    retval = drizzle_con_connect(con);
    if (retval != DRIZZLE_RETURN_OK)
    {
      do_error(drh, drizzle_con_errno(con), drizzle_con_error(con), NULL);
      XSRETURN_NO;
    }
  }

  (void) drizzle_result_create(con, &res);

  if (strEQ(command, "shutdown"))
  {
    (void) drizzle_shutdown(con, &res, DRIZZLE_SHUTDOWN_DEFAULT, &retval);
  }
  /*
  else if (strEQ(command, "createdb"))
  {
      D_imp_dbh(dbh);
      (void) create_schema(imp_dbh, &res, dbname);
  }
  else if (strEQ(command, "dropdb"))
  {
      D_imp_dbh(dbh);
      (void) drop_schema(imp_dbh, &res, dbname);
  }
    */
  else
  {
    croak("Unknown command: %s", command);
  }
  if (retval != DRIZZLE_RETURN_OK)
  {
    do_error(SvOK(dbh) ? dbh : drh, drizzle_con_errno(con),
             drizzle_con_error(con) ,drizzle_con_sqlstate(con));
  }

  if (SvOK(dbh))
  {
    (void) drizzle_con_close(con);
  }
  if (retval)
    XSRETURN_NO;
  else 
    XSRETURN_YES;
}


MODULE = DBD::drizzle    PACKAGE = DBD::drizzle::db


void
type_info_all(dbh)
  SV* dbh
  PPCODE:
{
  /* 	static AV* types = NULL; */
  /* 	if (!types) { */
  /* 	    D_imp_dbh(dbh); */
  /* 	    if (!(types = dbd_db_type_info_all(dbh, imp_dbh))) { */
  /* 	        croak("Cannot create types array (out of memory?)"); */
  /* 	    } */
  /* 	} */
  /* 	ST(0) = sv_2mortal(newRV_inc((SV*) types)); */
  D_imp_dbh(dbh);
  ST(0) = sv_2mortal(newRV_noinc((SV*) dbd_db_type_info_all(dbh,
                                                            imp_dbh)));
  XSRETURN(1);
}


void
_ListDBs(dbh)
  SV*	dbh
  PPCODE:
{
  drizzle_result_st res;
  drizzle_row_t cur;
  drizzle_return_t ret;

  D_imp_dbh(dbh);

  (void) drizzle_result_create(imp_dbh->con, &res);
  (void) drizzle_query_str(imp_dbh->con, &res,"SHOW DATABASES", &ret);
  if (ret != DRIZZLE_RETURN_OK)
    do_error(dbh,
            drizzle_con_errno(imp_dbh->con),
            drizzle_con_error(imp_dbh->con),
            drizzle_con_sqlstate(imp_dbh->con));
  ret= drizzle_result_buffer(&res);
  if (ret != DRIZZLE_RETURN_OK)
  {
    do_error(dbh, drizzle_result_error_code(&res),
    drizzle_result_error(&res), drizzle_result_sqlstate(&res));
  }
  else
  {
    EXTEND(sp, drizzle_result_row_count(&res));
    while ((cur = drizzle_row_next(&res)))
    {
      PUSHs(sv_2mortal((SV*)newSVpv(cur[0], strlen(cur[0]))));
    }
  }
  drizzle_result_free(&res);
}


void
do(dbh, statement, attr=Nullsv, ...)
  SV *        dbh
  SV *	statement
  SV *        attr
  PROTOTYPE: $$;$@
  CODE:
{
  D_imp_dbh(dbh);
  int num_params= 0;
  int retval;
  struct imp_sth_ph_st* params= NULL;
  drizzle_result_st _result;
  drizzle_result_st *result;
  if (items > 3)
  {
    /*  Handle binding supplied values to placeholders	   */
    /*  Assume user has passed the correct number of parameters  */
    int i;
    num_params= items-3;
    Newz(0, params, sizeof(*params)*num_params, struct imp_sth_ph_st);
    for (i= 0;  i < num_params;  i++)
    {
      params[i].value= ST(i+3);
      params[i].type= SQL_VARCHAR;
    }
  }
  retval = drizzle_st_internal_execute(dbh, statement, attr, num_params,
                                       params, &result, imp_dbh->con, 0);
  if (params)
    Safefree(params);

  drizzle_result_free(result);

  /* remember that dbd_st_execute must return <= -2 for error	*/
  if (retval == 0)		/* ok with no rows affected	*/
    XST_mPV(0, "0E0");	/* (true but zero)		*/
  else if (retval < -1)	/* -1 == unknown number of rows	*/
    XST_mUNDEF(0);		/* <= -2 means error   		*/
  else
    XST_mIV(0, retval);	/* typically 1, rowcount or -1	*/
}


SV*
ping(dbh)
    SV* dbh;
  PROTOTYPE: $
  CODE:
    {
      int retval;
      drizzle_return_t ret;
      drizzle_result_st res;
      D_imp_dbh(dbh);
      drizzle_ping(imp_dbh->con, &res, &ret);
      retval = (ret == DRIZZLE_RETURN_OK);
      if (!retval) {
        if (drizzle_db_reconnect(dbh)) {
          drizzle_ping(imp_dbh->con, &res, &ret);
          retval = (ret == DRIZZLE_RETURN_OK);
        }
      }
      drizzle_result_free(&res);
      RETVAL = boolSV(retval);
    }
  OUTPUT:
    RETVAL



void
quote(dbh, str, type=NULL)
    SV* dbh
    SV* str
    SV* type
  PROTOTYPE: $$;$
  PPCODE:
    {
        SV* quoted = dbd_db_quote(dbh, str, type);
	ST(0) = quoted ? sv_2mortal(quoted) : str;
	XSRETURN(1);
    }


MODULE = DBD::drizzle    PACKAGE = DBD::drizzle::st

int
more_results(sth)
    SV *	sth
    CODE:
{
  D_imp_sth(sth);
  int retval;
  if (dbd_st_more_results(sth, imp_sth))
  {
    RETVAL=1;
  }
  else
  {
    RETVAL=0;
  }
}
    OUTPUT:
      RETVAL

int
dataseek(sth, pos)
    SV* sth
    int pos
  PROTOTYPE: $$
  CODE:
{
  drizzle_return_t ret;
  D_imp_sth(sth);
  if (imp_sth->result) {
    drizzle_row_seek(imp_sth->result, pos);
    if (ret != DRIZZLE_RETURN_OK) {
      RETVAL = 0;
      do_error(sth, drizzle_result_error_code(imp_sth->result), drizzle_result_error(imp_sth->result), drizzle_result_sqlstate(imp_sth->result));
    } else { 
      RETVAL = 1;
    }  
  } else {
    RETVAL = 0;
    do_error(sth, JW_ERR_NOT_ACTIVE, "Statement not active" ,NULL);
  }
}
  OUTPUT:
    RETVAL

void
rows(sth)
    SV* sth
  CODE:
    D_imp_sth(sth);
    char buf[64];
  /* fix to make rows able to handle errors and handle max value from 
     affected rows.
     XXX check to see if this is still a reality -cb
     if drizzleclient_affected_row returns an error, it's value is 18446744073709551614,
     while a (uint64_t)-1 is  18446744073709551615, so we have to add 1 to
     imp_sth->row_num to know if there's an error
  */
  if (imp_sth->row_num+1 ==  (uint64_t) -1)
    sprintf(buf, "%d", -1);
  else
    sprintf(buf, "%lu", imp_sth->row_num);

  ST(0) = sv_2mortal(newSVpvn(buf, strlen(buf)));



MODULE = DBD::drizzle    PACKAGE = DBD::drizzle::GetInfo

# This probably should be grabed out of some ODBC types header file
#define SQL_CATALOG_NAME_SEPARATOR 41
#define SQL_CATALOG_TERM 42
#define SQL_DBMS_VER 18
#define SQL_IDENTIFIER_QUOTE_CHAR 29
#define SQL_MAXIMUM_STATEMENT_LENGTH 105
#define SQL_MAXIMUM_TABLES_IN_SELECT 106
#define SQL_MAX_TABLE_NAME_LEN 35
#define SQL_SERVER_NAME 13


#  dbd_drizzle_getinfo()
#  Return ODBC get_info() information that must needs be accessed from C
#  This is an undocumented function that should only
#  be used by DBD::drizzle::GetInfo.

void
dbd_drizzle_get_info(dbh, sql_info_type)
    SV* dbh
    SV* sql_info_type
  CODE:
    D_imp_dbh(dbh);
    IV type = 0;
    SV* retsv=NULL;
    bool using_322=0;

    if (SvMAGICAL(sql_info_type))
        mg_get(sql_info_type);

    if (SvOK(sql_info_type))
    	type = SvIV(sql_info_type);
    else
    	croak("get_info called with an invalied parameter");
    
    switch(type) {
    	case SQL_CATALOG_NAME_SEPARATOR:
	    /* (dbc->flag & FLAG_NO_CATALOG) ? WTF is in flag ? */
	    retsv = newSVpv(".",1);
	    break;
	case SQL_CATALOG_TERM:
	    /* (dbc->flag & FLAG_NO_CATALOG) ? WTF is in flag ? */
	    retsv = newSVpv("database",8);
	    break;
	case SQL_DBMS_VER:
	    retsv = newSVpv(
	        drizzle_con_server_version(imp_dbh->con),
		strlen(drizzle_con_server_version(imp_dbh->con))
	    );
	    break;
	case SQL_IDENTIFIER_QUOTE_CHAR:
	    /*XXX What about a DB started in ANSI mode? */
	    /* Swiped from MyODBC's get_info.c */
	    retsv = newSVpv("`", 1);
	    break;
	case SQL_MAXIMUM_STATEMENT_LENGTH:
	    retsv = newSViv(8192);
	    break;
	case SQL_MAXIMUM_TABLES_IN_SELECT:
	    /* newSViv((sizeof(int) > 32) ? sizeof(int)-1 : 31 ); in general? */
	    retsv= newSViv((sizeof(int) == 64 ) ? 63 : 31 );
	    break;
	case SQL_MAX_TABLE_NAME_LEN:
      // XXX need to look this up
	    retsv= newSViv(254);
	    break;
	case SQL_SERVER_NAME:
	    retsv= newSVpv(drizzle_con_host(imp_dbh->con),strlen(drizzle_con_host(imp_dbh->con)));
	    break;
    	default:
 		croak("Unknown SQL Info type");
    }
    ST(0) = sv_2mortal(retsv);