/*-------------------------------------------------------------------------
 *
 * pgtclCmds.c--
 *    C functions which implement pg_* tcl commands
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /usr/local/devel/pglite/cvs/src/libpgtcl/pgtclCmds.c,v 1.4 1995/03/11 01:00:50 jolly Exp $
 *
 *-------------------------------------------------------------------------
 */


#include <stdio.h>
#include <stdlib.h>
#include <tcl.h>
#include "libpq/pqcomm.h"
#include "pgtclCmds.h"
#include "pgtclId.h"

/* these are the environment variables used for getting defaults */
#define ENV_DEFAULT_PORT  "PGPORT"
#define ENV_DEFAULT_HOST  "PGHOST"
#define ENV_DEFAULT_TTY   "PGTTY"

static char* DefaultHost = "localhost";
static char* DefaultTTY = "/dev/null";
static int DefaultPort = 4321;


/**********************************
 * pg_connect
 make a connection to a backend.  
 
 syntax:
 pg_connect dbName [-host hostName] [-port portNumber] [-tty pqtty]]
 
 the return result is either an error message or a handle for a database
 connection.  Handles start with the prefix "pgp"
 
 **********************************/

int
Pg_connect(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{
    char *pqhost;
    char *pqtty;
    char *options = NULL;
    char *dbName; 
    int pqport;
    char *temp;
    int i;
    PGconn *conn;
    ConnStatusType status;
  
    if (argc == 1) {
	Tcl_AppendResult(interp, "pg_connect: database name missing\n", 0);
	Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pqtty]]", 0);
	return TCL_ERROR;
    
    }
    pqhost = (temp = getenv(ENV_DEFAULT_HOST)) ? temp : DefaultHost;
    pqtty = (temp = getenv(ENV_DEFAULT_TTY)) ? temp : DefaultTTY;
    /* use default settings for pg env */
    if (!(temp = getenv(ENV_DEFAULT_PORT)))
	pqport = DefaultPort;
    else
	pqport = atoi(temp);

    if (argc > 2) { 
	/* parse for pg environment settings */
	i = 2;
	while (i+1 < argc) {
	    if (strcmp(argv[i], "-host") == 0) {
		pqhost = argv[i+1];
		i += 2;
	    }
	    else
		if (strcmp(argv[i], "-port") == 0) {
		    pqport = atoi(argv[i+1]);
		    i += 2;
		}
		else
		    if (strcmp(argv[i], "-tty") == 0) {
			pqtty = argv[i+1];
			i += 2;
		    }
	            else if (strcmp(argv[i], "-options") == 0) {
			options = argv[i+1];
			i += 2;
		    }
		    else {
			Tcl_AppendResult(interp, "Bad option to pg_connect : \n",
					 argv[i], 0);
			Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pqtty]]",0);
			return TCL_ERROR;
		    }
	} /* while */
	if ((i % 2 != 0) || i != argc) {
	    Tcl_AppendResult(interp, "wrong # of arguments to pg_connect\n", argv[i],0);
	    Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pqtty]]",0);
	    return TCL_ERROR;
	}
    }
    dbName = argv[1];

    conn = (PGconn*)ckalloc(sizeof(PGconn));
    conn->Pfin = 0;
    conn->Pfout = 0;
    conn->port = 0;
    conn->dbName = (char*)ckalloc(strlen(dbName) + 1);
    strcpy(conn->dbName, dbName);
    conn->errorMessage = (char*)ckalloc(MAX_ERRMSG_LEN);
    conn->errorMessage[0]='\0';
  
   
    /* attempt to connect to database */
    status = Pgtcl_connectDB(dbName, pqhost, pqtty, options, pqport, conn);
  
    if (status == CONNECTION_OK) {
	sprintf(conn->errorMessage, "Connection OK");
	PgSetId(interp->result, (void*)conn);
	return TCL_OK;
    }
    else {
	Tcl_AppendResult(interp, "Connection to ", dbName, " failed\n", 0);
	Tcl_AppendResult(interp, conn->errorMessage, 0);
	return TCL_ERROR;
    }
}


/**********************************
 * pg_disconnect
 close a backend connection
 
 syntax:
 pg_disconnect connection
 
 The argument passed in must be a connection pointer.
 
 **********************************/

