/*  $Id: pdu.c,v 1.2 2000/12/26 19:30:23 remlali Exp $
 *
 * Copyright 2000 Larry Liimatainen
 *  pdu.c -- data encapsulation
 * 
 *  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.
 *
 *  pdu.c -- low level package transfer
 *  
 */


/*   
 * All these functions operate on a packet that is
 * sent between database daemon and the client API.
 *
 *
 * PROTOCOL LAYOUT
 *
 * +--------+--------+--------+--------+---------+-----+
 * | Action | Length | ObjLen | Object | Members | ... |
 * +--------+--------+--------+--------+---------+-----+
 * <------------- Header -------------><--- PDU ------->
 *                                        * We are here
 *
 * HEADER:
 *
 *    Action is a 32bit integer telling upper layer what 
 *    to do with data in PDU.
 *
 *    Length is a 32bit integer and has the size of header
 *    length and PDU length.
 *
 *    ObjLen is the length of Object in header.
 *
 *    Object is a single piece of data, used to represent
 *    a database table.
 * 
 * PDU
 *
 *    The PDU consist of one or more Members. Each
 *    member has following layout:
 *
 *    +--------------+------------+------------+
 *    | memberLength | memberType | MemberData | 
 *    +--------------+------------+------------+
 *
 *    memberLength is the length of the Member.
 *
 *    memberType tells upper layer how to read
 *    data in MemberData, ie a string, integer..
 *
 *    memberData holds actual data, that usually
 *    represent a field in a database row that is
 *    processed.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "dbapi.h"
#include "ddserver.h"

/* debug_decode_output()
 *
 * use this to get a quick look at your packet, before entering gdb
 * n is offset
 * packet is source of output
 * offset is offset to rewind
 * len is how much data to output
 *
 */
void
debug_decode_output(gint *n, unsigned char *packet, gint offset, gint len)
{
unsigned char c;
gint i;

  fprintf(stderr,"\nERROR_DATADUMP: n is %d, start is n - %d, printing %d bytes\n", *n, offset, len);
  i = *n;
  i -= offset;
  for(;len;len--){
    c = *(packet +i);
    fprintf(stderr,"%x ", c);
    i++; 
  }
  fprintf(stderr,"\n");
}

/* get_member()
 * decodes depending on memberType to buffer or integer
 *
 *
 */
                                                          
gint get_member(gint *n, gpointer packet, gpointer buffer){
gint ml, mt;                                                                     
            
  memcpy(&ml, packet+*n, sizeof(gint));     /*get MemberLen*/
  *n += sizeof(gint);                                        
  if(ml<8){
    fprintf(stderr,"MemberLen is invalid\n");
    debug_decode_output(n, packet, 20, 40);
    return -1;
  }                     
  memcpy(&mt, packet+*n, sizeof(gint));     /*get MemberType*/
  *n += sizeof(gint);
  if(ml == 8){                              /* we got a empty memberrecord, its OK ( SQL field is NULL or something)*/
    switch(mt)
      {
        case PDUMEMTYPE_INT:
          *(gint *)buffer = 0;
          return PDUMEMTYPE_INT;
        case PDUMEMTYPE_STR:
          *(gchar *)buffer = 0;
          return PDUMEMTYPE_STR;
        case PDUMEMTYPE_DOUBLE:
          *(gdouble *)buffer = 0;
          return PDUMEMTYPE_DOUBLE;
      }
  }
  switch(mt){
    case PDUMEMTYPE_INT:
      memcpy(buffer, packet+*n, sizeof(gint));
      *n += sizeof(gint);
      return PDUMEMTYPE_INT;
    case PDUMEMTYPE_DOUBLE:
      memcpy(buffer, packet+*n, sizeof(gdouble));
      *n += sizeof(gdouble);
      return PDUMEMTYPE_DOUBLE;
    case PDUMEMTYPE_STR:
      ml -= sizeof(gint) * 2;		/*length of memberData is: *memberLen - sizeof(memberLen) - sizeof(memberType)*/
      memcpy(buffer, packet+*n, ml);
      *((gchar *)(buffer+ml)) = 0; //dont sure this works....
      *n += ml;				/* advance packetpointer with sizeof(memberData) */
      return PDUMEMTYPE_STR;
    default:     /*decode error*/
      fprintf(stderr,"get_member(): APP_ERR packet decode error, undefined struct member\n");
      debug_decode_output(n, packet, 20, 40);
      return 0;
  }
  return 0;
}

void 
add_guint (gint *n, gpointer packet, guint data)
{
gint tmp;
  
  tmp = sizeof(guint) * 3;				/* MemberLength is set to Len of itself + MemberType + MemberData*/
  memcpy(packet+*n, &tmp, sizeof(guint));		/* cpy MemberLength */
  *n += sizeof(guint);
  tmp = PDUMEMTYPE_INT;
  memcpy(packet+*n, &tmp, sizeof(guint));		/* cpy MemberType */
  *n += sizeof(guint);
  memcpy(packet+*n, &data, sizeof(guint));		/* cpy MemberData */
  *n += sizeof(guint);
}
void 
add_gdouble (gint *n, gpointer packet, gdouble data)
{
gint tmp;
  
  tmp = sizeof(guint) * 2 + sizeof(gdouble);		/* calculate MemberLength */
  memcpy(packet+*n, &tmp, sizeof(guint));		/* cpy MemberLength */
  *n += sizeof(guint);
  tmp = PDUMEMTYPE_DOUBLE;
  memcpy(packet+*n, &tmp, sizeof(guint));		/* cpy MemberType */
  *n += sizeof(guint);
  memcpy(packet+*n, &data, sizeof(gdouble));		/* cpy MemberData */
  *n += sizeof(gdouble);
}

void
add_asciiz (gint *n, gpointer packet, gpointer str)
{
static gchar nullstr[] = "";
gint  j, tmp;

  if(!str) str = &nullstr;
  else j   = strlen ((gchar *)str);
  tmp = (sizeof (guint) * 2) + j;		/*tmp holds length of MemberLen + MemberType + MemberData */
  memcpy (packet+*n, &tmp, sizeof (guint));
  *n += sizeof (guint);
  tmp = PDUMEMTYPE_STR;
  memcpy (packet+*n, &tmp, sizeof (guint));
  *n += sizeof (guint);
  memcpy (packet+*n, str, j);
  *n += j;
}

gint
set_pdu_header(gpointer packet, gint type, gchar *table_name)
{
gint tablen;

  tablen = strlen(table_name);
  memcpy(packet, &type, sizeof(gint));				/*set action type*/
  memcpy(packet+sizeof(gint), &tablen, sizeof(gint));	        /*copy table name len*/
  memcpy(packet+(sizeof(gint) * 2), table_name, tablen);	/*copy table name*/
  return (sizeof(gint) * 3)+tablen;                             /*header length*/
}
