/*
 * $Id$
 *
 * xHarbour Project source code:
 * PostgreSQL RDBMS low level (client api) interface code.
 *
 * Copyright 2003 Rodrigo Moreno rodrigo_moreno@yahoo.com
 * www - http://www.xharbour.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 * See doc/license.txt for licensing terms.
 *
 */

#include "hbapi.h"
#include "hbapiitm.h"
#include "libpq-fe.h"

#define _CLIPDEFS_H
#if defined(HB_OS_WIN_32_USED)
   #include <windows.h>
#endif

#define VARHDRSZ              4
#define BOOLOID               16
#define INT8OID               20
#define INT2OID               21
#define INT4OID               23
#define TEXTOID               25
#define OIDOID                26
#define FLOAT4OID             700
#define FLOAT8OID             701
#define CASHOID               790                                                                
#define BPCHAROID             1042
#define VARCHAROID            1043
#define DATEOID               1082
#define TIMEOID               1083
#define TIMESTAMPOID          1114
#define TIMESTAMPTZOID        1184
#define TIMETZOID             1266
#define BITOID                1560
#define VARBITOID             1562
#define NUMERICOID            1700

#define INV_WRITE       0x00020000
#define INV_READ        0x00040000

#ifndef HB_PGVERSION
#  ifdef PG_DIAG_INTERNAL_POSITION
#     define HB_PGVERSION   0x0800
#  else
#     define HB_PGVERSION   0x0700
#  endif
#endif

/* 
 * Connection handling functions 
 */

HB_FUNC( PQCONNECT )
{
   char     conninfo[128];
   PGconn   *conn;

   if( hb_pcount() == 5 )
      sprintf( conninfo, "dbname = %s host = %s user = %s password = %s port = %i",
               hb_parcx(1), hb_parcx(2), hb_parcx(3), hb_parcx(4), (int) hb_parni(5) );

   conn = PQconnectdb( conninfo );
   hb_retptr( conn );
}

HB_FUNC( PQSETDBLOGIN )
{
    const char *pghost;
    const char *pgport;
    const char *pgoptions;
    const char *pgtty;
    const char *dbName;
    const char *login;
    const char *pwd;
                                         
    pghost    = hb_parcx(1);   
    pgport    = hb_parcx(2);   
    pgoptions = hb_parcx(3);
    pgtty     = hb_parcx(4);    
    dbName    = hb_parcx(5);   
    login     = hb_parcx(6);    
    pwd       = hb_parcx(8);      
                                         
    if (hb_pcount() == 7)
        hb_retptr( ( PGconn * ) PQsetdbLogin( pghost, pgport, pgoptions, pgtty, dbName, login, pwd) );
}   
                    
HB_FUNC(PQCLOSE)
{
    if (hb_parinfo(1))
        PQfinish(( PGconn * ) hb_parptr(1));
}

HB_FUNC(PQRESET)
{
    if (hb_parinfo(1))
        PQreset(( PGconn * ) hb_parptr(1));
}

HB_FUNC(PQPROTOCOLVERSION)
{
    if (hb_parinfo(1))
        hb_retni(PQprotocolVersion(( PGconn * ) hb_parptr(1)));
}

HB_FUNC(PQCLIENTENCODING)
{
    if (hb_parinfo(1))
        hb_retni(PQclientEncoding(( PGconn * ) hb_parptr(1)));
}

HB_FUNC(PQSETCLIENTENCODING)
{
    if (hb_pcount() == 2)
        hb_retni(PQsetClientEncoding(( PGconn * ) hb_parptr(1), hb_parcx(2)));
}
    
HB_FUNC(PQDB)
{   
    if (hb_parinfo(1))
        hb_retc(PQdb( ( PGconn * ) hb_parptr(1) ));
}
        
HB_FUNC(PQUSER)
{
    if (hb_parinfo(1))
       hb_retc(PQuser( ( PGconn * ) hb_parptr(1) ));
}
            
