///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoImpInStream.cc 
// ------------------
// Cego import instream implementation
//     
// Design and Implementation by Bjoern Lemke               
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoImpInStream
// 
// Description: Database import via XML or binary
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

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

// CEGO INCLUDES
#include "CegoImpInStream.h"
#include "CegoXMLdef.h"
#include "CegoField.h"
#include "CegoObject.h"
#include "CegoQueryManager.h"
#include "CegoTypeConverter.h"

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

CegoImpInStream::CegoImpInStream(const Chain& tableSet, CegoQueryManager* pGTM, CegoAdminHandler* pAH)
{
    _pGTM = pGTM;
    _tableSet = tableSet;
    _pDBMng = _pGTM->getDBMng();
    _pAH = pAH;
    _mode = IMP_ALL;
    _modId = _pDBMng->getModId("CegoImpInStream");
}

CegoImpInStream::CegoImpInStream(const Chain& tableSet, const Chain& tableName, CegoQueryManager* pGTM, CegoAdminHandler* pAH)
{
    _pGTM = pGTM;
    _tableSet = tableSet;
    _impTable = tableName;
    _pDBMng = _pGTM->getDBMng();
    _pAH = pAH;
    _mode = IMP_TABLE;
    _modId = _pDBMng->getModId("CegoImpInStream");
}

CegoImpInStream::~CegoImpInStream()
{
    if  ( _bp.isFixed() )
	_pDBMng->bufferUnfix(_bp, true, _pGTM->getLockHandler());  
}

unsigned long long CegoImpInStream::numImported() const
{
    return _rowCount;
}

void CegoImpInStream::putFirst(Element* pParent, const Chain& name, const ListT<Attribute>& attrList, ListT<char*>& dataList)
{
    _isFirst = true;
    _rowCount = 0;

    if  ( _bp.isFixed() )
    {
	_pDBMng->bufferUnfix(_bp, true, _pGTM->getLockHandler());  
    }

    Chain tabName = pParent->getAttributeValue(XML_NAME_ATTR);
    
    if ( _mode == IMP_ALL ||  ( _mode == IMP_TABLE && tabName == _impTable) )
    {
	_rowCount++;

	if ( _pAH )
	{
	    Chain info = Chain("Importing table data for ") +  tabName + Chain(" ...");
	    _pAH->sendInfo(info + Chain("\n"));
	}
	
	ListT<Element*> schemaList = pParent->getChildren(XML_SCHEMA_ELEMENT);
	
	Element **pSchemaElement = schemaList.First();
	
	_schema.Empty();
	
	if ( pSchemaElement )
	{
	    ListT<Element*> colList = (*pSchemaElement)->getChildren(XML_COL_ELEMENT);
	    
	    Element **pCol = colList.First();
	    while ( pCol ) 
	    {		
		Chain colName = (*pCol)->getAttributeValue(XML_COLNAME_ATTR);
		Chain colType = (*pCol)->getAttributeValue(XML_COLTYPE_ATTR);

		CegoDataType dt = CegoTypeConverter::getTypeId( colType );

		unsigned colSize;
		
		if (dt == VARCHAR_TYPE
		    || dt == BIGINT_TYPE
		    || dt == DECIMAL_TYPE )
		{
		    colSize = (*pCol)->getAttributeValue(XML_COLSIZE_ATTR).asUnsigned();
		}
		else
		{
		    colSize = CegoTypeConverter::getTypeLen( dt );
		}
		
		unsigned colDim;
		
		if ( dt == DECIMAL_TYPE )
		{
		    Chain dimCheck = (*pCol)->getAttributeValue(XML_COLDIM_ATTR);
		    if ( dimCheck == Chain() )
		    {
			colDim = colSize;
			colSize = RESERVED_BTREE_FLOATLEN;
		    }
		    else
		    {
			colDim = dimCheck.asUnsigned();
		    }
		}
		else
		{
		    colDim = 0;
		}
		
		Chain colNullable = (*pCol)->getAttributeValue(XML_COLNULLABLE_ATTR);
		bool isNullable;
		if ( colNullable == Chain(XML_TRUE_VALUE) ) 
		    isNullable = true;
		else
		    isNullable = false;
		
		CegoDataType type = CegoTypeConverter::getTypeId( colType );
		
		CegoFieldValue defValue;
		Chain colDefValue = (*pCol)->getAttributeValue(XML_COLDEFVALUE_ATTR);
		
		if ( colDefValue != Chain())
		    defValue = CegoFieldValue(type, colDefValue);
		
		CegoField f(CegoField(tabName, tabName, colName, dt, colSize, colDim, defValue, isNullable));		
		_schema.Insert(f);
		
		pCol = colList.Next();
	    }

	    ListT<CegoField> idxList;
	    _pGTM->createQueryTable(_tableSet, tabName, _schema, idxList);		
	    _pGTM->setAppend(true);
	}
	insertData(tabName, attrList, dataList);
    }
}

