///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoProcBlock.cc  
// ----------------                                                     
// Cego proc block class implementation
//                                                         
// Design and Implementation by Bjoern Lemke
//               
// (C)opyright 2000-2010 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: CegoProcBlock
// 
// Description:
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// cego includes
#include "CegoProcBlock.h"
#include "CegoSelect.h"
#include "CegoProcException.h"

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

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() 
{
        
    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 msg = Chain("Procedure error at stmt \n----------\n") + (*pStmt)->toChain() + Chain("\n----------\nwith error\n##########\n") +  e.getBaseMsg() + Chain("\n##########");

	    Chain plainStmt;
	    (*pStmt)->toChain().replaceAll(Chain("\n"), Chain(" "), plainStmt);
	    Chain msg = Chain("Procedure error at stmt <") + plainStmt + Chain(">");
	    closeBlock();

	    throw Exception(EXLOC, msg, e);
			      
	}
	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() const
{
    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(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(");\n"); 
		break;
	    case FIXED_TYPE:
		s += Chain(" fixed(") + Chain(pVar->getLength()) + 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;
	    }
	}

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

    return s;
}