HB_FUNC(PQPASS)
{
    if (hb_parinfo(1))
        hb_retc(PQpass( ( PGconn * ) hb_parptr(1) ));
}
            
HB_FUNC(PQHOST)
{
    if (hb_parinfo(1))
        hb_retc(PQhost( ( PGconn * ) hb_parptr(1) ));
}
            
HB_FUNC(PQPORT)
{
    if (hb_parinfo(1))
        hb_retc(PQport( ( PGconn * ) hb_parptr(1) ));
}
            
HB_FUNC(PQTTY)
{
    if (hb_parinfo(1))
        hb_retc(PQtty( ( PGconn * ) hb_parptr(1) ));
}
            
HB_FUNC(PQOPTIONS)
{
    if (hb_parinfo(1))
        hb_retc(PQoptions( ( PGconn * ) hb_parptr(1) ));
}

/* 
 * Query handling functions 
 */

HB_FUNC(PQCLEAR)
{
    if (hb_parinfo(1))
        PQclear(( PGresult * ) hb_parptr(1));
}

HB_FUNC(PQEXEC)
{
    PGresult   *res = NULL;

    if (hb_pcount() == 2)
        res = PQexec(( PGconn * ) hb_parptr(1), hb_parcx(2));

    hb_retptr( res );        
}

HB_FUNC(PQEXECPARAMS)
{
    PGresult   *res = NULL;
    const char **paramvalues;
    int        i;
    long       n;

    PHB_ITEM   aParam;

    if (hb_pcount() == 3)
    {
        aParam = hb_param(3,HB_IT_ARRAY);

        n = hb_arrayLen(aParam);

        paramvalues = (const char **) hb_xgrab( sizeof( char *) * n );

        for (i=0;i < n;i++)
            paramvalues[i] = hb_arrayGetCPtr( aParam, i + 1 );

        res = PQexecParams(( PGconn * ) hb_parptr(1), hb_parcx(2), n, NULL, paramvalues, NULL, NULL, 1);

        hb_xfree(paramvalues);
    }
    hb_retptr( res );        
}

HB_FUNC(PQFCOUNT)
{
    PGresult   *res;
    int nFields = 0;

    if (hb_parinfo(1))
    {
        res = ( PGresult * ) hb_parptr(1);

        if (PQresultStatus(res) == PGRES_TUPLES_OK)
                nFields = PQnfields(res);
    }

    hb_retni(nFields);
}

HB_FUNC(PQLASTREC)
{
    PGresult   *res;
    int nRows = 0;

    if (hb_parinfo(1))
    {
        res = ( PGresult * ) hb_parptr(1);

        if (PQresultStatus(res) == PGRES_TUPLES_OK)
            nRows = PQntuples(res);
    }
    hb_retni(nRows);
}

HB_FUNC(PQGETVALUE)
{
    PGresult   *res;
    int         nRow, nCol;

    if (hb_pcount() == 3)
    {
        res = ( PGresult * ) hb_parptr(1);

        if (PQresultStatus(res) == PGRES_TUPLES_OK)
        {
            nRow = hb_parni(2) - 1;
            nCol = hb_parni(3) - 1;

            if (! PQgetisnull(res, nRow, nCol))
                hb_retc(PQgetvalue(res, nRow, nCol));
        }
    }
}

HB_FUNC(PQGETLENGTH)
{
    PGresult   *res;
    int         nRow, nCol;
    int         result = 0;

    if (hb_pcount() == 3)
    {
        res = ( PGresult * ) hb_parptr(1);

        if (PQresultStatus(res) == PGRES_TUPLES_OK)
        {
            nRow = hb_parni(2) - 1;
            nCol = hb_parni(3) - 1;

            result = PQgetlength(res, nRow, nCol);
        }
    }
    hb_retni(result);
}

