/**************************************************************************
 * Source Id :
 *
 * $Id: diagrel.cpp,v 1.3 2001/04/20 10:15:01 georgetasker Exp $
 *-------------------------------------------------------------------------
 * Project Notes :
 *
 *  Diamond Base
 *  ============
 *      A solid database implementation, spurred on by the continuing
 *  Metal (Lead) Base saga.
 *
 *  Project Team :
 *        A. Davison
 *        K. Lentin
 *        D. Platt
 *
 *	Project Commenced : 05-02-1993
 *
 *-------------------------------------------------------------------------
 *  Module Notes :
 *
 *
 * The diaGRel class is used to provide generalised access to relations
 * without the need for associated compiled comparison code. It effectively
 * sits on top of a diaRel class and provides comparison, load and store
 * support for the object methods (must inherit an object base class too).
 *
 *  Original Author : Daz
 *
 * See no evil, hear no evil, don't program this ... 
 *
 * Abandon hope, all ye who enter here .. 
 *
 *************************************************************************/

// FOR NOW::
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#include <diamond/dbconf.h>

#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <string.h>
#include <assert.h>

#if HAVE_MALLOC_H && !MALLOC_H_INCLUDED
extern "C" {
#include <malloc.h>
}
#define MALLOC_H_INCLUDED 1
#endif

#include <diamond/diagrel.h>
#include <diamond/ordcomp.h>

//
// Now for my first trick, I will attempt to
// examine the header of a db file.

extern diamondBase theDiamondBase;

//
// Implementation of the diaGRel methods.
// ---

diaGRel::diaGRel(const char *relName, void* newData) : diaRel(relName)
{
	if (status != db_ok)
		return;

	//
	// Allocate a transfer area or use a given one.
	//
	if (newData) {
		theData = newData;
	} else {
		theData = new char[objData->dataLength];
	}

	//
	// If they have a unique field, set up a pointer to it, otherwise
	// leave it zeroed.
	//
	if (objData->uniqOff != -1) {
		uniqOffset = (int32 *)((char *)theData+objData->uniqOff);
	} else {
		uniqOffset = 0;
	}

	//
	// All the dbstring stuff
	//
	if (numStrings())
	{
		theArray = new _db_strStruct[numStrings()];
		for (int i=0; i<numStrings(); i++)
		{
			if (objData->fieldType[objData->stringField[i]] == D_STRING)
				theArray[i].theString = new dbString;
			else
				theArray[i].theString = new dbData;
			theArray[i].theLong = 
				(int32*)((char*)theData+objData->fieldOffset[
					objData->stringField[i]]);
			*theArray[i].theLong = 0;
		}
	} else {
		theArray = 0;
	}
}

diaGRel::~diaGRel()
{
	delete [] theData;
}


diaGRel::operator void*() const
{
	return theData;
}

// These are just cues to the person using the
// database, that they should have the data ready for fetching.
// This is to make inter process transfer efficient when the engine
// and the application aren't the same process. Don't worry about them
// for now.

void diaGRel::getData(void) const
{
}
void diaGRel::putData(void) const
{
}
void diaGRel::startData(void) const
{
}
void diaGRel::endData(void) const
{
}
int32 diaGRel::getDataLength(void) const
{
	return objData->dataLength;
}

//
// Extract the data associated with a given key.
// We need to get the appropriate fields, and concate the data
// together. There are some nasty details - we reorder the data
// so that it packs nicely in a word-aligned format. For example,
// if a key is made fdrom a field of 50 characters and 1 long, then
// the long is actually stored first, although the character field
// would be compared first. This avoids alignment problems.
//

