///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoProcBlock.cc  
// ----------------                                                     
// Cego proc block class implementation
//                                                         
// Design and Implementation by Bjoern Lemke
//               
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoProcBlock
//
// Description: Stored procedure block container class
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// CEGO INCLUDES
#include "CegoProcBlock.h"
#include "CegoSelect.h"
#include "CegoProcException.h"

#include "CegoDatabaseFormater.h"

CegoProcBlock::CegoProcBlock(CegoProcBlock *pParentBlock)
{
    _pParentBlock = pParentBlock;
    _pTriggerValueList = 0;
    _isEval=false;
}

CegoProcBlock::~CegoProcBlock()  
{    
    CegoProcStmt **pStmt = _stmtList.First();
    while (pStmt)
    {
	delete (*pStmt);
	pStmt = _stmtList.Next();
    }
    CegoProcCursor **pCur = _cursorList.First();
    while ( pCur ) 
    {
	delete (*pCur);
	pCur = _cursorList.Next();
    }
    CegoProcException **pExcep = _exceptionList.First();
    while ( pExcep ) 
    {
	delete *pExcep;
	pExcep = _exceptionList.Next();
    }
}

void CegoProcBlock::setVarList(const ListT<CegoProcVar>& varList)
{
    _varList = varList;
}

ListT<CegoProcVar>& CegoProcBlock::getVarList()
{
    return _varList;
}

void CegoProcBlock::addCursor(const Chain& cursorName, CegoSelect *pSelect)
{
    CegoProcCursor **pCur = _cursorList.First();
    while ( pCur )
    {
	if ( (*pCur)->getName() == cursorName )
	{
	    Chain msg = Chain("Cursor ") + cursorName + Chain(" already defined");
	    throw Exception(EXLOC, msg);	    
	}
	pCur = _cursorList.Next();
    }

    _cursorList.Insert(new CegoProcCursor(cursorName, pSelect));
    return;
}

CegoProcCursor* CegoProcBlock::getCursor(const Chain &cursorName)
{
    CegoProcCursor **pCur = _cursorList.First();
    while ( pCur )
    {
	if ( (*pCur)->getName() == cursorName )
	{
	    return *pCur;
	}
	pCur = _cursorList.Next();
    }

    if ( _pParentBlock )
    {
	return _pParentBlock->getCursor(cursorName);
    }
    else
    {
	Chain msg = Chain("Unknown cursor ") + cursorName;
	throw Exception(EXLOC, msg);
    }
}

void CegoProcBlock::setParentBlock(CegoProcBlock* pParentBlock)
{
    _pParentBlock = pParentBlock;
}

CegoProcBlock* CegoProcBlock::getParentBlock()
{
    return _pParentBlock;
}

void CegoProcBlock::setValue(const Chain& varName, const CegoFieldValue& fv)
{
    CegoProcVar *pVar = _varList.Find(CegoProcVar(varName));
    if ( pVar )
    {
	pVar->setValue(fv);
    }
    else
    {
	if ( _pParentBlock )
	{
	    _pParentBlock->setValue(varName, fv);
	}
	else
	{
	    Chain msg = Chain("Unknown variable ") + varName;
	    throw Exception(EXLOC, msg);
	}
    }    
}

CegoFieldValue CegoProcBlock::getValue(const Chain& varName)
{
    if ( varName ==  Chain(EXCEPINFO))
	return CegoFieldValue(VARCHAR_TYPE, _exceptionMsg);
    
    CegoProcVar *pVar = _varList.Find(CegoProcVar(varName));
    CegoFieldValue fv;
    if ( pVar )
    {
	return pVar->getValue();
    }
    else
    {
	if ( _pParentBlock )
	{
	    return _pParentBlock->getValue(varName);
	}
	else
	{
	    Chain msg = Chain("Unknown variable ") + varName;
	    throw Exception(EXLOC, msg);
	}
    }
}

void CegoProcBlock::addStatement(CegoProcStmt* pS) 
{
    _stmtList.Insert(pS);   
}

void CegoProcBlock::addException(CegoProcException* pE) 
{
    _exceptionList.Insert(pE);   
}

CegoException CegoProcBlock::execute(ListT<CegoField> *pFVL) 
{       
    CegoProcStmt **pStmt = _stmtList.First();

    while (pStmt)
    {
	try
	{
	    CegoException excep;

	    if ( ( excep = (*pStmt)->execute() ) != NONE_EXCEP ) 
	    {
		if ( excep == RETURN_EXCEP )
		{
		    closeBlock();
		    return excep;
		}
		CegoProcException **pException = _exceptionList.First();
		while ( pException ) 
		{
		    if ( (*pException)->getException() == excep || (*pException)->getException() == ANY_EXCEP )
		    {
			CegoProcBlock *pBlock = (*pException)->getBlock();
			pBlock->setExceptionMsg( getExceptionMsg() );
			if ( pBlock->execute() == RETURN_EXCEP ) 
			    return RETURN_EXCEP;
			excep = NONE_EXCEP;
		    }
		    pException = _exceptionList.Next();
		}
		
		// propagate exception to parent block		
		if ( excep != NONE_EXCEP && _pParentBlock )
		    _pParentBlock->setExceptionMsg( getExceptionMsg() );

		closeBlock();
		
		return excep;
	    }
	}
	catch ( Exception e )
	{
	    Chain errMsg;
	    e.pop(errMsg);
	    Chain plainStmt;
	    (*pStmt)->toChain(0).replaceAll(Chain("\n"), Chain(" "), plainStmt);
	    Chain msg = Chain("Procedure block error at stmt <") + plainStmt + Chain("> : ") + errMsg;

	    closeBlock();

	    throw Exception(EXLOC, msg);
			      
	}
	pStmt = _stmtList.Next();
    }

    closeBlock();

    return NONE_EXCEP;
}