HB_FUNC( PQMETADATA )
{
   PGresult *res;
   if( hb_parinfo( 1 ) )
   {
      res = ( PGresult * ) hb_parptr( 1 );
      if( PQresultStatus( res ) == PGRES_TUPLES_OK )
      {
         int nFields = PQnfields( res ), i;
         PHB_ITEM pResult = hb_itemArrayNew( nFields ), pField;

         for( i = 0; i < nFields; i++ )
         {
            char  buf[256];
            Oid   type_oid = PQftype( res, i );
            int   typemod = PQfmod( res, i );
            int   length = 0;
            int   decimal = 0;

            switch( type_oid )
            {
               case BITOID:
                  if( typemod >= 0 )
                     length = ( int ) typemod;
                  strcpy( buf, "bit" );
                  break;

               case BOOLOID:
                  length = 1;
                  strcpy( buf, "boolean" );
                  break;

               case BPCHAROID:
                  if( typemod >= 0 )
                     length = ( int ) ( typemod - VARHDRSZ );
                  strcpy( buf, "character" );
                  break;

               case FLOAT4OID:
                  strcpy( buf, "real" );
                  break;

               case FLOAT8OID:
                  strcpy( buf, "double precision" );
                  break;

               case INT2OID:
                  strcpy( buf, "smallint" );
                  break;

               case INT4OID:
                  strcpy( buf, "integer" );
                  break;

               case OIDOID:
                  strcpy( buf, "bigint" );
                  break;

               case INT8OID:
                  strcpy( buf, "bigint" );
                  break;

               case NUMERICOID:
                  length = ( ( typemod - VARHDRSZ ) >> 16 ) & 0xffff;
                  decimal = ( typemod - VARHDRSZ ) & 0xffff;
                  strcpy( buf, "numeric" );
                  break;

               case DATEOID:
                  strcpy( buf, "date" );
                  break;

               case TIMEOID:
               case TIMETZOID:
                  strcpy( buf, "timezone" );
                  break;

               case TIMESTAMPOID:
               case TIMESTAMPTZOID:
                  strcpy( buf, "timestamp" );
                  break;

               case VARBITOID:
                  if( typemod >= 0 )
                     length = (int) typemod;
                  strcpy( buf, "bit varying" );
                  break;

               case VARCHAROID:
                  if( typemod >= 0 )
                     length = ( int ) ( typemod - VARHDRSZ );
                  strcpy( buf, "character varying" );
                  break;

               case TEXTOID:
                  strcpy(buf, "text");
                  break;

               case CASHOID:
                  strcpy( buf, "money" );
                  break;

               default:
                 strcpy( buf, "not supported" );
                 break;
            }

            pField = hb_arrayGetItemPtr( pResult, i + 1 );
            hb_arrayNew ( pField, 6 );
            hb_itemPutC ( hb_arrayGetItemPtr( pField, 1 ), PQfname( res, i ) );
            hb_itemPutC ( hb_arrayGetItemPtr( pField, 2 ), buf );
            hb_itemPutNI( hb_arrayGetItemPtr( pField, 3 ), length );
            hb_itemPutNI( hb_arrayGetItemPtr( pField, 4 ), decimal );
            hb_itemPutNL( hb_arrayGetItemPtr( pField, 5 ), PQftable( res, i ) );
            hb_itemPutNI( hb_arrayGetItemPtr( pField, 6 ), PQftablecol( res, i ) );
         }
         hb_itemRelease( hb_itemReturnForward( pResult ) );
      }
   }
}

HB_FUNC(PQTRANSACTIONSTATUS)
{
    if (hb_parinfo(1))
        hb_retni(PQtransactionStatus(( PGconn * ) hb_parptr(1) ));
}

HB_FUNC(PQERRORMESSAGE)
{
    if (hb_parinfo(1))
        hb_retc(PQerrorMessage(( PGconn * ) hb_parptr(1) ));
}