pKeyType diaGRel::getKey(const int32 keyNum) const
{
	//
	// Retrieve the data associated with keyNum.
	//

	//
	// Allocate space to hold the key.
	//
	char *data = new char[objData->keyLength[keyNum]];
	int32 i,j;
	j = 0;

	//
	// Copy the data.
	// ------------------------- 
	// 
	// Go through each field in the index in turn.  
	// 
	
	for(i=0;i<MAX_FIELDS_IN_INDEX;i++) { 
		int32 k = objData->indexedOnP[keyNum][i]; 
		if (k==-1) break; 

		// Copy each field of the record according to 
		// its type.


		// Switch statement is probably overkill for this kind of
		// situation.
		switch(objData->fieldType[k]) {
			case D_UNIQUE:
			case D_STRING:
			case D_DATA:
			case D_LONG:
			case D_ULONG:
			case D_DOUBLE:
			case D_MONEY:
			case D_DATE:
			case D_SHORT:
			case D_USHORT:
			case D_FLOAT:
				// For most things, just copy a int32 across.
				// It might not be correct for some of the above
				// data types, but we don't use them - I think ?
				*(int32 *)(data+j) = *(long *)((char *)theData + objData->fieldOffset[k]);
				break;
			case D_CHAR:
			case D_ICHAR:
				strncpy(data+j,((char*)theData+objData->fieldOffset[k]),
					(size_t)(objData->fieldLength[k]));
				*(data+j+objData->fieldLength[k]-1) = 0;
				break;
			case D_BYTE:
				copyMem(((char*)theData+objData->fieldOffset[k]), data+j,
					(size_t)(objData->fieldLength[k]));
				break;
			default:
				abort();
		}
		j+=objData->fieldLength[k];
	}
	return data;
}
bool diaGRel::hasUnique(void) const
{
	return (uniqOffset!=0)?true:false;
}
int32 diaGRel::getUnique(void) const
{
	return *uniqOffset;
}
void diaGRel::setUnique(const int32 uniq)
{
	*uniqOffset = uniq;
}
int32 diaGRel::getKeyLength(const int32 keyNum) const
{
	if (keyNum==-1) return 0;
	return objData->keyLength[keyNum];
}

void diaGRel::setKey(const pKeyType newKey, const int32 keyNum)
{
	// Unpack a key that is given to us.
	int32 i,j;
	j = 0;

	for(i=0;i<MAX_FIELDS_IN_INDEX;i++) {
		int32 k = objData->indexedOnP[keyNum][i];
		if (k==-1) break;

		// Copy each field of the record according to
		// its type.
		// Switch statement is probably overkill for this kind of
		// situation.
		switch(objData->fieldType[k]) {
			case D_UNIQUE:
			case D_STRING:
			case D_DATA:
			case D_LONG:
			case D_ULONG:
			case D_DOUBLE:
			case D_MONEY:
			case D_DATE:
			case D_SHORT:
			case D_USHORT:
			case D_FLOAT:
				// For most things, just copy a long across.
				// It might not be correct for some of the above
				// data types, but we don't use them - I think ?
				*(int32 *)((char *)theData + objData->fieldOffset[k]) =
					*(int32 *)(newKey+j);
				break;
			case D_CHAR:
			case D_ICHAR:
				strncpy(
					(char *)theData+objData->fieldOffset[k],
					newKey + j, (size_t)objData->fieldLength[k]);
				*((char*)theData+objData->fieldOffset[k]+
					objData->fieldLength[k]-1) = 0;
				break;
			case D_BYTE:
				copyMem(newKey + j, (char *)theData+objData->fieldOffset[k],
					(size_t)objData->fieldLength[k]);
				*((char*)theData+objData->fieldOffset[k]+
					objData->fieldLength[k]-1) = 0;
				break;
			default: 
				abort();
		}
		j+=objData->fieldLength[k];
	}
}

