///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoProcedure.cc  
// -----------------                                                     
// Cego procedure 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: CegoProcedure
// 
// Description:
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// cego includes
#include "CegoProcedure.h"
#include "CegoTypeConverter.h"
#include "CegoProcException.h"

CegoProcedure::CegoProcedure()
{
}

CegoProcedure::CegoProcedure(const Chain& procName, CegoProcBlock *pBlock)
{
    _procName = procName;
    _pBlock = pBlock;    
    _procType = PROCEDURE;
    _pMasterBlock = 0;
}
 
CegoProcedure::CegoProcedure(const Chain& procName, CegoProcBlock *pBlock, CegoDataType returnType, int returnTypeLen)
{
    _procName = procName;
    _pBlock = pBlock;
    _returnType = returnType;
    _returnTypeLen = returnTypeLen;
    _procType = FUNCTION;
    _pMasterBlock = 0;
}

CegoProcedure::~CegoProcedure()  
{
    if ( _pBlock )
	delete _pBlock;
}

void CegoProcedure::setMasterBlock(CegoProcBlock *pBlock)
{
    _pMasterBlock = pBlock;
}

const Chain& CegoProcedure::getName() const
{
    return _procName;
}

CegoProcBlock* CegoProcedure::getBlock()
{
    return _pBlock;
}

ListT<CegoProcVar>& CegoProcedure::getOutParamList()
{
    return _outParamList;
}

void CegoProcedure::getArgList(ListT<CegoProcVar>& argList)
{
    CegoProcVar *pArg = _pBlock->getVarList().First();
    
    while ( pArg )
    {
	if ( pArg->getVarType() == CegoProcVar::INVAR  || pArg->getVarType() == CegoProcVar::OUTVAR )
	    argList.Insert(*pArg);
	pArg = _pBlock->getVarList().Next();
    }    
}

void CegoProcedure::execute(const ListT<CegoExpr*>& expList)
{
    CegoExpr **pExpr = expList.First();
    CegoProcVar *pVar = _pBlock->getVarList().First();

    // we just treat the in out param values at the beginning of the param list in the block
    if ( pVar )
	if ( pVar->getVarType() == CegoProcVar::BLOCKVAR )
	    pVar = 0;
    
    int pos=1;
    while ( pExpr && pVar)
    {
	if ( pVar->getVarType() == CegoProcVar::INVAR )
	{
	    CegoFieldValue fv = (*pExpr)->evalFieldValue();

	    if ( pVar->getType() != fv.getType() )		
	    {
		if ( fv.castTo(pVar->getType()) == false )
		{
		    throw Exception(EXLOC, Chain("Mismatched datatype <") 
				    + CEGO_TYPE_MAP[(int)fv.getType()] 
				    + Chain("> in value list for argument ") + Chain(pos) 
				    + " ( expected " 
				    + CEGO_TYPE_MAP[(int)pVar->getType()] + " )");
		}
	    }
	    else if ( fv.getType() == VARCHAR_TYPE )
	    {
		if ( pVar->getLength() < fv.getLength() - 1 )
		    throw Exception(EXLOC, Chain("Parameter length for ") + pVar->getName() + Chain(" exceeded ( max len is ") + Chain(pVar->getLength()) + Chain(")"));
	    }

	    pVar->setValue(fv);
	}
	pos++;
	pExpr = expList.Next();
	pVar = _pBlock->getVarList().Next();
	
	// we just treat the in out param values at the beginning of the param list in the block
	if ( pVar )
	    if ( pVar->getVarType() == CegoProcVar::BLOCKVAR )
		pVar = 0;
    }

    if ( pVar || pExpr )
    {
	Chain msg = Chain("Mismatched parameter count for procedure ");
	throw Exception(EXLOC, msg);
    }

    CegoException excep = _pBlock->execute();

    if ( excep != NONE_EXCEP && excep != RETURN_EXCEP )
    {
	Chain msg = Chain("Uncatched procedure exception : ") + _pBlock->getExceptionMsg();
	throw Exception(EXLOC, msg);
    }

    pExpr = expList.First();
    pVar = _pBlock->getVarList().First(); 
    _outParamList.Empty();

    while ( pExpr && pVar)
    {
	if ( pVar->getVarType() == CegoProcVar::OUTVAR )
	{
	    // CegoFieldValue fv = (*pExpr)->evalExpr();
	    if ( _pMasterBlock )
	    {
		Chain var;
		(*pExpr)->checkVar(var);
		_pMasterBlock->setValue(var, pVar->getValue());
	    }
	    Chain var;
	    (*pExpr)->checkVar(var);
	    
	    CegoFieldValue fv = pVar->getValue();
	    _outParamList.Insert(CegoProcVar(var, CegoProcVar::OUTVAR, fv.getType(), fv.getLength(), fv));
	}
	pExpr = expList.Next();
	pVar = _pBlock->getVarList().Next();
		
    }    
}

