/*  $Id: table-common.c,v 1.3 2001/02/23 11:52:58 remlali Exp $
 *
 * Copyright 2000 Larry Liimatainen
 * table-common.c -- table conversion routines
 *
 *  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 of the License, 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 program; if not, write to the Free Software
 *  Foundation, Inc.,  59 Temple Place - Suite 330, Cambridge, MA 02139, USA.
 *
 *  table-common.c
 *
 */

/* common functionallity to the
   database.
   This is done using table and
   field descriptors.

   This file also contains
   functions to operate on the
   data using the table/field 
   descriptors.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "debug.h"

#include "tables.h" /*FIX: be homogenous using dbapi.h instead */
#include "ddserver.h"
#include "table-common.h"
#include "pdu.h"

extern void debug(); /*this is for gdb*/
GList * table_desc = NULL; /*GList of table descriptors */

static gint db_table_field_check_private ( guint type )
{
  if ( 
  type == TD_PGUINT ||
  type == TD_PGINT  ||
  type == TD_PGSTR  ||
  type == TD_PGL    ||
  type == TD_PGDOUBLE
  ) return 1;
  return 0;
}

/* db_lookup_field_offset()
*
* given a database field name,
* return is the offset into
* a struct member.
*
* RETURN:
* offset to member in struct.
* FAIL: -1
*
*/
gint db_lookup_field_offset(db_table_descriptor *table, gchar *name)
{
GList *gl;
db_field_descriptor *field;

  gl = table->fields;
  while(gl){
    field = gl->data;
    if(!strcmp(name, field->name)) return field->offset;
    gl = gl->next;
  }
  return -1;
}

gint db_lookup_field_type(db_table_descriptor *table, gchar *fieldname)
{
GList *gl;
db_field_descriptor *field;

  gl = table->fields;
  while(gl){
    field = gl->data;
    if(!strcmp(fieldname, field->name)) return field->type;
    gl = gl->next;
  }
  return 0;
}

db_table_descriptor *
db_lookup_table(gchar *tablename)
{
GList *gl;
db_table_descriptor * table;

  gl = table_desc;
  while(gl){
    table = gl->data;
    if(!strcmp(tablename, table->name)) return table;
    gl = gl->next;
  }
  return 0;
}


/*****************************************************************************
**
**  decode_sql()
**
** takes a SQL result and builds membersRecords.
**
** arguments:
** packet, here we put resulting memberrecords
** sql,    this is the SQL result to use.
** numfields, is how many fields per row to expect
** *n,     is packet offset
** tpl,    total packet length.
**
**
** returns:
**
**  TRUE  --  If the operation succeeded
**  FALSE --  If the operation failed
**
*****************************************************************************/
/*about numfields, it feels more important to make numfields select
  if user wants primary key decoded, or if he wants whole structure. */

gboolean
decode_sql(db_table_descriptor *table, 
           gpointer packet, 
           G_sql_query *sql, 
           gint numfields, 
           gint *n)
{
  gchar *data;
  gchar nulstr[] = "";
  gchar nulint = "0";
  guint i = 0;
  GList *gl;
  db_field_descriptor *field;
  gdouble g;

  if(!table) return 0;
  gl = table->fields;

  while(gl){
    field = gl->data;
    if(db_table_field_check_private(field->type)){
      gl = gl->next;
      continue;
    }
    switch(field->type){
      case TD_GUINT:
      case TD_GINT:
         if(!(data = g_sql_field_pos(sql, i))) data = &nulint;
         add_guint(n, packet, strtol(data,0,0));
      break;
      case TD_GDOUBLE:
         if(!(data = g_sql_field_pos(sql, i))) data = &nulint;
         add_gdouble(n, packet, g_strtod(data,0));
      break; 
      case TD_GSTR:
        if(!(data = g_sql_field_pos(sql, i))) data = nulstr;
        add_asciiz(n, packet, data);
      break;
    }
    i++;    /* increase SQL result field position */
    gl = gl->next; /* next field descriptor */
  }

  return TRUE;
}

/* encode_members()
 *
 * This function takes a user table and makes
 * memberrecords of all structure members.
 * remember, to compile a valid packet to send
 * to database daemon, one must create a header
 * and after calling this function, set total
 * packet length.
 * The user is described by table descriptors.
 *
 * Arguments:
 * packet     This is destination of all members created
 * data       data this is a pointer to a DB_host table
 *
 * Returns:
 * how much data that has been encoded
 * ERROR: 0
 */

