///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoBTreeValue.cc
// -----------------
// Cego index value class implementation
//     
// Design and Implementation by Bjoern Lemke               
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoBTreeValue
// 
// Description: This class handles all operations on btree values. Since the values are sequentially stored
//              in a byte array, conversion methods are required to store, retrieve and for compare these values 
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// LFC INCLUDES
#include <lfcbase/Exception.h>

// CEGO INCLUDES
#include "CegoFieldValue.h"
#include "CegoBTreeValue.h"

// POSIX INCLUDES
#include <string.h>
#include <stdlib.h>

CegoBTreeValue::CegoBTreeValue()
{
    _idxArray = 0;
    _pI = 0;
    _len = 0;
}

CegoBTreeValue::CegoBTreeValue(char* p, unsigned len)
{
    _idxArray = 0;
    _pI = p;
    _len = len;
}

CegoBTreeValue::~CegoBTreeValue()
{
    // is local memory allocated ? ( just used for valueFromSchema )
    if ( _idxArray )
	free(_idxArray);
}

bool CegoBTreeValue::isNullTerminated(CegoDataType type)
{
    if ( type == VARCHAR_TYPE
	 || type == BIGINT_TYPE
	 || type == DECIMAL_TYPE )
	return true;
    return false;
}

unsigned CegoBTreeValue::getKeyLen(const ListT<CegoField>& schema)
{
    CegoField *pF = schema.First();
    unsigned keyLen = 0;
    while ( pF )
    {
	keyLen += 1; // null value indicator
	keyLen += getReservedLength(pF); 
	          
	// we have to add an additional character, in case of the full length
	// of the attribute size defintion is used by the value
	if ( isNullTerminated( pF->getType() ) )
	    keyLen += 1; // termination character
	 	
	pF = schema.Next();
    }
    return keyLen;
}

unsigned CegoBTreeValue::getKeyPos(const Chain& attrName, const ListT<CegoField>& schema)
{
    CegoField *pF = schema.First();
    unsigned keypos = 0;
    while ( pF )
    {
	keypos += 1; // null value indicator
	if ( pF->getAttrName() == attrName )
	    return keypos;
	
	keypos += getReservedLength(pF);
	
	if ( isNullTerminated(pF->getType() ) )
	    keypos++;

	pF = schema.Next();
    }
    throw Exception(EXLOC, "Attribute not found");
}

bool CegoBTreeValue::isNull(const ListT<CegoField>& schema) const
{
    CegoField* pF = schema.First();
    char* idxPtr = _pI;
    while (pF)
    {
	unsigned len = getReservedLength(pF);
	
	char nullIndicator;
	memcpy(&nullIndicator, idxPtr, 1);
	idxPtr += 1;

	if ( nullIndicator == 1 )
	{	    
	    return false;
	}
	else
	{
	    idxPtr += len + 1;
	    if ( isNullTerminated(pF->getType() ) )
	    {
		idxPtr += 1;
	    }
	}
	
	pF = schema.Next();
    }
    return true;
}

bool CegoBTreeValue::hasNull(const ListT<CegoField>& schema) const
{
    CegoField* pF = schema.First();
    char* idxPtr = _pI;
    while (pF)
    {
	char nullIndicator;
	memcpy(&nullIndicator, idxPtr, 1);
	idxPtr += 1;

	if ( nullIndicator == 1 )
	{	    
	    unsigned len = getReservedLength(pF);
	    
	    idxPtr+=len;

	    if ( isNullTerminated(pF->getType() ) )
		idxPtr+=1;
	}
	else
	{
	    return true;
	}

	pF = schema.Next();
    }
    return false;
}

void CegoBTreeValue::valueFromSchema(ListT<CegoField>* pSchema)
{
    // check if local memory already allocated, do if not
    if ( _idxArray == 0 )
	_idxArray = (char*)malloc(TABMNG_MAXINDEXVALUE);
    _pI = _idxArray;
    
    char* idxPtr = _pI;
    CegoField *pF = pSchema->First();

    unsigned vlen = 0;
    while (pF)
    {
	unsigned len = pF->getValue().getLength();

	// checking reserved length + 1 because value length includes zero byte  
	if ( len > getReservedLength(pF) + 1 )
	    throw Exception(EXLOC, "Reserved btree length for floating type exceeded");
	
	vlen += getReservedLength(pF);
	
	if ( vlen > TABMNG_MAXINDEXVALUE )
	    throw Exception(EXLOC, "Index value exceeded");

	if ( len > 0 )
	{
	    char nullIndicator = 1;
	    memcpy(idxPtr, &nullIndicator, 1);
	    idxPtr += 1;
	    
	    memcpy(idxPtr, pF->getValue().getValue(), len);

	    idxPtr += getReservedLength(pF);

	    // treat all string stored values
	    if ( isNullTerminated(pF->getType() ) )
		idxPtr+=1;
	    
	    // terminate in any case
	    *idxPtr=0;
	}
	else
	{
	    char nullIndicator = 0;
	    memcpy(idxPtr, &nullIndicator, 1);
	    idxPtr += getReservedLength(pF) + 1;

	    if ( isNullTerminated(pF->getType() ) )
		idxPtr+=1;	    
	}
	pF = pSchema->Next();
    }
    // cout << "Value from schema : " << toChain(pSchema) << endl;
}