void CegoImpInStream::putNext(Element* pParent, const Chain& name, const ListT<Attribute>& attrList, ListT<char*>& dataList)
{    
    Chain tabName = pParent->getAttributeValue(XML_NAME_ATTR);
    if ( _mode == IMP_ALL || ( _mode == IMP_TABLE && tabName == _impTable) )
    {
	_rowCount++;
	
	if ( _pAH && ( _rowCount % XP_ROWINTERVAL == 0 ) )
	    _pAH->sendInfo(Chain(_rowCount) + Chain(" rows\r"));
	
	insertData(tabName, attrList, dataList);
    }
}

void CegoImpInStream::insertData(const Chain& tabName, const ListT<Attribute>& attrList, ListT<char*> dataList)
{	 
    if ( attrList.Size() == 0 )
	return;
    
    ListT<CegoField> fvl;

    CegoField *pF = _schema.First();
    while ( pF ) 
    {
	Chain colName = pF->getAttrName();
	
	Attribute *pAttr = attrList.Find( Attribute( colName ) );

	if ( pAttr )
	{
	    Chain colVal = pAttr->getValue();
	    
	    if ( pF->getType() == BLOB_TYPE )
	    {
		unsigned blobPos = colVal.cutTrailing(XML_BLOBPREFIX).asUnsigned();
		
		unsigned tabSetId = _pDBMng->getTabSetId(_tableSet);
				
		unsigned long long decBufSize = strlen(dataList[blobPos]);
		
		Base64Coder b64;
		unsigned char *decBuf = (unsigned char*)malloc(decBufSize);
		if ( decBuf == NULL )
		{
		    throw Exception(EXLOC, "malloc system error");     
		}

		unsigned len = CegoTypeConverter::getTypeLen(pF->getType(), colVal);
		CegoField f(CegoField(pF->getTableName(), pF->getTableAlias(), colName, pF->getType(), len));
		
		if ( blobPos < dataList.Size() )
		{
		    unsigned long long blobSize = b64.decode(dataList[blobPos], decBuf, decBufSize);

		    PageIdType pageId;
		    _pGTM->putBlobData(tabSetId, decBuf, blobSize, pageId);
		    
		    free (decBuf );
		
		    Chain blobRef = Chain("[") + Chain(pageId) + Chain("]");
		    CegoFieldValue fv(pF->getType(), blobRef);
		    f.setValue(fv);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Missing blob information in stream for table ") + tabName);
		    // set to zero
		    CegoFieldValue fv;
		    f.setValue(fv);		    
		}
		fvl.Insert(f);
	    }
	    else if ( pF->getType() == CLOB_TYPE )
	    {
		unsigned clobPos = colVal.cutTrailing(XML_CLOBPREFIX).asUnsigned();
		
		unsigned tabSetId = _pDBMng->getTabSetId(_tableSet);
		
		PageIdType pageId;

		unsigned len = CegoTypeConverter::getTypeLen(pF->getType(), colVal);
		CegoField f(CegoField(pF->getTableName(), pF->getTableAlias(), colName, pF->getType(), len));
		
		if ( clobPos < dataList.Size() )
		{
		    _pGTM->putClobData(tabSetId, dataList[clobPos], strlen(dataList[clobPos]), pageId);
		    Chain clobRef = Chain("[") + Chain(pageId) + Chain("]");
		    CegoFieldValue fv(pF->getType(), clobRef);
		    f.setValue(fv);
		}
		else
		{
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Missing clob information in stream for table ") + tabName);
		    // set to zero
		    CegoFieldValue fv;
		    f.setValue(fv);
		}
		fvl.Insert(f);
	    }
	    else
	    {
		CegoField f(pF->getTableName(), pF->getTableAlias(), colName, pF->getType(), pF->getLength(), pF->getDim());
		CegoFieldValue fv(pF->getType(), colVal);
		f.setValue(fv);
		fvl.Insert(f);
	    }
	}
	else
	{
	    // null value
	    CegoField f(CegoField(pF->getTableName(), colName));		    
	    fvl.Insert(f);
	}	
	pF = _schema.Next();
    }
        
    if ( _isFirst )
    {
	_idxList.Empty();
	_keyList.Empty();
	_checkList.Empty();

	unsigned tabSetId = _pDBMng->getTabSetId(_tableSet);	
	_pGTM->getObjectWithFix(tabSetId, tabName, CegoObject::TABLE, _oe, _bp);
	_sysEntry = CegoDataPointer(_bp.getPageId(), _bp.getEntryPos());

	unsigned numInvalid;
	_pGTM->getObjectListByTable(_oe.getTabSetId(), _oe.getName(), _idxList, _btreeList, _keyList, _checkList, _triggerList, _aliasList, numInvalid);
	_isFirst = false;
    }

    CegoField* pCF = _schema.First();
    CegoField* pCV =  fvl.First();
    
    while ( pCF && pCV)
    {	    
	CegoFieldValue fv = pCV->getValue();

	CegoQueryHelper::prepareFieldValue(pCF, fv, _pGTM, _oe.getTabSetId());
	pCV->setValue(fv); 
	
	pCF = _schema.Next();
	pCV = fvl.Next();	
    }
    if ( pCF || pCV )
    {
	throw Exception(EXLOC, "Mismatched argument count for value list");
    }

    CegoDataPointer dp;
    Chain virginIndex;
    
    _pGTM->insertDataTable(_oe, fvl, _idxList, _btreeList, _keyList, _checkList, _sysEntry, virginIndex, dp, false, true, false, false);
}