HB_FUNC(PQSTATUS)
{
    if (hb_parinfo(1))
        hb_retni(PQstatus(( PGconn * ) hb_parptr(1) ));
}

HB_FUNC(PQRESULTERRORMESSAGE)
{
    if (hb_parinfo(1))
        hb_retc(PQresultErrorMessage(( PGresult * ) hb_parptr(1)));
}

HB_FUNC(PQRESULTSTATUS)
{
    if (hb_parinfo(1))
        hb_retni(PQresultStatus(( PGresult * ) hb_parptr(1) ));
}


HB_FUNC(PQCMDSTATUS)
{
    if (hb_parinfo(1))
        hb_retc(PQcmdStatus( (PGresult *) hb_parptr(1) ));
}


HB_FUNC(PQCMDTUPLES)
{
    if (hb_parinfo(1))
        hb_retc(PQcmdTuples( (PGresult *) hb_parptr(1) ));
}


HB_FUNC(PQESCAPESTRING)
{
    char *source;
    char *dest;
    size_t size;
        
    source = hb_parcx(1);
    dest = (char *) hb_xgrab( strlen(source) * 2 + 1);
    size = strlen(source);
    
    PQescapeString(dest, source, size);
    
    hb_retc(dest);
    hb_xfree( (char *) dest);    
}


HB_FUNC(PQESCAPEBYTEA) /* deprecated */
{
    unsigned const char *from;
    unsigned char *to;
    size_t from_length;
    size_t to_length;
        
    from = ( BYTE * ) hb_parc(1);
    from_length = hb_parclen(1);
    to_length = from_length * 5 + 1;
    
    to = PQescapeBytea(from, from_length, &to_length);
    hb_retc( ( char * ) to );
    PQfreemem(to);
}


HB_FUNC(PQUNESCAPEBYTEA)
{
    unsigned char *from;
    size_t to_length;        
    
    from = PQunescapeBytea(( BYTE * ) hb_parcx(1), &to_length);
    hb_retclen( ( char * ) from, to_length);
    PQfreemem(from);
}


HB_FUNC(PQOIDVALUE)
{
    if (hb_parinfo(1))
        hb_retnl( ( Oid ) PQoidValue(( PGresult * ) hb_parptr(1) ));
}

HB_FUNC(PQOIDSTATUS)
{
    if (hb_parinfo(1))
        hb_retc( PQoidStatus(( PGresult * ) hb_parptr(1) ));
}

HB_FUNC(PQBINARYTUPLES)
{
    if (hb_parinfo(1))
        hb_retl( PQbinaryTuples(( PGresult * ) hb_parptr(1) ));
}

HB_FUNC(PQFTABLE)
{
    if (hb_pcount() == 2)
        hb_retnl( ( Oid ) PQftable(( PGresult * ) hb_parptr(1), hb_parni(2) - 1 ));
}

HB_FUNC(PQFTYPE)
{
    if (hb_pcount() == 2)
        hb_retnl( ( Oid ) PQftype(( PGresult * ) hb_parptr(1), hb_parni(2) - 1 ));
}

HB_FUNC(PQFNAME)
{
    if (hb_pcount() == 2)
        hb_retc( PQfname(( PGresult * ) hb_parptr(1), hb_parni(2) - 1 ));
}

HB_FUNC(PQFMOD)
{
    if (hb_pcount() == 2)
        hb_retni( PQfmod(( PGresult * ) hb_parptr(1), hb_parni(2) - 1 ));
}

HB_FUNC(PQFSIZE)
{
    if (hb_pcount() == 2)
        hb_retni( PQfsize(( PGresult * ) hb_parptr(1), hb_parni(2) - 1 ));
}

HB_FUNC(PQGETISNULL)
{
    if (hb_pcount() == 3)
        hb_retl( PQgetisnull(( PGresult * ) hb_parptr(1), hb_parni(2) - 1 , hb_parni(3) - 1));
}

