/*
 * $Id$
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <ibase.h>
#include <iberror.h>

#ifndef ISC_INT64_FORMAT

#if (defined(_MSC_VER) && defined(WIN32)) || (defined(__BORLANDC__) && defined(__WIN32__))
#define  ISC_INT64_FORMAT  "I64"
#else
#define  ISC_INT64_FORMAT  "ll"
#endif
#endif

#define USER     "sysdba"
#define PASSWORD "masterkey"
#define DATABASE "192.168.1.33:d:\\fontes\\lixo\\test.gdb"
#define ERREXIT(status, rc)   {isc_print_status(status); return rc;}
#define MAX_BUFFER          1024

int execute( char *exec_str );
int query( char *sel_str );
int fetch( void );
int qclose( void );
char *getdata(int pos);


isc_db_handle       db = NULL;             
int                 dialect = 1;
XSQLDA  ISC_FAR *   sqlda;
isc_stmt_handle     stmt = NULL;
isc_tr_handle       trans = NULL;

int main ()
{
    char                dpb[48];
    int                 i = 0, len;
    long                status[20];
    
    dpb[i++] = isc_dpb_version1;
    
    dpb[i++] = isc_dpb_user_name;
    len = strlen(USER);
    dpb[i++] = (char) len;
    strncpy(&(dpb[i]), USER, len);
    i += len;
    
    dpb[i++] = isc_dpb_password;
    len = strlen (PASSWORD);
    dpb[i++] = len;
    strncpy(&(dpb[i]), PASSWORD, len);
    i += len;
    
    if (isc_attach_database(status, 0, DATABASE, &db, i, dpb))
        ERREXIT(status, 1);

    execute("DROP TABLE TESTE");
    
    execute("CREATE TABLE TESTE (code smallint)");

    execute("INSERT INTO TESTE (code) VALUES (100)");

    query("SELECT * FROM TESTE");
    while (fetch() == 0) 
        printf("%s\n", getdata(0));
    qclose();


    if (isc_detach_database(status, &db))
        ERREXIT(status, 1);

    return 1;
}

int execute( char *exec_str )
{
    isc_tr_handle    trans = NULL;          
    long             status[20];            

    if (isc_start_transaction(status, &trans, 1, &db, 0, NULL))
        ERREXIT(status, 1);
        
    if (isc_dsql_execute_immediate(status, &db, &trans, 0, exec_str, dialect, NULL))
        ERREXIT(status, 1);
        
    if (isc_commit_transaction(status, &trans))
        ERREXIT(status, 1);

    return 1;        
}

int query( char *sel_str )
{
    ISC_STATUS          status[20];
    XSQLVAR             *var;
    
    int                 n, i, dtype;
        
    if ( isc_start_transaction ( status, &trans, 1, &db, 0, NULL ) ) 
        ERREXIT(status, 1);   
              
    // Allocate an output SQLDA. Just to check number of columns 
    sqlda = ( XSQLDA * ) malloc( XSQLDA_LENGTH ( 1 ) );
    sqlda->sqln = 1;
    sqlda->version = 1;

    // Allocate a statement
    if (isc_dsql_allocate_statement(status, &db, &stmt))
        ERREXIT(status, 1);

    // Prepare the statement. 
    if (isc_dsql_prepare(status, &trans, &stmt, 0, sel_str, dialect, sqlda))
        ERREXIT(status, 1);
    
    // Describe sql contents        
    if (isc_dsql_describe(status, &stmt, dialect, sqlda))
        ERREXIT(status, 1);
    
    // Relocate necessary number of columns
    if ( sqlda->sqld > sqlda->sqln ) {
        free( sqlda );
        n = sqlda->sqld;
        sqlda = ( XSQLDA * ) malloc( XSQLDA_LENGTH ( n ) );
        sqlda->sqln = n;
        sqlda->version = 1;

        if (isc_dsql_describe(status, &stmt, dialect, sqlda))
            ERREXIT(status, 1);
    }
    
    for ( i = 0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++ ) {
        dtype = ( var->sqltype & ~1 );
        switch ( dtype ) {
        case SQL_VARYING:
            var->sqltype = SQL_TEXT;
            var->sqldata = ( char * ) malloc( sizeof ( char ) * var->sqllen + 2 );
            break;
        case SQL_TEXT:
            var->sqldata = ( char * ) malloc( sizeof ( char ) * var->sqllen + 2 );
            break;
        case SQL_LONG:
            var->sqltype = SQL_LONG;
            var->sqldata = ( char * ) malloc( sizeof ( long ) );
            break;
        default:
            var->sqldata = ( char * ) malloc( sizeof ( char ) * var->sqllen );
            break;
        }
        if ( var->sqltype & 1 ) {
            var->sqlind = ( short * ) malloc( sizeof ( short ) );
        }
    }
        
    if ( !sqlda->sqld ) {
        // Execute and commit non-select querys 
        if ( isc_dsql_execute ( status, &trans, &stmt, dialect, NULL ) ) 
            ERREXIT(status, 1);

        if (isc_commit_transaction(status, &trans))
            ERREXIT(status, 1);

        trans = NULL;
                            
    } else { 
        if ( isc_dsql_execute ( status, &trans, &stmt, dialect, sqlda ) ) 
             ERREXIT(status, 1);
    }                
    
    return 1;      
}

int fetch( void )
{
   long fetch_stat;    
   long status[20];
   
   fetch_stat = isc_dsql_fetch(status, &stmt, dialect, sqlda);

   if (fetch_stat != 100L)
        ERREXIT(status, 1);
    
    return fetch_stat;
}    
    
int qclose( void )
{
   long status[20];

    if (isc_dsql_free_statement(status, &stmt, DSQL_drop))
        ERREXIT(status, 1);

    if (trans) 
        if (isc_commit_transaction(status, &trans)) 
            ERREXIT(status, 1);
        
    if ( sqlda )
        free( sqlda );  
    
    return 1;
}    

char *getdata(int pos)
{
   short dtype;
   char data[MAX_BUFFER], *p;
   char blob_s[20], date_s[25];
   short len;
   long status[20];
    
   struct tm times;
   ISC_QUAD bid;
   XSQLVAR *var;

   if ( ( pos + 1 ) > sqlda->sqln )              
       return "error";

   var = sqlda->sqlvar;

   var += pos;

   dtype = var->sqltype & ~1;
   p = data;

   if ( ( var->sqltype & 1 ) && ( *var->sqlind < 0 ) ) {
      switch ( dtype ) {
      case SQL_TEXT:
      case SQL_VARYING:
         len = var->sqllen;
         break;
      case SQL_SHORT:
         len = 6;
         if ( var->sqlscale > 0 )
            len += var->sqlscale;
         break;
      case SQL_LONG:
         len = 11;
         if ( var->sqlscale > 0 )
            len += var->sqlscale;
         break;
      case SQL_INT64:
         len = 21;
         if ( var->sqlscale > 0 )
            len += var->sqlscale;
         break;
      case SQL_FLOAT:
         len = 15;
         break;
      case SQL_DOUBLE:
         len = 24;
         break;
      case SQL_TIMESTAMP:
         len = 24;
         break;
      case SQL_TYPE_DATE:
         len = 10;
         break;
      case SQL_TYPE_TIME:
         len = 13;
         break;
      case SQL_BLOB:
      case SQL_ARRAY:
      default:
         len = 17;
         break;
      }
      if ( ( dtype == SQL_TEXT ) || ( dtype == SQL_VARYING ) )
         sprintf ( p, "%-*s ", len, "NULL" );
      else
         sprintf ( p, "%*s ", len, "NULL" );
   } else {
      switch ( dtype ) {
      case SQL_TEXT:
         sprintf ( p, "%.*s ", var->sqllen, var->sqldata );
         break;

      case SQL_VARYING:
         sprintf ( p, "%.*s ", var->sqllen, var->sqldata );
         break;

      case SQL_SHORT:
      case SQL_LONG:
      case SQL_INT64:
      {
         ISC_INT64 value = 0;
         short field_width = 0;
         short dscale;

         switch ( dtype ) {
         case SQL_SHORT:
            value = ( ISC_INT64 ) * ( short ISC_FAR * ) var->sqldata;
            field_width = 6;
            break;
         case SQL_LONG:
            value = ( ISC_INT64 ) * ( long ISC_FAR * ) var->sqldata;
            field_width = 11;
            break;
         case SQL_INT64:
            value = ( ISC_INT64 ) * ( ISC_INT64 ISC_FAR * ) var->sqldata;
            field_width = 21;
            break;
         }
         dscale = var->sqlscale;
         if ( dscale < 0 ) {
            ISC_INT64 tens;
            short i;

            tens = 1;
            for ( i = 0; i > dscale; i-- ) {
               tens *= 10;

               if ( value >= 0 ) {

                  sprintf ( p,
                       "%*" ISC_INT64_FORMAT "d.%0*"
                       ISC_INT64_FORMAT "d",
                       field_width - 1 + dscale,
                       ( ISC_INT64 ) ( value / tens ), -dscale,
                       ( ISC_INT64 ) ( value % tens ) );
               } else if ( ( value / tens ) != 0 ) {

                  sprintf ( p,
                       "%*" ISC_INT64_FORMAT "d.%0*"
                       ISC_INT64_FORMAT "d",
                       field_width - 1 + dscale,
                       ( ISC_INT64 ) ( value / tens ), -dscale,
                       ( ISC_INT64 ) - ( value % tens ) );
               } else {

                  sprintf ( p, "%*s.%0*" ISC_INT64_FORMAT "d",
                       field_width - 1 + dscale,
                       "-0", -dscale,
                       ( ISC_INT64 ) - ( value % tens ) );
               }
            }
         } else if ( dscale ) {
            sprintf ( p, "%*" ISC_INT64_FORMAT "d%0*d",
                 field_width, ( ISC_INT64 ) value, dscale, 0 );
         } else {
            sprintf ( p, "%*" ISC_INT64_FORMAT "d",
                 field_width, ( ISC_INT64 ) value );
         }
      };
         break;

      case SQL_FLOAT:
         sprintf ( p, "%15g ", *( float ISC_FAR * ) ( var->sqldata ) );
         break;

      case SQL_DOUBLE:
         sprintf ( p, "%24f ", *( double ISC_FAR * ) ( var->sqldata ) );
         break;

      case SQL_TIMESTAMP:
         isc_decode_timestamp ( ( ISC_TIMESTAMP ISC_FAR * ) var->sqldata, &times );
         sprintf ( date_s, "%04d-%02d-%02d %02d:%02d:%02d.%04lui",
              times.tm_year + 1900,
              times.tm_mon + 1,
              times.tm_mday,
              times.tm_hour,
              times.tm_min,
              times.tm_sec,
              ( ( ISC_TIMESTAMP * ) var->sqldata )->timestamp_time % 10000 );
         sprintf ( p, "%*s ", 24, date_s );
         break;

      case SQL_TYPE_DATE:
         isc_decode_sql_date ( ( ISC_DATE ISC_FAR * ) var->sqldata, &times );
         sprintf ( date_s, "%04d-%02d-%02d",
              times.tm_year + 1900, times.tm_mon + 1, times.tm_mday );
         sprintf ( p, "%*s ", 10, date_s );
         break;

      case SQL_TYPE_TIME:
         isc_decode_sql_time ( ( ISC_TIME ISC_FAR * ) var->sqldata, &times );
         sprintf ( date_s, "%02d:%02d:%02d.%04lui",
              times.tm_hour,
              times.tm_min,
              times.tm_sec, ( *( ( ISC_TIME * ) var->sqldata ) ) % 10000 );
         sprintf ( p, "%*s ", 13, date_s );
         break;

      case SQL_BLOB:
      case SQL_ARRAY:
         /* Print the blob id on blobs or arrays */
         bid = *( ISC_QUAD ISC_FAR * ) var->sqldata;
         sprintf ( blob_s, "%08x:%08x", ( unsigned int ) bid.gds_quad_high,
              ( unsigned int ) bid.gds_quad_low );
         sprintf ( p, "%17s ", blob_s );
         break;

      default:
         break;
      }
   }

   return ( p );
}