int32 diaGRel::compare(const pKeyType key, const int32 keyNum) const
{
	// This is the nasty one which does a lot of hard work.
	// It performs the necessary comparisons on the necessary
	// keys. The actual fields which are to be compared come from
	// various arrays.

	// Retrieve the data associated with keyNum.
	int32 i,j,k,res;
	uint32 m;

	// Compare each field in turn - as soon as they
	// differ, return +1 or -1 - if they match all the
	// way down, return 0.

	// j tracks the index into the packed key supplied.
	for(i=0;i<MAX_FIELDS_IN_INDEX;i++) {
		k = objData->indexedOnC[keyNum][i];
		//cout << "Compare   i=" << i << "   k=" << k << endl;
		if (k==-1) return 0; // Have reached end with no differences.

		j = objData->keyOffset[keyNum][i];

		// Compare each field of the record according to
		// its type.

		switch(objData->fieldType[k]) {
			case D_STRING:
				// String comparison unimplemented
				abort();
			case D_DATA:
				// String comparison unimplemented
				abort();
			case D_UNIQUE:
				if ((res = compare_uniqueType(
					(pKeyType)theData + objData->fieldOffset[k],
					(pKeyType)key + j))!=0) {
					return res;
				}
				break; // continue comparison.
			case D_LONG:
				if ((res = compare_int32(
					(pKeyType)theData + objData->fieldOffset[k],
					(pKeyType)key + j))!=0) {
					return res;
				}
				break; // continue comparison.
			case D_ULONG:
				if ((res = compare_uint32(
					(pKeyType)theData + objData->fieldOffset[k],
					(pKeyType)key + j))!=0) {
					return res;
				}
				break; // continue comparison.
			case D_DOUBLE:
				if ((res = compare_double(
					(pKeyType)theData + objData->fieldOffset[k],
					(pKeyType)key + j))!=0) {
					return res;
				}
				break; // continue comparison.
			case D_MONEY:
				if ((res = compare_moneyType(
					(pKeyType)theData + objData->fieldOffset[k],
					(pKeyType)key + j))!=0) {
					return res;
				}
				break; // continue comparison.
			case D_DATE:
				if ((res = compare_dateType(
					(pKeyType)theData + objData->fieldOffset[k],
					(pKeyType)key + j))!=0) {
					return res;
				}
				break; // continue comparison.
			case D_SHORT:
				if ((res = compare_short(
					(pKeyType)theData + objData->fieldOffset[k],
					(pKeyType)key + j))!=0) {
					return res;
				}
				break; // continue comparison.
			case D_USHORT:
				if ((res = compare_ushort(
					(pKeyType)theData + objData->fieldOffset[k],
					(pKeyType)key + j))!=0) {
					return res;
				}
				break; // continue comparison.
			case D_FLOAT:
				if ((res = compare_float(
					(pKeyType)theData + objData->fieldOffset[k],
					(pKeyType)key + j))!=0) {
					return res;
				}
				break; // continue comparison.
			case D_ICHAR:
				//cout << "Compare:" << (char *)(theData+objData->fieldOffset[k]) << ":" << (char *)(key + j) << ":" << endl;
				if ((res = strncasecmp(
					(char *)theData+objData->fieldOffset[k], key + j,
					(size_t)objData->fieldLength[k]))!=0) {
					return res;
				}
				break;
			case D_CHAR:
				//cout << "Compare:" << (char *)(theData+objData->fieldOffset[k]) << ":" << (char *)(key + j) << ":" << endl;
				if ((res = strncmp(
					(char *)theData+objData->fieldOffset[k], key + j,
					(size_t)objData->fieldLength[k]))!=0) {
					return res;
				}
				break;
			case D_BYTE:
				//
				// Compare from MSB to LSB
				//
				{
					unsigned char *a = (unsigned char*)theData + 
						objData->fieldOffset[k];
					unsigned char *b = (unsigned char *)(pKeyType)key + j;

					for(m=0;m<(size_t)objData->fieldLength[k];m++,a++,b++) {
						if (*a < *b) return -1;
						if (*a > *b) return  1;
					}
				}
				break;
				
			default:    
				abort();
		}
	}
	return 0; // All fields done up to MAX FIELDS, and no difference.
}

bool diaGRel::isUnique(const int32 keyNum) const
{
	// Return true if the index number 'keyNum' includes
	// a unique field.
	if (keyNum == -1) return false; // Not sure why this is here.
	return objData->containsUniq[keyNum]?true:false;
}

