///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoSerial.cc
// -------------
// Cego serialize class implementation
//
// Design and Implementation by Bjoern Lemke
//               
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoSerial
//
// Description: This class implements the serialization of any query request and response data
//              Since the xml protocol seems to throttle down query performance, this class
//              was introduced
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

#include <lfcbase/Exception.h>

// cego includes
#include "CegoSerial.h"
#include "CegoTypeConverter.h"
#include "CegoField.h"
#include "CegoDataType.h"

#include <string.h>

#define SERSEP "@"
#define SERNULL "-"

CegoSerial::CegoSerial(NetHandler *pN, bool isFast)
{    
    _pN = pN;
    if ( isFast )
    {
	_isFast = true;
	_pTok = 0;
	_pFastCursor = 0;
	if ( _pN->getMsgSize() > 0 )
	    _pFastCursor = _pN->getMsg();
    }
    else
    {
	_isFast = false;
	_pFastCursor = 0;
	_pTok = new Tokenizer(pN->getMsg(), 0, Chain(SERSEP));
    }
}

CegoSerial::~CegoSerial()
{
    if ( _pTok )
	delete _pTok;
}

void CegoSerial::reset()
{
    if ( _pN )
	_pN->concatReset();
    if ( _pTok )
    {
	_pTok->reset(_pN->getMsg(), _pN->getMsgSize());
    }

    if ( _isFast )
    {
	if ( _pN->getMsgSize() > 0 )
	{
	    _pFastCursor = _pN->getMsg();
	}
    }    
}

bool CegoSerial::isReset() const
{
    if ( _pN->concatPos() == 0 )
	return true;
    return false;
}

unsigned CegoSerial::numWritten() const
{
    return _pN->concatPos();
}

unsigned CegoSerial::numAhead() const
{
    if ( _pTok )
    {
	return _pTok->numAhead();
    }
    else
    {
	unsigned na = 0;
	if ( _pN->getMsgSize() > 0 && _pFastCursor != 0 )
	    na = _pN->getMsgSize() - (unsigned)( (long long)_pFastCursor - (long long)_pN->getMsg() );
	return na;
    }
}

void CegoSerial::writeChain(const Chain& s)
{
    if ( _isFast )
    {	
	unsigned l = 0;
	if ( s.length() > 0 )
	    l = s.length() - 1;
	_pN->concatAdd((char*)&l, sizeof(unsigned));
	if ( l > 0 )
	    _pN->concatAdd(s);
    }
    else
    {
	if ( _pN->concatPos() != 0 )
	    _pN->concatAdd(Chain(SERSEP));
	
	if ( s.length() < 2 )
	    _pN->concatAdd(Chain(SERNULL)); 
	else
	{
	    Chain cl(s.length() - 1);
	    _pN->concatAdd(cl);
	    
	    _pN->concatAdd(Chain(SERSEP));
	    _pN->concatAdd(s);
	}
    }
}

void CegoSerial::writeRow(const ListT<CegoField>& fl)
{
    if ( _isFast )
    {
	unsigned l = fl.Size();
	_pN->concatAdd((char*)&l, sizeof(unsigned));
	
	CegoField *pF = fl.First();
	while ( pF )
	{       
	    if ( ! pF->getValue().isNull() )
	    {
		unsigned l = pF->getValue().getLength();

		_pN->concatAdd((char*)&l, sizeof(unsigned));
		if ( l > 0 )
		    _pN->concatAdd((char*)pF->getValue().getValue(), l);   
	    }
	    else
	    {
		unsigned l = 0;
		_pN->concatAdd((char*)&l, sizeof(unsigned));
	    }
	    
	    pF = fl.Next();
	}
    }
    else
    {
	writeChain(Chain(fl.Size()));
	CegoField *pF = fl.First();
	while ( pF )
	{       
	    if ( ! pF->getValue().isNull() )
	    {
		writeChain(pF->getValue().valAsChain());   
	    }
	    else
	    {
		writeChain(Chain());   
	    }
	    
	    pF = fl.Next();
	}
    }
}

void CegoSerial::writeRow(const ListT<CegoFieldValue>& fvl)
{	
    if ( _isFast )
    {
	unsigned l = fvl.Size();
	
	_pN->concatAdd((char*)&l, sizeof(unsigned));

	CegoFieldValue *pFV = fvl.First();
	while ( pFV )
	{       
	    if ( ! pFV->isNull() )
	    {
		unsigned l = pFV->getLength();
		_pN->concatAdd((char*)&l, sizeof(unsigned));		
		_pN->concatAdd((char*)pFV->getValue(), l);   
	    }
	    else
	    {
		unsigned l = 0;
		_pN->concatAdd((char*)&l, sizeof(unsigned));
	    }
	    
	    pFV = fvl.Next();
	}
    }
    else
    {
	writeChain(Chain(fvl.Size()));
	CegoFieldValue *pFV = fvl.First();
	while ( pFV )
	{       
	    if ( ! pFV->isNull() )
	    {
		writeChain(pFV->valAsChain());   
	    }
	    else
	    {
		writeChain(Chain());   
	    }
	    
	    pFV = fvl.Next();
	}
    }
}