int
Pg_disconnect(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{
    PGconn *conn;
    char* connPtrName;

    if (argc != 2) {
	Tcl_AppendResult(interp, "Wrong # of arguments\n", "pg_disconnect connection", 0);
	return TCL_ERROR;
    }

    connPtrName = argv[1];
    if (! PgValidId(connPtrName)) {
	Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
	return TCL_ERROR;
    }
  
    conn = (PGconn*)PgGetId(connPtrName);
    Pgtcl_disconnectDB(conn);
    return TCL_OK;
}

/**********************************
 * pg_exec
 send a query string to the backend connection
 
 syntax:
 pg_exec connection query
 
 the return result is either an error message or a handle for a query
 result.  Handles start with the prefix "pgp"
 **********************************/

int
Pg_exec(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{
    PGconn *conn;
    PGresult *result;
    char* connPtrName;

    if (argc != 3) {
	Tcl_AppendResult(interp, "Wrong # of arguments\n",
			 "pg_exec connection queryString", 0);
	return TCL_ERROR;
    }
    connPtrName = argv[1];

    connPtrName = argv[1];
    if (! PgValidId(connPtrName)) {
	Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
	return TCL_ERROR;
    }
  
    conn = (PGconn*)PgGetId(connPtrName);
    result = Pgtcl_pgexec(conn, argv[2]);
    if (result) {
	PgSetId(interp->result, (void*)result);
	return TCL_OK;
    }
    else {
	/* error occurred during the query */
	Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC);
	return TCL_ERROR;
    }
    /* check return status of result */
    return TCL_OK;
}

/* keep this in same order as ExecStatusType in pgtclCmds.h */
static char* pgresStatus[] = {
    "PGRES_EMPTY_QUERY",
    "PGRES_COMMAND_OK",
    "PGRES_TUPLES_OK",
    "PGRES_BAD_RESPONSE",
    "PGRES_NONFATAL_ERROR",
    "PGRES_FATAL_ERROR"
};


/**********************************
 * pg_result
 get information about the results of a query
 
 syntax:
 pg_result result ?option? 
 
 the options are:
 -status  
 the status of the result
 -conn
 the connection that produced the result
 -assign arrayName
 assign the results to an array
 -numTuples
 the number of tuples in the query
 -attributes
 returns a list of the name/type pairs of the tuple attributes
 -getTuple tupleNumber
 returns the values of the tuple in a list
 -clear 
 clear the result buffer. Do not reuse after this
 **********************************/
int
Pg_result(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{
    char* resultPtrName;
    PGresult *result;
    char *opt;
    int i;
    int tupno;
    char arrayInd[MAX_MESSAGE_LEN];
    char *arrVar;

    if (argc != 3 && argc != 4) {
	Tcl_AppendResult(interp, "Wrong # of arguments\n",0);
	goto Pg_result_errReturn;
    }

    resultPtrName = argv[1];
    if (! PgValidId(resultPtrName)) {
	Tcl_AppendResult(interp, "First argument is not a valid query result\n", 0);
	return TCL_ERROR;
    }

    result = (PGresult*)PgGetId(resultPtrName);
    opt = argv[2];

    if (strcmp(opt, "-status") == 0) {
	Tcl_AppendResult(interp, pgresStatus[result->resultStatus], 0);
	return TCL_OK;
    }
    else if (strcmp(opt, "-conn") == 0) {
	PgSetId(interp->result, (void*)result->conn);
	return TCL_OK;
    }
    else if (strcmp(opt, "-clear") == 0) {
	freePGresult(result);
	return TCL_OK;
    }
    else if (strcmp(opt, "-numTuples") == 0) {
	sprintf(interp->result, "%d", result->ntups);
	return TCL_OK;
    }
    else if (strcmp(opt, "-assign") == 0) {
	if (argc != 4) {
	    Tcl_AppendResult(interp, "-assign option must be followed by a variable name");
	    return TCL_ERROR;
	}
	arrVar = argv[3];
	/* this assignment assigns the table of result tuples into a giant
	   array with the name given in the argument,
	   the indices of the array or (tupno,attrName)*/
	for (tupno = 0; tupno<result->ntups; tupno++) {
	    for (i=0;i<result->numAttributes;i++) {
		sprintf(arrayInd, "%d,%s", tupno, result->attDescs[i].name);
		Tcl_SetVar2(interp, arrVar, arrayInd, result->tuples[tupno][i],
			    TCL_LEAVE_ERR_MSG);
	    }
	}
	Tcl_AppendResult(interp, arrVar, 0);
	return TCL_OK;
    }
    else if (strcmp(opt, "-getTuple") == 0) {
	if (argc != 4) {
	    Tcl_AppendResult(interp, "-getTuple option must be followed by a tuple number");
	    return TCL_ERROR;
	}
	tupno = atoi(argv[3]);
	
	if (tupno >= result->ntups) {
	    Tcl_AppendResult(interp, "argument to getTuple cannot exceed number of tuples - 1");
	    return TCL_ERROR;
	}
	    

	/* we take advantage of the fact that a tuple is stored as a array
	   of char*. */
	interp->result = Tcl_Merge(result->numAttributes, result->tuples[tupno]);
	interp->freeProc = (Tcl_FreeProc*) free;
	return TCL_OK;
    }
    else if (strcmp(opt, "-attributes") == 0) {
	for (i=0;i<result->numAttributes;i++) {
	    Tcl_AppendResult(interp, result->attDescs[i].name, " ", 0);
	}
	return TCL_OK;
    }
    else   { 
	Tcl_AppendResult(interp, "Invalid option");
	goto Pg_result_errReturn;
    }
  

 Pg_result_errReturn:
    Tcl_AppendResult(interp, 
		     "pg_result result ?option? where ?option is\n", 
		     "\t-status\n",
		     "\t-conn\n",
		     "\t-assign arrayVarName\n",
		     "\t-numTuples\n",
		     "\t-attributes\n"
		     "\t-getTuple tupleNumber\n",
		     "\t-clear\n",
		     0);
    return TCL_ERROR;
  

}
