///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoSerial.cc
// ------------
// Cego serialize class implementation
//
// Design and Implementation by Bjoern Lemke
//               
// (C)opyright 2000-2012 Bjoern Lemke
//
// 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, 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; see the file COPYING.  If not, write to
// the Free Software Foundation, 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
// IMPLEMENTATION MODULE
//
// Class: CegoSerial
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

#include <lfc/Exception.h>

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

#define SERSEP "@"
#define SERNULL "-"
#define QSIGN '\''
#define ESIGN '^'
#define CESIGN "!^&"

CegoSerial::CegoSerial(NetHandler *pN)
{
    _pN = pN;
    _pTok = new Tokenizer(pN->getMsg(), 0, Chain(SERSEP), QSIGN, ESIGN);
}

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

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

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

/*
void CegoSerial::setSerial(const Chain& serial)
{
    _serial = serial;

    if ( _pTok )
	delete _pTok;
    _pTok = new Tokenizer(_serial, Chain(SERSEP), QSIGN, ESIGN);
    
}

const Chain& CegoSerial::getSerial() const
{
    return _serial;
}
*/

int CegoSerial::numAhead() const
{
    if ( _pTok )
    {
	return _pTok->numAhead();
    }
    return 0;
}


void CegoSerial::writeChain(const Chain& s)
{

    if ( _pN->concatPos() != 0 )
	_pN->concatAdd(Chain(SERSEP));

    if ( s.length() < 2 )
	_pN->concatAdd(Chain(SERNULL)); 
    else
    {
	
	// treat escape charater
	int pos;
	Chain ec;
	bool isMod=false;
	if ( s.posStr(Chain(ESIGN), pos) )
	{
	    s.replaceAll(Chain(ESIGN), Chain(CESIGN), ec);
	    isMod = true;
	    if ( ec.posStr(Chain(SERSEP), pos) )
	    {
		Chain eec;
		ec.replaceAll(Chain(SERSEP), Chain(ESIGN) + Chain(SERSEP), eec);
		ec = eec;
	    }

	}
	else if ( s.posStr(Chain(SERSEP), pos) )
	{
	    isMod = true;
	    s.replaceAll(Chain(SERSEP), Chain(ESIGN) + Chain(SERSEP), ec);
	}

	if ( isMod )
	    _pN->concatAdd(ec); 
	else
	    _pN->concatAdd(s);
    }
}

void CegoSerial::writeRow(const ListT<CegoField>& fl)
{

    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)
{

    writeChain(Chain(fvl.Size()));
    CegoFieldValue *pFV = fvl.First();
    while ( pFV )
    {       
	if ( ! pFV->isNull() )
	{
	    writeChain(pFV->valAsChain());   
	}
	else
	{
	    writeChain(Chain());   
	}
	
	pFV = fvl.Next();
    }
}

Chain CegoSerial::readChain()
{
    if ( _pTok )
    {
	Chain n;
	if ( _pTok->nextToken(n) )
	{
	    if ( n == Chain(SERNULL))
		return Chain();

	    int pos;
	    if ( n.posStr(Chain(ESIGN) + Chain(SERSEP), pos) )
	    {
		Chain es;
		n.replaceAll(Chain(ESIGN) + Chain(SERSEP), Chain(SERSEP), es);
		n = es;		
	    }
	    if ( n.posStr(Chain(CESIGN), pos) )
	    {
		Chain es;
		n.replaceAll(Chain(CESIGN), Chain(ESIGN), es);
		n = es;		
	    }
	   
	    return n;
	}
	throw Exception(EXLOC, "No token available");
    }
    else
    {
	throw Exception(EXLOC, "No token available");
    }    
}

void CegoSerial::writeSchema(const ListT<CegoField>& schema)
{

    CegoTypeConverter tc;

    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(tc.getTypeString(pF->getType()));
	writeChain(Chain(pF->getLength()));
	
	pF = schema.Next();

    }
}

void CegoSerial::writeObject(const CegoDecodableObject& oe)
{
    writeChain("XXX DECODABLE OBJECT ");
}

ListT<CegoField> CegoSerial::readSchema()
{

    CegoTypeConverter tc;

    int schemaSize = readChain().asInteger();

    ListT<CegoField> fl;

    for ( int i=0; i<schemaSize; i++ )
    {
	Chain colTable = readChain();
	Chain colName = readChain();
	Chain isNull = readChain();
	Chain colDefValue = readChain();
	Chain colDataType = readChain();
	Chain colSize = readChain();
	
	CegoDataType dt = tc.getTypeId(colDataType);
	
	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.asInteger(), defValue, isNullable);	
	
	fl.Insert(f);
	
    }
    
    return fl;
}


ListT<CegoFieldValue> CegoSerial::readRow(const ListT<CegoField>& schema)
{

    ListT<CegoFieldValue> fvl;

    int cols = readChain().asInteger();

    int i=0;
    CegoField *pF = schema.First();
    while ( pF && i<cols )
    {      	
	CegoFieldValue fv(pF->getType(), readChain());
	fvl.Insert(fv);
	
	i++;
	pF = schema.Next();
    }
    
    return fvl;

}


/*
CegoSerial& CegoSerial::operator = ( const CegoSerial& ser)
{
    _pN = ser._pN;
    _p
    return (*this);
}
*/


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

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