///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoLogReader.cc
// ----------------                           
// Cego log reading and printing
// 
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2011 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: Main
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// base includes
#include <lfc/Exception.h>
#include <lfc/GetLongOpt.h>
#include <lfc/Chain.h>
#include <lfc/Datetime.h>

// cego includes
#include "CegoDefs.h"
#include "CegoLogRecord.h"
#include "CegoQueryHelper.h"
#include "CegoLogManager.h"
#include "CegoTableObject.h"
#include "CegoKeyObject.h"
#include "CegoViewObject.h"
#include "CegoProcObject.h"

#include <string.h>
#include <stdlib.h>

#define USAGE "Usage: cglog --log=<logfile>\n\
          [ --version  ] [ --help ]"

extern char __lfcVersionString[];
extern char __lfcxmlVersionString[];

int main(int argc, char **argv)
{

    try 
    {
	GetLongOpt longOpt(argc, argv);
	
	longOpt.addOpt("version");
	longOpt.addOpt("help");
	longOpt.addOpt("log");
	
	try
	{
	    longOpt.parseOpt(); 
	}
	catch ( Exception e )
	{
	    Chain msg;
	    e.pop(msg);
	    cerr << msg << endl;
	    cerr << USAGE << endl;
	    exit(1);	
	}
	
	
	if ( longOpt.isSet( Chain("help") ) )
	{	
	    cerr << USAGE << endl;
	    exit(0);
	}
	
	if ( longOpt.isSet( Chain("version") ) )
	{
	    cout << CEGO_PRODUCT << " (" << sizeof(long) * 8 << " bit), Version " << CEGO_VERSION 
		 << " [ lfc : " << __lfcVersionString  
		 << ", lfcxml : " <<  __lfcxmlVersionString << " ]" << endl;
	    cout << CEGO_COPYRIGHT << endl;
	    exit(0);
	}
	
	if ( longOpt.isSet( Chain("log") ) == false )
	{
	    throw Exception(EXLOC, "Redo logfile file must be specified");
	}
	
	Chain logFileName = longOpt.getOptValue("log");
	
	File logFile(logFileName);
	
	logFile.open(File::READ);
	
	bool moreLog = true;
	
	int offset;
	int c = logFile.readByte((char*)&offset, sizeof(int));	
	
	int pos = sizeof(int);
	
	CegoQueryHelper qh;
	
	while (pos < offset)
	{
	    int len;
	    c = logFile.readByte((char*)&len, sizeof(int));	
	    
	    cout << "Read len : " << len << endl;
	    
	    pos += len + sizeof(int);
	    
	    char* buf = new char[len];
	    
	    logFile.readByte(buf, len);
	    CegoLogRecord lr;
	    
	    lr.decode(buf);

	    delete buf;

	    Datetime dt( lr.getTS() );

	    cout << "LSN=" << lr.getLSN() << ":";
	    cout << "TS=" << dt.asChain() << ":";
	    cout << "TID=" << lr.getTID() << ":";

	    if ( lr.getObjName().length() > 0 )
	    { 
		cout << "ObjName=" << lr.getObjName() << ":";
		cout << "ObjType=" << lr.getObjType() << ":";
	    }

	    cout << "LogAction=";

	    switch ( lr.getAction() )
	    {
	    case CegoLogRecord::LOGREC_CREATE:
	    {
		cout << "CREATE:" << endl;
		
		cout << "--- Log Data Start ---" << endl;
		
		switch ( lr.getObjType() )
		{
		case CegoObject::TABLE:
		case CegoObject::SYSTEM:
		case CegoObject::INDEX:
		case CegoObject::PINDEX:
		case CegoObject::UINDEX:
		{
		    CegoTableObject to;
		    to.decode(lr.getData());
		    cout << to.toChain();
		    break;
		}
		case CegoObject::VIEW:
		{
		    CegoViewObject vo;
		    vo.decode(lr.getData());
		    cout << vo.toChain();
		    break;		    
		}
		case CegoObject::PROCEDURE:
		{
		    CegoProcObject po;
		    po.decode(lr.getData());
		    cout << po.toChain();
		    break;		    
		}
		case CegoObject::FKEY:
		{
		    CegoKeyObject ko;
		    ko.decode(lr.getData());
		    cout << ko.toChain();
		    break;		    
		}
		default:
		    break;
		}

		cout << "--- Log Data End   ---" << endl;

	       
		break;
	    }
	    case CegoLogRecord::LOGREC_ALTER:
	    {
		cout << "ALTER:" << endl;

		cout << "--- Log Data Start ---" << endl;

		switch ( lr.getObjType() )
		{
		case CegoObject::TABLE:
		case CegoObject::SYSTEM:
		case CegoObject::INDEX:
		case CegoObject::PINDEX:
		case CegoObject::UINDEX:
		{
		    CegoTableObject to;
		    to.decode(lr.getData());
		    cout << to.toChain();
		    break;
		}
		case CegoObject::VIEW:
		{
		    CegoViewObject vo;
		    vo.decode(lr.getData());
		    cout << vo.toChain();
		    break;		    
		}
		case CegoObject::PROCEDURE:
		{
		    CegoProcObject po;
		    po.decode(lr.getData());
		    cout << po.toChain();
		    break;		    
		}
		case CegoObject::FKEY:
		{
		    CegoKeyObject ko;
		    ko.decode(lr.getData());
		    cout << ko.toChain();
		    break;		    
		}
		default:
		    break;
		}		
		cout << "--- Log Data End   ---" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_RENAME:
	    {
		cout << "RENAME:" << endl;
		cout << "--- Log Data Start ---" << endl;
		Chain newObjName(lr.getData(), lr.getDataLen());
		cout << "NewName=" << newObjName << endl; 
		cout << "--- Log Data End   ---" << endl;
	    }
	    case CegoLogRecord::LOGREC_DROP:
	    {
		cout << "DROP:" << endl;
		
		cout << "--- No log data ---" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_INSERT:
	    {
		cout << "INSERT:" << endl;
		
		cout << "--- Log Data Start ---" << endl;	

		
		ListT<CegoFieldValue> fvl;
		ListT<CegoBlob> blobList;
		
		int tid = 0;
		int tastep = 0;
		
		CegoTupleState ts;
		qh.decodeNativeFVL(fvl, blobList, lr.getData(), lr.getDataLen(), tid, tastep, ts);

		CegoFieldValue *pFV = fvl.First();
		while ( pFV )
		{
		    if ( pFV->getLength() > 0 )
		    {
			cout << *pFV << "|";
		    }
		    
		    // cout << "Field " << pF->getId() << " Len=" << pF->getFieldValue().getLength() << endl;
		    pFV = fvl.Next();
		}
		cout << endl;
		if ( blobList.Size() > 0 )
		{
		    CegoBlob *pBlob = blobList.First();
		    while ( pBlob )
		    {
			cout << "BlobSize = " << pBlob->getSize() << endl;
			pBlob = blobList.Next();
		    }
		}
		cout << "--- Log Data End   ---" << endl;
		
		// cout << lr << endl;;

		break;
	    }
	    case CegoLogRecord::LOGREC_DELETE:
	    {
		cout << "DELETE:" << endl;
		
		cout << "--- Log Data Start ---" << endl;	

		CegoPredDesc *pPred = 0;	    
		CegoQueryHelper qh;
		Chain tableAlias;

		qh.decodeDelRec(tableAlias, pPred, lr.getData(), lr.getDataLen(), 0);

		cout << " TableAlias=" << tableAlias << endl;
		
		if ( pPred )
		{
		    cout << "where " << pPred->toChain() << endl;
		}
		
		cout << "--- Log Data End   ---" << endl;
		
		break;
	    }
	    case CegoLogRecord::LOGREC_DELETE_COMMIT:
	    {
		cout << "DELETE_COMMIT:" << endl;	       
		cout << "--- No log data ---" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_UPDATE:
	    {
		cout << "UPDATE:" << endl;		
		cout << "--- Log Data Start ---" << endl;	
		Chain tableAlias;
		CegoPredDesc *pPred = 0;
		ListT<CegoField> updList;
		ListT<CegoExpr*> exprList;
		
		qh.decodeUpdRec(tableAlias,
				pPred, 
				updList,
				exprList,
				lr.getData(), lr.getDataLen(), 0);

		
		cout << "TableAlias=" << tableAlias << endl;
		CegoField *pF = updList.First();
		CegoExpr **pExpr = exprList.First();
		while ( pF && pExpr )
		{
		    cout << "Set " << pF->getAttrName() << " = " << (*pExpr)->toChain() << endl;
		    pF = updList.Next();
		    pExpr = exprList.Next();
		}
		if ( pPred )
		    cout << "where " << pPred->toChain() << endl;
		cout << "--- Log Data End   ---" << endl;
		
		break;
	    }
	    case CegoLogRecord::LOGREC_UPDATE_COMMIT:
	    {
		cout << "UPDATE_COMMIT:" << endl;
		cout << "--- No log data ---" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_BEGIN:
	    {
		cout << "BEGIN:" << endl;
		cout << "--- No log data ---" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_COMMIT:
	    {
		cout << "COMMIT:" << endl;
		cout << "--- No log data ---" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_SYNC:
	    {
		cout << "SYNC:" << endl;
		cout << "--- No log data ---" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_BUPAGE:
	    {
		cout << "BUPAGE:" << endl;
		cout << "[... page data ...]" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_BUFBM:
	    {
		cout << "BUFBM:" << endl;
		cout << "[ ... file bitmap ...]" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_BUFIN:
	    {
		cout << "BUFIN:" << endl;
		cout << "--- No log data ---" << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_ADDCOUNTER:
	    {
		cout << "ADDCOUNTER:" << endl;
		Chain counterName(lr.getData(), lr.getDataLen());		
		cout << "Counter="<< counterName << endl;
		break;
	    }
	    case CegoLogRecord::LOGREC_DELCOUNTER:
	    {
		cout << "DELCOUNTER:" << endl;
		Chain counterName(lr.getData(), lr.getDataLen());		
		cout << "Counter="<< counterName << endl;
		break;
	    }
	    default:
		break;   		             	
	    }
	}
    }
    catch ( Exception e)
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exit(1);
    }
    exit(0);
}