int32
diaGRel::numStrings(void) const
{
	return objData->stringCount;
}

const _db_strStruct*
diaGRel::getArray(void) const
{
	return theArray;
}

dbData*
diaGRel::getString(const int32 num) const
{
	return theArray[num].theString;
}

int32*
diaGRel::getLong(const int32 num) const
{
	return theArray[num].theLong;
}

int32
diaGRel::fieldNum(const char* name) const
{
	for (uint32 i=0; i<objData->numFields; i++)
		if (!strcmp(objData->fieldName[i], name))
			return i;
	return -1;
}

///////////////////////////////////////////////////////////////////////////
// Revision History:
//
// $Log: diagrel.cpp,v $
// Revision 1.3  2001/04/20 10:15:01  georgetasker
// Cygwin compilation fixes, compilation speed enhancements
// Code modified to get rid of some compiler warnings
//
// Revision 1.2  2001/04/19 21:12:54  georgetasker
// cygwin compilation fixes
//
// Revision 1.1  2001/04/04 12:05:49  aleksandras
// forgotten diamond .cpps
//
// Revision 1.23  1996/08/20  13:25:23  daz
// Changes by Alex Zeltser to support NT using MSVC.
//
// Revision 1.22  1996/08/06  10:46:07  daz
// Changes to incorporate the 'byte' type and the Windows NT patches.
//
// Revision 1.21  1996/03/05  22:04:29  dmb
// removed ^M.
//
// Revision 1.20  1996/03/05  21:42:35  dmb
// Preliminary modifications to get Dos/Windows 0.4 portability.
//
// Revision 1.19  1996/02/01  22:09:06  dmb
// long->int32
// ,
//
// Revision 1.18  1994/12/12  03:44:44  kevinl
// stdlib.h, dbconf.h
//
// Revision 1.17  1994/09/30  04:51:04  darrenp
// Comments
// Fixed keyOffset stuff
//
// Revision 1.16  1994/02/03  05:54:19  kevinl
// Moved include files to diamond/ and fixed HAVE_MALLOC_H
//
// Revision 1.15  1994/01/28  13:06:03  kevinl
// Longs initialised for offsets.
//
// Revision 1.14  1993/12/23  22:58:48  kevinl
// dbErr uses dbString, autoconf stuff, moved logs, added const and size_t
//
// Revision 1.13  1993/11/18  02:57:05  kevinl
// Moved logs
//
// Revision 1.12  1993/11/03  12:21:12  davison
// Ahh - maybe I cant count parenthesis ?
//
// Revision 1.11  1993/11/03  12:17:27  davison
// Made implicit boolean tests explicit in comparison functions.
//
// Revision 1.10  1993/11/03  11:05:19  kevinl
// Missing char* cast on theData
//
// Revision 1.9  1993/11/03  10:04:46  kevinl
// Added ichar and utils.{h,cc}
//
// Revision 1.8  1993/10/31  16:25:15  darrenp
// uniqueOffset not set correctly when no unique present - and
// has unique failed, and writing to unique gave wierd results.
//
// Revision 1.7  1993/10/28  07:49:01  kevinl
// Added diaGRel dbString/dbData support
//
// Revision 1.6  1993/10/18  11:38:31  kevinl
// Added 0 on end of char array fields
// Added fieldNum
//
// Revision 1.5  1993/10/05  07:32:11  kevinl
// Moved all info out and into dbObj. Made all accesses to relation data
// via dbObjData pointer.
//
// Revision 1.4  1993/09/28  13:03:43  kevinl
// Moved some includes, fixed the extern diamondBase
// Added UNIQUE to some of the assignment functions
// Added the result check to the strncmp in compare D_CHAR
//
// Revision 1.3  1993/09/27  05:22:25  darrenp
// Removed test code.
//
// Revision 1.1  1993/09/26  16:06:20  darrenp
// Initial revision
//
///////////////////////////////////////////////////////////////////////////