void CegoBTreeValue::valueFromSchema(const ListT<CegoField> tableSchema, const ListT<CegoField> indexSchema)
{
    unsigned len = 0;
    CegoField* pIF = indexSchema.First();
    while (pIF)
    {
	len += getReservedLength(pIF);	    	
	pIF = indexSchema.Next();
    }

    if ( len > TABMNG_MAXINDEXVALUE )
	throw Exception(EXLOC, "Index value exceeded");

    // check if local memory already allocated, do if not
    if ( _idxArray == 0 )
	_idxArray = (char*)malloc(TABMNG_MAXINDEXVALUE);
    _pI = _idxArray;

    char* idxPtr = _pI;
    pIF = indexSchema.First();
    while (pIF)
    {
	CegoField *pTF = tableSchema.First();

	while ( pTF )
	{
	    if ( (Chain)pTF->getAttrName() == (Chain)pIF->getAttrName() )
	    {
		unsigned len = pTF->getValue().getLength();
		
		if ( len > 0 )
		{
		    // checking reserved length + 1 because value length includes zero byte  
		    if ( pTF->getValue().getLength() > getReservedLength(pTF) + 1 )  
			throw Exception(EXLOC, "Reserved btree length for floating type exceeded");
		    
		    char nullIndicator = 1;
		    memcpy(idxPtr, &nullIndicator, 1);
		    idxPtr += 1;

		    memcpy(idxPtr, pTF->getValue().getValue(), len);

		    idxPtr += getReservedLength(pTF);

		    // treat all string stored values
		    if ( isNullTerminated(pTF->getType() ) )
		    {
			idxPtr+=1;
		    }
		    // terminate in any case
		    *idxPtr=0;		    
		}
		else
		{
		    char nullIndicator = 0;
		    memcpy(idxPtr, &nullIndicator, 1);
		    idxPtr += getReservedLength(pTF) + 1;

		    if ( isNullTerminated(pTF->getType() ) )
		     	idxPtr+=1;
		}
	    }   
	    pTF = tableSchema.Next();
	}
	pIF = indexSchema.Next();
    }    
}

ListT<CegoFieldValue> CegoBTreeValue::valueToFVL(const ListT<CegoField>& schema) const
{
    ListT<CegoFieldValue> fvl;

    char* idxPtr = _pI;
    CegoField* pF = schema.First();
    while (pF)
    {
	char nullIndicator;
	memcpy(&nullIndicator, idxPtr, 1);
	idxPtr += 1;

	CegoFieldValue fv;
	unsigned len = getReservedLength(pF);
	
	if ( nullIndicator == 1 )
	{	    
	    // treat all string stored values
	    if ( isNullTerminated(pF->getType() ) )
	    {
		fv = CegoFieldValue(pF->getType(), Chain(idxPtr));		
		idxPtr += len + 1;
	    }
	    else
	    {
		fv = CegoFieldValue(pF->getType(), idxPtr, len);
		idxPtr+=len;
	    }	    
	}
	else
	{
	    idxPtr += len;
	    if ( isNullTerminated(pF->getType() ) )
	    {
		idxPtr += 1;
	    }
	}
	
	fvl.Insert(fv);
	pF = schema.Next();
    }
    return fvl;
}

void CegoBTreeValue::setPtr(char* p, unsigned len)
{
    _pI = p;
    _len = len;
}

char* CegoBTreeValue::getPtr() const
{
    return _pI;
}
 
CegoBTreeValue& CegoBTreeValue::operator = (const CegoBTreeValue& iv)
{
    if ( iv._idxArray )
    {
	if ( _idxArray == 0 )
	    _idxArray = (char*)malloc(TABMNG_MAXINDEXVALUE);	    
	memcpy(_idxArray, iv._idxArray, _len);
	_pI = _idxArray;
    }
    else
    {
	_pI = iv._pI;
    }
    _len = iv._len;
	    
    return (*this);
}