void CegoSerial::writeSchema(const ListT<CegoField>& schema)
{
    if ( _isFast )
    {
	unsigned l = schema.Size();
	_pN->concatAdd((char*)&l, sizeof(unsigned));
    }
    else
    {
	writeChain(Chain(schema.Size()));
    }
    
    CegoField *pF = schema.First();
    while ( pF ) 
    {	
	Chain tname;
	if (pF->getTableAlias().length() > 0)
	{
	    tname = pF->getTableAlias();
	}
	else
	{
	    tname = pF->getTableName();
	}
	
	writeChain(tname);
	
	writeChain(pF->getAttrName());
	
	if ( pF->isNullable() )
	    writeChain(Chain("y"));
	else
	    writeChain(Chain("n"));
	
	if ( pF->getValue().getValue() )
	    writeChain(pF->getValue().valAsChain());
	else
	    writeChain(Chain());

	writeChain(Chain(pF->getType()));
	// writeChain(CegoTypeConverter::getTypeString(pF->getType()));
	writeChain(Chain(pF->getLength()));
	
	pF = schema.Next();	
    }
}
    
void CegoSerial::writeObject(const CegoDecodableObject& oe)
{
    writeChain("TODO : WRITE DECODABLE OBJECT ");
}

Chain CegoSerial::readChain(unsigned maxLen)
{
    if ( _isFast )
    {
	// we return empty string, if cursor was not setup
	if ( _pFastCursor == 0 )
	    return Chain();
	
	unsigned l;
	
	memcpy(&l, _pFastCursor, sizeof(unsigned));     

	// cout << "Read len " << l << endl;
	_pFastCursor += sizeof(unsigned);
	
	if ( l > 0 )
	{
	    if ( l > numAhead() || l > maxLen )
	    {
		throw Exception(EXLOC, Chain("Corrupted or invalid fastserial string value ( len = ") + Chain(l) + Chain(" )"));
	    }
	    Chain n(_pFastCursor, l);
	    _pFastCursor += l;
	    return n;
	}
	else
	{
	    return Chain();
	}
    }
    else
    {
	if ( _pTok )
	{
	    Chain n;
	    if ( _pTok->nextToken(n) )
	    {
		if ( n == Chain(SERNULL))
		    return Chain();
		
		unsigned l = n.asUnsigned();

		_pTok->skip(1); // skip separator
		_pTok->rawRead(n, l);
		
		return n;
	    }
	    throw Exception(EXLOC, Chain("No token available, net message is <") + _pN->getMsg() + Chain(">"));
	}
	else
	{
	    throw Exception(EXLOC, Chain("No token available, net message is <") + _pN->getMsg() + Chain(">"));
	}
    }    
}

void CegoSerial::readRow(const ListT<CegoField>& schema, ListT<CegoFieldValue>& fvl, unsigned maxLen)
{    
    fvl.Empty();

    if ( _isFast )
    {
	if ( _pFastCursor == 0 )	    
	    throw Exception(EXLOC, Chain("Cursor for fast serial message not setup "));
	
	unsigned cols;
	memcpy(&cols, _pFastCursor, sizeof(unsigned));     
	_pFastCursor += sizeof(unsigned);
	unsigned i=0;
	CegoField *pF = schema.First();
	while ( pF && i<cols )
	{      	
	    unsigned len;
	    memcpy(&len, _pFastCursor, sizeof(unsigned));     
	    _pFastCursor += sizeof(unsigned);

	    if ( len > 0 )
	    {
		if ( len > numAhead() || len > maxLen )
		{
		    throw Exception(EXLOC, Chain("Corrupted or invalid fastserial string value ( len = ") + Chain(len) + Chain(" )"));
		}
		CegoFieldValue fv(pF->getType(), _pFastCursor, len);
		_pFastCursor += len;
		fvl.Insert(fv);
	    }
	    else
	    {
		CegoFieldValue fv;
		fvl.Insert(fv);
	    }
	    i++;
	    pF = schema.Next();
	}
    }
    else
    {
	unsigned cols = readChain().asUnsigned();
	
	unsigned i=0;
	CegoField *pF = schema.First();
	while ( pF && i<cols )
	{      	
	    CegoFieldValue fv(pF->getType(), readChain());
	    fvl.Insert(fv);
	    
	    i++;
	    pF = schema.Next();
	}
    }
    return;
}

ListT<CegoField> CegoSerial::readSchema()
{
    unsigned schemaSize;

    if ( _isFast )
    {
	if ( _pFastCursor == 0 )	    
	    throw Exception(EXLOC, Chain("Cursor for fast serial message not setup "));

	memcpy(&schemaSize, _pFastCursor, sizeof(unsigned));     
	_pFastCursor += sizeof(unsigned);
    }
    else
    {
	schemaSize = readChain().asUnsigned();
    }
    
    ListT<CegoField> fl;

    for ( unsigned i=0; i<schemaSize; i++ )
    {
	Chain colTable = readChain();
	Chain colName = readChain();
	Chain isNull = readChain();
	Chain colDefValue = readChain();
	CegoDataType dt = (CegoDataType)readChain().asUnsigned();
	Chain colSize = readChain();
		
	bool isNullable;
	if ( isNull == Chain("y"))
	    isNullable = true;
	else
	    isNullable = false;

	CegoFieldValue defValue;
	if ( colDefValue.length() > 1 )
	{
	    defValue = CegoFieldValue(dt, colDefValue);
	}

	CegoField f(colTable, colTable, colName, dt, colSize.asUnsigned(), 0, defValue, isNullable);	
	
	fl.Insert(f);	
    }
    
    return fl;
}

Chain CegoSerial::toChain() const
{
    Chain s;
    return s;    
}

ostream& operator << (ostream& s, const CegoSerial& t)
{
    return s;
}