void CegoProcBlock::cleanBlock()
{
    CegoProcCursor **pCur = _cursorList.First();
    while ( pCur ) 
    {
	(*pCur)->reset();
	pCur = _cursorList.Next();
    }
}

void CegoProcBlock::closeBlock()
{    
    CegoProcCursor **pCur = _cursorList.First();
    while ( pCur ) 
    {
	(*pCur)->close();
	pCur = _cursorList.Next();
    }
}

void CegoProcBlock::setRetVal(const CegoFieldValue& retVal)
{
    if ( _pParentBlock )
	_pParentBlock->setRetVal(retVal);
    else
	_retVal = retVal;
}

CegoFieldValue& CegoProcBlock::getRetVal()
{    
    return _retVal;
}

void CegoProcBlock::setExceptionMsg(const Chain& msg)
{
    _exceptionMsg = msg;
}

Chain CegoProcBlock::getExceptionMsg() const
{
    return _exceptionMsg;
}

ListT<CegoProcStmt*>& CegoProcBlock::getStmtList()
{
    return _stmtList;
}

const ListT<CegoProcException*>& CegoProcBlock::getExceptionList() const
{
    return _exceptionList;
}

Chain CegoProcBlock::toChain(unsigned defTabSetId, const Chain& indent) const
{
    Chain s;
    CegoProcVar *pVar = _varList.First();
    while ( pVar )
    {
	if ( pVar->getVarType() == CegoProcVar::BLOCKVAR )
	{
	    s += indent + Chain("var ");
	    s += pVar->getName();
	    switch ( pVar->getType() )
	    {
	    case INT_TYPE:
		s += Chain(" int;\n");
		break;
	    case LONG_TYPE:
		s += Chain(" long;\n");
		break;
	    case VARCHAR_TYPE:
		s += Chain(" string(") + Chain(pVar->getLength()) + Chain(");\n"); 
		break;
	    case BIGINT_TYPE:
		s += Chain(" bigint(") + Chain(pVar->getLength()) + Chain(");\n"); 
		break;
	    case DECIMAL_TYPE:
		s += Chain(" decimal(") + Chain(pVar->getLength()) + Chain(",") + Chain(pVar->getDim()) + Chain(");\n");
		break;
	    case BOOL_TYPE:
		s += Chain(" bool;\n"); 
		break;
	    case DATETIME_TYPE:
		s += Chain(" datetime;\n"); 
		break;
	    case FLOAT_TYPE:
		s += Chain(" float;\n"); 
		break;
	    case DOUBLE_TYPE:
		s += Chain(" double;\n"); 
		break;
	    case SMALLINT_TYPE:
		s += Chain(" smallint;\n"); 
		break;
	    case TINYINT_TYPE:
		s += Chain(" tinyint;\n"); 
		break;
	    case NULL_TYPE:
		s += Chain(" null;\n"); 
		break;
	    case BLOB_TYPE:
		s += Chain(" blob;\n"); 
		break;
	    case CLOB_TYPE:
		s += Chain(" clob;\n"); 
		break;
	    case PAGEID_TYPE:
		break;
	    }
	}

	pVar = _varList.Next();
    }
    
    CegoProcStmt **pStmt = _stmtList.First();
    while (pStmt)
    {
	s += (*pStmt)->toChain(defTabSetId, indent) + Chain(";\n");
	pStmt = _stmtList.Next();
    }

    CegoProcException **pException = _exceptionList.First();
    while ( pException ) 
    {
	s += Chain("exception when ") + (*pException)->toChain(defTabSetId) + Chain("\nthen\n");	
	s += (*pException)->getBlock()->toChain(defTabSetId, indent + Chain(DEFAULTINDENT));
	pException = _exceptionList.Next();
    }
    
    return s;
}

Chain CegoProcBlock::dbFormat(CegoDatabaseFormater *pForm) const
{
    return pForm->formatProcBlock(_varList, _stmtList, _exceptionList);
}

void CegoProcBlock::setTriggerValueList(ListT<CegoField>* pTriggerValueList)
{
    _pTriggerValueList = pTriggerValueList;    
}

ListT<CegoField>* CegoProcBlock::getTriggerValueList()
{
    return _pTriggerValueList;
}

void CegoProcBlock::cleanUp()
{
    CegoProcStmt **pStmt = _stmtList.First();
    while ( pStmt )
    {
	(*pStmt)->cleanUp();
	pStmt = _stmtList.Next();
    }
}

bool CegoProcBlock::isStatic()
{
    if ( _isEval )
	return _isStatic;
    
    CegoProcStmt **pStmt = _stmtList.First();
    while ( pStmt )
    {
	if ( (*pStmt)->isStatic() == false )
	{
	    _isEval = true;
	    _isStatic = false;
	    return _isStatic;
	}
	
	pStmt = _stmtList.Next();
    }
    
    _isEval = true;
    _isStatic = true;
    return _isStatic;
}