CegoBTreeValue::Comparison CegoBTreeValue::comp(const CegoBTreeValue& iv, ListT<CegoField>* pSchema) const
{
    char* idxPtr1 = _pI;
    char* idxPtr2 = iv.getPtr();

    CegoField* pF = pSchema->First();
    while (pF)
    {
	unsigned len = getReservedLength(pF);
	
	CegoFieldValue fv1;
	CegoFieldValue fv2;
	
	/* for performance reasons, we construct fieldvalue with pointer reference
           in case of VARCHAR & friend values len might be not correct, but for 
	   the used methods, it does't matter */

	char nullIndicator1;
	memcpy(&nullIndicator1, idxPtr1, 1);
	idxPtr1 += 1;
	
	char nullIndicator2;
	memcpy(&nullIndicator2, idxPtr2, 1);
	idxPtr2 += 1;

	if ( nullIndicator1 == 1 )
	{
	    if ( isNullTerminated(pF->getType() ) )
	    {
		fv1 = CegoFieldValue(pF->getType(), Chain(idxPtr1));
	    }
	    else
	    {
		fv1 = CegoFieldValue(pF->getType(), idxPtr1, len);
	    }
	}

	if ( nullIndicator2 == 1 )
	{
	    if ( isNullTerminated(pF->getType() ) )
	    {
		fv2 = CegoFieldValue(pF->getType(), Chain(idxPtr2));
	    }
	    else
	    {
		fv2 = CegoFieldValue(pF->getType(), idxPtr2, len);
	    }
	}

	CegoFieldValue::Comparison c = fv1.comp(fv2);
	
	if ( c == CegoFieldValue::MORE )
	{
	    return MORE;
	}
	else if ( c == CegoFieldValue::LESS )
	{
	    return LESS;
	}

	if ( isNullTerminated(pF->getType() ) )
	{
	    len++;
	}

	idxPtr1+=len;
	idxPtr2+=len;
	pF = pSchema->Next();
		
    }

    return EQUAL;
}

bool CegoBTreeValue::isHigher(const CegoBTreeValue& iv, ListT<CegoField>* pSchema) const
{   
    Comparison c = comp(iv, pSchema);
    return c == MORE;
}

bool CegoBTreeValue::isEqualHigher(const CegoBTreeValue& iv, ListT<CegoField>* pSchema) const
{   
    Comparison c = comp(iv, pSchema);
    return c == MORE || c == EQUAL;
}

bool CegoBTreeValue::isEqual(const CegoBTreeValue& iv, ListT<CegoField>* pSchema) const
{
    Comparison c = comp(iv, pSchema);
    return c == EQUAL;
}

Chain CegoBTreeValue::toChain(ListT<CegoField>* pSchema) const
{
    Chain s;

    char* idxPtr = _pI;
    CegoField* pF = pSchema->First();
    while (pF)
    {
	char nullIndicator;
	memcpy(&nullIndicator, idxPtr, 1);
	idxPtr += 1;

	CegoFieldValue fv;

	unsigned len = getReservedLength(pF);
	if ( nullIndicator == 1 )
	{
	    if ( isNullTerminated(pF->getType() ) )
	    {
		fv = CegoFieldValue(pF->getType(), Chain(idxPtr));
		idxPtr += len + 1;
	    }
	    else
	    {
		fv = CegoFieldValue(pF->getType(), idxPtr, len);
		idxPtr+=len;
	    }
	}
	else
	{	   
	    idxPtr+=len;
	    if ( isNullTerminated(pF->getType() ) )
	    {
		idxPtr+=1;
	    }
	}
	
	s += fv.valAsChain();
	pF = pSchema->Next();
	if ( pF )
	    s += Chain(",");
    }
    
    return s;
}

unsigned CegoBTreeValue::getReservedLength(CegoField* pF)
{
    switch ( pF->getType() )
    {
    case DECIMAL_TYPE:
	return pF->getLength();
    case BIGINT_TYPE:
    case VARCHAR_TYPE:
	return pF->getLength();
    case INT_TYPE:
    case LONG_TYPE:
    case BOOL_TYPE:
    case DATETIME_TYPE:	
    case FLOAT_TYPE:
    case DOUBLE_TYPE:
    case SMALLINT_TYPE:
    case TINYINT_TYPE:
	return pF->getLength();
    default:
    // case BLOB_TYPE:	
    // case CLOB_TYPE:
    // case NULL_TYPE:
    // case PAGEID_TYPE:
	throw Exception(EXLOC, Chain("Data type not supported"));
    }
}