CegoFieldValue CegoProcedure::getRetVal() const
{
    return _pBlock->getRetVal();
}

CegoProcedure::ProcType CegoProcedure::getProcType() const
{
    return _procType;
}

CegoProcedure& CegoProcedure::operator = ( const CegoProcedure& p)
{
    _procName = p._procName;
    _pBlock = p._pBlock;
    _pMasterBlock = p._pMasterBlock;
    _procType = p._procType;
    _returnType = p._returnType;
    _returnTypeLen = p._returnTypeLen;
    return (*this);
}

bool CegoProcedure::operator == ( const CegoProcedure& p) const
{

    if ( _procName == p._procName )
	return true;
    return false;
}

Chain CegoProcedure::toChain() const
{

    Chain s;
    s = Chain("procedure ") + _procName + Chain("(");
    CegoProcVar *pVar = _pBlock->getVarList().First();
    while ( pVar )
    {
	if ( pVar->getVarType() == CegoProcVar::BLOCKVAR )
	    pVar = 0;
	else
	{
	    s += pVar->toChain();
	    pVar = _pBlock->getVarList().Next();
	    	    
	    if ( pVar )
	    {
		if ( pVar->getVarType() != CegoProcVar::BLOCKVAR )
		s += Chain(",\n     ");
	    }
	}
    } 

    s += Chain(")");
    if ( _procType == FUNCTION )
    {
	s += Chain(" return");
	switch ( _returnType )
	{
	case INT_TYPE:
	    s += Chain(" int");
	    break;
	case LONG_TYPE:
	    s += Chain(" long");
	    break;
	case VARCHAR_TYPE:
	    s += Chain(" string(");
	    s += Chain(_returnTypeLen);
	    s += Chain(")");
	    break;
	case BOOL_TYPE:
	    s += Chain(" bool");
	    break;
	case DATETIME_TYPE:
	    s += Chain(" datetime");
	    break;
	case BIGINT_TYPE:
	    s += Chain(" bigint(");
	    s += Chain(_returnTypeLen);
	    s += Chain(")");
	    break;
	case FLOAT_TYPE:
	    s += Chain(" float");
	    break;
	case DOUBLE_TYPE:
	    s += Chain(" double");
	    break;
	case DECIMAL_TYPE:
	    s += Chain(" decimal(");
	    s += Chain(_returnTypeLen);
	    s += Chain(")");
	    break;
	case FIXED_TYPE:
	    s += Chain(" fixed(");
	    s += Chain(_returnTypeLen);
	    s += Chain(")");
	    break;
	case SMALLINT_TYPE:
	    s += Chain(" smallint");
	    break;
	case TINYINT_TYPE:
	    s += Chain(" tinyint");
	    break;	  
	case NULL_TYPE:
	    s += Chain(" null");
	    break;
	case BLOB_TYPE:
	    throw Exception(EXLOC, Chain("Blob not supported as return type"));
	}
    }

    s += Chain("\n");
    s += Chain("begin\n");

    Chain indent = Chain("   ");

    s += _pBlock->toChain(indent);
    
    CegoProcException **pException = _pBlock->getExceptionList().First();
    while ( pException ) 
    {
	s += Chain("exception when ") + (*pException)->toChain() + Chain("\nthen\n");	
	s += (*pException)->getBlock()->toChain(indent + indent);
	pException = _pBlock->getExceptionList().Next();
    }

    s += Chain("end");
    return s;
}