HB_FUNC(PQFNUMBER)
{
    if (hb_pcount() == 2)
        hb_retni( PQfnumber(( PGresult * ) hb_parptr(1), hb_parcx(2) ) + 1);
}

HB_FUNC(PQNTUPLES)
{
    if (hb_parinfo(1))
        hb_retnl( PQntuples(( PGresult * ) hb_parptr(1) ));
}

HB_FUNC(PQNFIELDS)
{
    if (hb_parinfo(1))
        hb_retnl( PQnfields(( PGresult * ) hb_parptr(1) ));
}

/* 
 * Asynchronous functions 
 */

HB_FUNC(PQSENDQUERY)
{
    int res = 0;        

    if (hb_pcount() == 2)
        res = PQsendQuery(( PGconn * ) hb_parptr(1), hb_parcx(2));

    hb_retl( res );        
}

HB_FUNC(PQGETRESULT)
{
    PGresult   *res = NULL;

    if (hb_parinfo(1))
        res = PQgetResult(( PGconn * ) hb_parptr(1));

    /* when null, no more result to catch */
    if (res)
        hb_retptr( res );        
}

HB_FUNC(PQCONSUMEINPUT)
{
    int res = 0;        

    if (hb_parinfo(1))
        res = PQconsumeInput(( PGconn * ) hb_parptr(1));

    hb_retl( res );        
}

HB_FUNC(PQISBUSY)
{
    int res = 0;        

    if (hb_parinfo(1))
        res = PQisBusy(( PGconn * ) hb_parptr(1));

    hb_retl( res );        
}

HB_FUNC(PQREQUESTCANCEL) /* deprecated */
{
    int res = 0;        

    if (hb_parinfo(1))
        res = PQrequestCancel(( PGconn * ) hb_parptr(1));

    hb_retl( res );        
}


HB_FUNC(PQFLUSH)
{
    if (hb_parinfo(1))
        hb_retni( PQflush(( PGconn * ) hb_parptr(1)) );
}


HB_FUNC(PQSETNONBLOCKING)
{
    if (hb_pcount() == 2)
        hb_retl( PQsetnonblocking( ( PGconn * ) hb_parptr(1), hb_parl(2) ) );
}

HB_FUNC(PQISNONBLOCKING)
{
    if (hb_parinfo(1))
        hb_retl( PQisnonblocking( ( PGconn * ) hb_parptr(1) ) );
}

/* 
 * Trace Connection handling functions 
 */

HB_FUNC(PQCREATETRACE)
{
    FILE * pFile;

    if (hb_parinfo(1))
    {
        pFile = fopen( hb_parcx(1), "w+b");
    
        if (pFile != NULL)
            hb_retptr( ( FILE * ) pFile );
    }            
}

HB_FUNC(PQCLOSETRACE)
{
    if (hb_parinfo(1))
        fclose( ( FILE * ) hb_parptr(1) );
}

HB_FUNC(PQTRACE)
{
    if (hb_pcount() == 2)
        PQtrace( ( PGconn * ) hb_parptr(1), ( FILE * ) hb_parptr(2) );
}

HB_FUNC(PQUNTRACE)
{
    if (hb_parinfo(1))
        PQuntrace( ( PGconn * ) hb_parptr(1) );
}

HB_FUNC(PQSETERRORVERBOSITY)
{
    /* PQERRORS_TERSE   0
       PQERRORS_DEFAULT 1
       PQERRORS_VERBOSE 2
    */
    
    if (hb_pcount() == 2)
        hb_retni( ( PGVerbosity ) PQsetErrorVerbosity( ( PGconn * ) hb_parptr(1), ( PGVerbosity ) hb_parni(2) ) );
}


/* 
 * Large Object functions 
 */


HB_FUNC(LO_IMPORT)
{
    int ret = 0; 
    
    if (hb_pcount() == 2)
         ret = lo_import( ( PGconn * ) hb_parptr(1), hb_parcx(2) );

    hb_retni(ret);         
}

