///////////////////////////////////////////////////////////////////////////////
//                                   
// CegoOutput.cc
// -------------
// Cego formatting data for output
//      
// 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: CegoOutput
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// base includes
#include <lfc/Datetime.h>
#include <lfc/Tokenizer.h>

// cego includes
#include "CegoOutput.h"
#include "CegoDefs.h"

CegoOutput::CegoOutput()
{
    _pDbHandle = 0;
    _rawMode = false;
}


CegoOutput::CegoOutput(const ListT<CegoField>& schema, const Chain& format)
{
    _schema = schema;
    _pDbHandle = 0;
    _rowsPerMsg = 0;
    _rawMode = false;
    _format = format;
}

CegoOutput::~CegoOutput()
{
}

void CegoOutput::setDbHandle(CegoDbHandler* pDbHandle, int rowsPerMsg)
{
    _pDbHandle = pDbHandle;
    _rowsPerMsg = rowsPerMsg;
}

void CegoOutput::setRawMode(bool isOn)
{
    _rawMode = isOn;
}

void CegoOutput::chainOut(const Chain& msg, long affCount)
{

    if ( _pDbHandle ) 
    {
	_pDbHandle->sendResponse(msg, affCount);
    }
    else
    {
	cout << msg << endl;
    }    
}

void CegoOutput::procResultOut(const Chain& msg, const ListT<CegoProcVar>& outParamList, CegoFieldValue *pRetVal)
{
    if ( _pDbHandle ) 
    {	
	_pDbHandle->sendProcResult(msg, outParamList, pRetVal);
    }
    else
    {
	cout << msg << endl;
    }
}

void CegoOutput::tabOut(const ListT< ListT<CegoFieldValue> >& fa)
{
    headOut();
    ListT<CegoFieldValue>* pFVL = fa.First();
    while ( pFVL )
    {
	rowOut(*pFVL);
	pFVL = fa.Next();
    }
    tailOut();
} 
   
void CegoOutput::objectInfo(CegoDecodableObject& co)
{
    if ( _pDbHandle ) 
    {
	_pDbHandle->sendObjInfo(co);
    }
    else
    {
	cout << co.getFormatted();
    }	
}

void CegoOutput::headOut()
{
    
    if ( _rawMode == true ) 
	return;

    int maxLen = 0;
    CegoField* pF;

    if ( _pDbHandle ) 
    {
	_pDbHandle->collectSchema(_schema, _format);	
	_rowCount = 0;
    }
    else if ( _rawMode == false )
    {

	pF = _schema.First();
	while (pF)
	{
	    maxLen = maxFieldSize(pF);
	    cout << "+-" << fill("-", maxLen );	    
	    pF = _schema.Next();
	}
	cout << "+" << endl;
	
	
	pF = _schema.First();
	int i=0;
	while (pF)
	{
	    maxLen = maxFieldSize(pF);
	    
	    Chain tname;
	    if (pF->getTableAlias().length() > 0)
	    {
		tname = pF->getTableAlias();
	    }
	    else
	    {
		tname = pF->getTableName();
	    }

	    cout << formatCell(i, tname, maxLen);

	    i++;
	    pF = _schema.Next();
	}
		
	cout << "|" << endl;
    	
	pF = _schema.First();
	i=0;
	while (pF)
	{
	    maxLen = maxFieldSize(pF);	    

	    cout << formatCell(i, pF->getAttrName(), maxLen);

	    i++;
	    pF = _schema.Next();
	}

	cout << "|" << endl;
		
	pF = _schema.First();
	while (pF)
	{	   
	    maxLen = maxFieldSize(pF);	    
	    cout << "+-" << fill("-", maxLen );	    
	    pF = _schema.Next();
	}
	cout << "+" << endl;
    }
    
}

void CegoOutput::rowOut(const ListT<CegoField>& fl)
{
    
    if ( _pDbHandle )
    {

	_pDbHandle->collectData(fl);
	_rowCount++;

	if ( _rowCount == _rowsPerMsg )	
	{
	    _pDbHandle->sendCollectedData();
	    _rowCount=0;
	}
    }
    else if ( _rawMode == false )
    {
	int maxLen = 0;
	CegoField *pF1;
	CegoField *pF2;
	
	pF1 = fl.First();
	pF2 = _schema.First();
	
	int i=0;
	_preFill=0;
	while ( pF1 && pF2 )
	{
	    int maxLen = maxFieldSize(pF2);
	    
	    Chain s = pF1->getValue().valAsChain();
	    
	    cout << formatCell(i, s, maxLen);
	    _preFill+=maxLen + 1;

	    i++;	    
	    pF1 = fl.Next();
	    pF2 = _schema.Next();
	}
	
	cout << "|" << endl;

    }
    else
    {
	
	CegoField *pF;
	
	pF = fl.First();
	
	while ( pF )
	{
	    Chain s = pF->getValue().valAsChain();
	    cout << s;
	    pF = fl.Next();
	    if ( pF )
		cout << " ";
	    else 
		cout << endl;

	}	    
    }
}