gint
encode_members(db_table_descriptor *table, gpointer packet, gpointer data)
{
  gint n = 0;

  GList *gl;
  db_field_descriptor *field;

  if(!table) return 0;
  gl = table->fields;
  if(!gl) return 0;
  while(gl){
    field = gl->data;
    if(db_table_field_check_private(field->type)){
      gl = gl->next;
      continue;
    }
    add_asciiz(&n, packet, field->name);
    switch(field->type){
      case TD_GUINT:
      case TD_GINT:

        add_guint (&n, packet, *((guint *) (data + field->offset)));
      break;
      case TD_GDOUBLE:
        add_gdouble (&n, packet, *((gdouble *) (data + field->offset)));
      break;
      case TD_GSTR:
        add_asciiz(&n, packet, *((gpointer *) (data + field->offset)));
      break;
    }
    gl = gl->next; /* next field descriptor */
  }

  return n;
}

/* decode_members()
*
*
*/
gpointer
decode_members(db_table_descriptor *table, gpointer packet, gint *n, gint plen)
{

#if 0
  union _member { 
  gchar string[2049];
  guint integer;
  gdouble dbl;
  } member;
#endif

pdumember member;

static gpointer ret;
GList * gl;
db_field_descriptor *field;
DB_dbfieldconfig *dfc;

  if(*n >= plen) return 0;

  if(!(ret = g_malloc0 (table->size) ))
    {
      fprintf(stderr,"ERR: couldn't alloc()\n");
      fprintf(stderr, "failed to alloc %u memory for table name: %s\n", table->size, table->name);
      return 0;
    }

  gl = table->fields;
  while(gl){
    field = gl->data;
    if(db_table_field_check_private(field->type)){
      gl = gl->next;
      continue;
    }
    get_member(n, packet, &member);
    switch(field->type){
      case TD_GUINT:
      case TD_GINT:
        *(guint * ) (ret + field->offset) = member.integer;
      break;
      case TD_GDOUBLE:
        *(gdouble * ) (ret + field->offset) = member.dbl;
      break;
      case TD_GSTR:
        *((gpointer *) (ret + field->offset)) = g_strdup(member.string);
      break;
    }
    if(*n > plen) return 0;
    gl = gl->next; /* next field descriptor */
  }
dfc = ret;
  return ret;
}


/*****************************************************************************
**
** encode_sql()
**
** Build a SQL query out of member records found in the packet.
**
** Arguments:
** packet    is a pointer to beginning of the packet
** sqlstr    is a SQL string we are building
** querytype is what type of SQL string to build
** *n        is how far into the packet we have decoded
** tpl       is total packet length, so we know when to stop decoding Members
**
** Returns:
** TRUE  --  If the operation succeeded
** FALSE --  If the operation failed
**
*****************************************************************************/