HB_FUNC(LO_EXPORT)
{
    int ret = 0; 
    
    if (hb_pcount() == 3)
    {
        ret = lo_export( ( PGconn * ) hb_parptr(1), ( Oid ) hb_parnl(2), hb_parcx(3) );

        if (ret != 1)
            ret = 0;
    }            

    hb_retl(ret);        
}

HB_FUNC(LO_UNLINK)
{   
    int ret = 0; 
           
    if (hb_pcount() == 2)
    {
        ret = lo_unlink( ( PGconn * ) hb_parptr(1), ( Oid ) hb_parnl(2) );        
        
        if (ret != 1)
            ret = 0;
    }            

    hb_retl(ret);        
}



#if HB_PGVERSION >= 0x0800

HB_FUNC(PQSERVERVERSION)
{
    if (hb_parinfo(1))
        hb_retni(PQserverVersion(( PGconn * ) hb_parptr(1)));
}

HB_FUNC(PQGETCANCEL)
{
    if (hb_parinfo(1))
        hb_retptr( ( PGcancel * ) PQgetCancel( ( PGconn * ) hb_parptr(1) ) );
}        

HB_FUNC(PQCANCEL)
{
    char errbuf[256];
    int ret = 0;

    if (hb_parinfo(1))
        if (PQcancel( ( PGcancel * ) hb_parptr(1), errbuf, 255) == 1)
        {
            ret = 1;                
            hb_storc( errbuf, 2 );
        }            
        
    hb_retl(ret);            
}        

HB_FUNC(PQFREECANCEL)
{
    if (hb_parinfo(1))
        PQfreeCancel( ( PGcancel * ) hb_parptr(1) ) ;
}        

HB_FUNC(PQESCAPEBYTEACONN) 
{
    unsigned const char *from;
    unsigned char *to;
    size_t from_length;
    size_t to_length;
        
    from = ( BYTE * ) hb_parc(2);
    from_length = hb_parclen(2);
    to_length = from_length * 5 + 1;
    
    to = PQescapeByteaConn(( PGconn * ) hb_parptr(1), from, from_length, &to_length);
    hb_retc( ( char * ) to );
    PQfreemem(to);
}

#endif


/*

TODO: Implement Full Large Objects Support
TODO: Implement Prepared Query handling

extern int  lo_open(PGconn *conn, Oid lobjId, int mode);
extern int  lo_close(PGconn *conn, int fd);
extern int  lo_read(PGconn *conn, int fd, char *buf, size_t len);
extern int  lo_write(PGconn *conn, int fd, char *buf, size_t len);
extern int  lo_lseek(PGconn *conn, int fd, int offset, int whence);
extern Oid  lo_creat(PGconn *conn, int mode);
extern int  lo_tell(PGconn *conn, int fd);

PGresult *PQprepare(PGconn *conn,
                    const char *stmtName,
                    const char *query,
                    int nParams,
                    const Oid *paramTypes);
  
  
PGresult *PQexecPrepared(PGconn *conn,
                         const char *stmtName,
                         int nParams,
                         const char * const *paramValues,
                         const int *paramLengths,
                         const int *paramFormats,
                         int resultFormat);
                         
int PQsendQueryParams(PGconn *conn,
                      const char *command,
                      int nParams,
                      const Oid *paramTypes,
                      const char * const *paramValues,
                      const int *paramLengths,
                      const int *paramFormats,
                      int resultFormat);
                      
int PQsendPrepare(PGconn *conn,
                  const char *stmtName,
                  const char *query,
                  int nParams,
                  const Oid *paramTypes);
                  
int PQsendQueryPrepared(PGconn *conn,
                        const char *stmtName,
                        int nParams,
                        const char * const *paramValues,
                        const int *paramLengths,
                        const int *paramFormats,
                        int resultFormat);                                                                                     
                                                
*/