void CegoOutput::rowOut(const ListT<CegoFieldValue>& fvl)
{

    if ( _pDbHandle )
    {

	_pDbHandle->collectData(_schema, fvl);
	_rowCount++;

	if ( _rowCount == _rowsPerMsg )	
	{
	    _pDbHandle->sendCollectedData();
	    _rowCount=0;
	}
    }
    else if ( _rawMode == false )
    {
	int maxLen = 0;
	CegoFieldValue *pFV;
	CegoField *pF2;
	
	pFV = fvl.First();
	pF2 = _schema.First();

	int i=0;
	_preFill=0;
	while ( pFV && pF2 )
	{
	    int maxLen = maxFieldSize(pF2);

	    Chain s = pFV->valAsChain();    

	    cout << formatCell(i, s, maxLen);
	    _preFill+=maxLen + 1;

	    i++;
	    pFV = fvl.Next();
	    pF2 = _schema.Next();
	}	    

	cout << "|" << endl;

    }
    else
    {
	
	CegoFieldValue *pFV;
	
	pFV = fvl.First();
	
	while ( pFV )
	{
	    Chain s = pFV->valAsChain();
	    cout << s;
	    
	    pFV = fvl.Next();
	    if ( pFV )
		cout << " ";
	    else 
		cout << endl;
	}	    
    }
    
}
 
void CegoOutput::tailOut()
{

    if ( _pDbHandle )
    {
	if ( _rowCount > 0 )
	{
	    _pDbHandle->sendCollectedData();
	    _rowCount = 0;
	}
	_pDbHandle->sendFinishData();
    }
    else if ( _rawMode == false )
    {
	int maxLen = 0;
	CegoField *pF;
	
	pF = _schema.First();
	while (pF)
	{
	    maxLen = maxFieldSize(pF);
	    
	    cout << "+-" << fill("-", maxLen );
	    
	    pF = _schema.Next();
	}
	cout << "+" << endl;
    }
}

void CegoOutput::abort(const Chain& msg)
{

    if ( _pDbHandle )
    {
	_pDbHandle->sendErrorData(msg);
    }   
    else
    {
	cout << "Aborting Select : " << msg << endl;
    }
}

int CegoOutput::maxFieldSize(CegoField *pF)
{

    int maxLen = 0;

    switch ( pF->getType() )
    {
    case INT_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_INT_LEN);
	break;

    case LONG_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_LONG_LEN);
	break;
	
    case VARCHAR_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     pF->getLength());
	break;
	
    case BOOL_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_BOOL_LEN);
	break;
	
    case DATETIME_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_DATETIME_LEN);
	break;
    case FLOAT_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_FLOAT_LEN);
	break;

    case DOUBLE_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_DOUBLE_LEN);
	break;

    case BIGINT_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     pF->getLength());
	break;

    case DECIMAL_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_DECIMAL_LEN);
	break;

    case SMALLINT_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_SMALLINT_LEN);
	break;

    case TINYINT_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_TINYINT_LEN);
	break;

    case FIXED_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_FIXED_LEN);
	break;
    case BLOB_TYPE:
	maxLen = max(pF->getAttrName().length(), 
		     pF->getTableAlias().length(), 
		     pF->getTableName().length(),
		     MAX_BLOB_LEN);
	break;

	
    default:
	maxLen = max(pF->getAttrName().length(), 0,0,0);
	break;
		
    }
    
    return maxLen;
    
}

int CegoOutput::max(int i1, int i2, int i3, int i4)
{
    int c1 =  i1 < i2 ? i2 : i1 ;
    int c2 =  i3 < i4 ? i4 : i3 ;
    return c1 < c2 ? c2 : c1 ;
}

Chain CegoOutput::fill(const Chain& s, int num)
{
    
    Chain fs = Chain("");
    while (num > 0)
    {
	fs = fs + s;
	num--;
    }

    return fs;
}

Chain CegoOutput::formatCell(int i, const Chain& s, int maxLen)
{
    Chain cell;

    if ( _format.length() < 2 ) // _format == Chain("") )
    {
	cell = Chain("|") + fill(" ", maxLen - s.length() + 1) +  s + Chain(" ");	    
    }
    else
    {
	if ( _format[i] == 'l' )
	{
	    cell = Chain("| ") + s + fill(" ", maxLen - s.length() + 1);
	}
	else if ( _format[i] == 'r' )
	{
	    cell = "|" + fill(" ", maxLen - s.length() + 1) + s + Chain(" ");
	}
	else if ( _format[i] == 'm' )
	{
	    Tokenizer lineTok(s, Chain("\n"));
	    Chain line;
	    bool isFirst = true;
	    while ( lineTok.nextToken(line) )
	    {
		if ( isFirst == false ) 
		{
		    cell += Chain("|\n");
		    if ( _preFill )
			cell += Chain("| ") + fill(" ", _preFill);		
		}

		// cout << "Line is" << line << endl;

		cell += Chain("| ") + line + fill(" ", maxLen - line.length() + 1);		
		
		isFirst = false;		  
	    }
	}		
    }

    return cell;
}