gboolean
encode_sql(db_table_descriptor *table, gpointer packet, gchar *sqlstr, gint querytype, gint *n, gint tpl)
{
static gchar mds[1000];
static gchar mds2[1000];
static gint a,b,i,rowid;

#if 0
  union _member { 
  gchar string[2049];
  guint integer;
  gdouble dbl;
  } member;
  union _member2 { 
  gchar string[2049];
  guint integer;
  gdouble dbl;
  } member2;
#endif

pdumember member, member2;

  switch(querytype){
    case SQL_INSERT: /*this method should support all tables*/
      sprintf(sqlstr, "INSERT INTO %s VALUES(", table->name);
      while(*n < tpl){
        a = get_member(n, packet, &member);
        if( a != PDUMEMTYPE_STR ){
          fprintf(stderr,"encode_sql(): SQL_INSERT, non-string field name type.\n");
        }
        switch(get_member(n, packet, &member)){
          case PDUMEMTYPE_INT:
            sprintf(mds2, "%d,", member.integer);
            strcat(sqlstr, mds2);
          break;
          case PDUMEMTYPE_STR:
            sprintf(mds2, "'%s',", member.string);
            strcat(sqlstr, mds2);
          break;
          case PDUMEMTYPE_DOUBLE:
            sprintf(mds2, "'%f',", member.dbl);
            strcat(sqlstr, mds2);
          break;
          default:
            fprintf(stderr,"encode_sql(): APP_ERR generic packet decode error, at packetpointer %d.\n", *n);
            return FALSE;
        }
      }
      i = strlen(sqlstr);
      *((char *)sqlstr+(--i)) = 0;              /*remove last comma*/
      strcat(sqlstr, ")");
    break;
    case SQL_SELECT:
      sprintf(sqlstr, "SELECT * FROM %s WHERE ", table->name);
      if(!(*n < tpl)){ /* NO memberrecords */
        i = strlen(sqlstr);
        *((char *)sqlstr+i-1) = 0;              /*remove 'WHERE '*/
      }
      while(*n < tpl){
        a = get_member(n, packet, &member);           /* get the field identifier */
        b = get_member(n, packet, &member2);         /* get the field value */
        switch(a){
          case PDUMEMTYPE_STR:
          case PDUMEMTYPE_DOUBLE:
          break;
          case -1:
            return -1;
          default:
            fprintf(stderr,"encode_sql(): SQL_SELECT: expected a field name of string-type.\n");
            return FALSE;
        }

        strcat(sqlstr, member.string);
        strcat(sqlstr, " = ");

        switch(b){
          case PDUMEMTYPE_INT:                          /* its the value of the field */
            sprintf(mds, "%d AND ", member2.integer);
            strcat(sqlstr, mds);
            break;
          case PDUMEMTYPE_DOUBLE:                          /* its the value of the field */
            sprintf(mds, "%f AND ", member2.dbl);
            strcat(sqlstr, mds);
            break;
          case PDUMEMTYPE_STR:                          /* its the value of the field */
            sprintf(mds, "'%s' AND ", member2.string);
            strcat(sqlstr, mds);
            break;
          case -1:
            return -1;
          default:
            fprintf(stderr,"encode_sql(): SQL_SELECT:, unknown field data type.\n");
        }
      } /*end of while decoding member records */
      i = strlen(sqlstr);
      *((char *)sqlstr+i-5) = 0;              /*remove last ' AND'*/
    break;
    case SQL_DELETE:
      get_member(n, packet, &member); /* fetch ROWID field name, don't care about it*/
      get_member(n, packet, &member2); /* fetch ROWID value */
      sprintf(sqlstr, "DELETE FROM %s WHERE _ROWID = %d", table->name, member2.integer);
    break;
    case SQL_SELECT_PRI:
      get_member(n, packet, &member); /* fetch ROWID */
      get_member(n, packet, &member2); /* fetch ROWID */
      sprintf(sqlstr, "SELECT * FROM %s WHERE _ROWID = %d", table->name, member2.integer);
    break;
    case SQL_SELECT_ALL:
      sprintf(sqlstr, "SELECT * FROM %s", table->name);
    break;
    case SQL_UPDATE:
      a = get_member(n, packet, &member);
      b = get_member(n, packet, &member2);
      if(a != PDUMEMTYPE_STR || b != PDUMEMTYPE_INT ){
        fprintf(stderr,"encode_sql(): SQL_UPDATE, expected a proper encoded rowid\n");
        return FALSE;
      }
      rowid = member2.integer;
      sprintf(sqlstr, "UPDATE %s SET ", table->name);
      while(*n < tpl){
        a = get_member(n, packet, &member);           /* get the field identifier */
        b = get_member(n, packet, &member2);         /* get the field value */
        switch(a){
          case PDUMEMTYPE_STR:
          break;
          case -1:
            return FALSE;
          default:
            fprintf(stderr,"encode_sql(): SQL_UPDATE: expected a field name of string type.\n");
            return FALSE;
        }

        strcat(sqlstr, member.string);
        strcat(sqlstr, " = ");

        switch(b){
          case PDUMEMTYPE_INT:                          /* its the value of the field */
            sprintf(mds, "%d, ", member2.integer);
            strcat(sqlstr, mds);
          break;
          case PDUMEMTYPE_DOUBLE:                          /* its the value of the field */
            sprintf(mds, "%f, ", member2.dbl);
            strcat(sqlstr, mds);
          break;
          case PDUMEMTYPE_STR:                          /* its the value of the field */
            sprintf(mds, "'%s', ", member2.string);
            strcat(sqlstr, mds);
          break;
          case -1:
            return FALSE;
          default:
            fprintf(stderr,"encode_sql(): SQL_UPDATE: unknown field data type.\n");
        }
      } /*end of while decoding member records */
      i = strlen(sqlstr);
      *((char *)sqlstr+i-2) = 0;              /*remove last ','*/
      sprintf(mds, " WHERE _rowid = %d", rowid);
      strcat(sqlstr, mds);
    break;
    default:
      fprintf(stderr,"encode_sql(): APP_ERR packet decode error, undefined SQL command\n");
      return FALSE;
  }
  /* SQL string is built, it is now ready to send to SQL database */
  //fprintf(stderr,"SQL STRING: %s\n", (char *) sqlstr);
  return TRUE;
}

