///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoClient.cc
// -------------
// Cego client program
//
// Design and Implementation by Bjoern Lemke               
//                                                         
// (C)opyright 2000-2012 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
//
///////////////////////////////////////////////////////////////////////////////

#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>

#include <lfc/Chain.h>
#include <lfc/Exception.h>
#include <lfc/GetLongOpt.h>
#include <lfc/File.h>
#include <lfc/Timer.h>
#include <lfc/Tokenizer.h>
#include <lfc/Net.h>
#include <lfc/SigHandler.h>
#include <lfc/Tokenizer.h>

#include <lfc/Element.h>
#include <lfc/Document.h>
#include <lfc/XMLSuite.h>

#include "CegoOutput.h"
#include "CegoViewObject.h"
#include "CegoProcObject.h"
#include "CegoKeyObject.h"
#include "CegoDbHandler.h"
#include "CegoModule.h"
#include "CegoNet.h"
#include "CegoXMLdef.h"

#include "CegoDefs.h"


#define USAGE "Usage: cgclt --user=<user>/<password>\n\
          [ --server=<host>]\n\
          [ --port=<port> ]\n\
          [ --protocol={serial|xml} ]\n\
	  [ --tableset=<tableset> ]\n\
          [ --batchfile=<batchfile> ]\n\
          [ --dumpfile=<dumpfile> ]\n\
          [ --structure ]\n\
          [ --cmd=<command> ]\n\
          [ --logfile=<logfile> ]\n\
          [ --raw ]\n\
          [ --ignore ]\n\
          [ --profile=<pid> ]\n\
          [ --debug ] [ --version  ] [ --help ]"


#define DEFAULTPORT 2200
#define DEFAULTSERVER "localhost"
#define DEFAULTPROTOCOL "xml"

#define HISTFILE ".cgclt_history"
#define PROMPT "CGCLT > "
#define PROMPT2 "> "

#define CGPROFILE ".cgprofile"

#define LANGUAGE_ENV "LANG"
#define LOCALE_CATEGORY LC_TIME

#define MAXCMDLEN 5000

enum RunMode { INTERACTIVE, BATCH, COMMAND, DUMP };

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

bool flowCtl = false;
int flowCount = 10;

class AbortHandler : public SigHandler
{
  
public:

    AbortHandler(const Chain& serverName, 
		 int portNo,
		 const Chain& tableSet,
		 const Chain& user, 
		 const Chain& password,		 
		 long tid, 
		 CegoModule *pModule);
    ~AbortHandler();
    void sigCatch(int sig);

    void setQueryAbort(bool doAbort);
    
private:

    long _tid;
    bool _doAbort;
    CegoModule *_pModule;
    int _portNo;
    Chain _serverName;
    Chain _user;
    Chain _password;
    Chain _tableSet;
};

class ProfileEntry
{
  
public:

    ProfileEntry();
    ProfileEntry(const Chain& pid);
    ProfileEntry(const Chain& pid,
		 const Chain& serverName,
		 int portNo,
		 const Chain& protocol,
		 const Chain& tableSet,
		 const Chain& auth, 
		 const Chain& prompt);
    ~ProfileEntry();

    const Chain& getId() const;
    const Chain& getServerName() const;
    int getPortNo() const;
    const Chain& getProtocol() const;
    const Chain& getTableSet() const;
    const Chain& getAuth() const;
    const Chain& getPrompt() const;
        
    ProfileEntry& operator = ( const ProfileEntry& ad);
    bool operator == ( const ProfileEntry& ad);
    
private:

    Chain _pid;
    int _portNo;
    Chain _serverName;
    Chain _protocol;
    Chain _tableSet;
    Chain _auth;
    Chain _prompt;
};

int execute();
bool dispatchCmd(CegoDbHandler* pNetHandle, AbortHandler* pAH, Chain cmd, bool outMode, int& exitCode, Chain& msg);
void loadProfile(const Chain& rcFileName, ListT<ProfileEntry>& pfeList);
void dumpTableSet(CegoNet *pCegoNet, const Chain& dumpFileName, bool isStructure);
void dumpCounter(CegoNet *pCegoNet, File *pDF);
void dumpTable(CegoNet *pCegoNet, File *pDF, bool isStructure);
void dumpIndex(CegoNet *pCegoNet, File *pDF);
void dumpKey(CegoNet *pCegoNet, File *pDF);
void dumpCheck(CegoNet *pCegoNet, File *pDF);
void dumpView(CegoNet *pCegoNet, File *pDF);
void dumpProcedure(CegoNet *pCegoNet, File *pDF);

Chain user;
Chain password;
Chain serverName;
Chain tableSet;
int portNo;
Chain authString;
Chain promptString;
Chain protocol;

bool rawMode = false;
bool ignoreError = false;
bool isStructure = false;
Chain batchFileName;
Chain dumpFileName;
Chain command;
Chain logFile;    
RunMode runMode = INTERACTIVE;
bool debug=false;
CegoModule *pModule = 0;
unsigned long modId;
Chain homeDir;
Chain histPath;
CegoDbHandler::ProtocolType protType;

int main(int argc, char **argv)
{	
    
    GetLongOpt longOpt(argc, argv);
    
    longOpt.addOpt("version");
    longOpt.addOpt("help");
    longOpt.addOpt("logfile");
    longOpt.addOpt("tableset");
    longOpt.addOpt("user");
    longOpt.addOpt("cmd");
    longOpt.addOpt("batchfile");
    longOpt.addOpt("dumpfile");
    longOpt.addOpt("structure");
    longOpt.addOpt("qescmode");
    longOpt.addOpt("debug");
    longOpt.addOpt("raw");
    longOpt.addOpt("ignore");
    longOpt.addOpt("server", DEFAULTSERVER);
    longOpt.addOpt("protocol", DEFAULTPROTOCOL);
    longOpt.addOpt("port", Chain(DEFAULTPORT));
    longOpt.addOpt("profile");
    
    try
    {
	longOpt.parseOpt(); 
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	cerr << USAGE << endl;
	exit(1);	
    }
    

#ifdef HAVE_MINGW32
    homeDir = getenv("HOMEPATH");
    histPath = homeDir + "\\" + Chain(HISTFILE);
#else
    Chain homeDir(getenv("HOME"));
    Chain histPath = homeDir + "/" + Chain(HISTFILE);
#endif
    
    if ( longOpt.isSet("debug") )
	debug=true;
    
    if ( longOpt.isSet("qescmode") )
	__quoteEscapeFlag=1;
    else
	__quoteEscapeFlag=0;
    

    if ( longOpt.isSet("raw") )
	rawMode=true;

    if ( longOpt.isSet("ignore") )
	ignoreError=true;

    
    if ( longOpt.isSet("structure") )
	isStructure=true;

    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);
    }

    
    ListT<ProfileEntry> pfeList;
    
    try
    {
	loadProfile(homeDir + "/" + Chain(CGPROFILE), pfeList);
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exit(1);
    }

    logFile = longOpt.getOptValue("logfile");

    if ( longOpt.isSet("batchfile") )
    {
	runMode = BATCH;
	batchFileName = longOpt.getOptValue("batchfile");
    }
    if ( longOpt.isSet("dumpfile") )
    {
	runMode = DUMP;
	dumpFileName = longOpt.getOptValue("dumpfile");
    }
    else if ( longOpt.isSet("cmd") )
    {
	runMode = COMMAND;
	command = longOpt.getOptValue("cmd");
    }


    if ( longOpt.isSet("profile") )
    {
	Chain profile;
        profile = longOpt.getOptValue("profile");
	
	ProfileEntry *pE;
	if ( ( pE = pfeList.Find( ProfileEntry(profile))) != 0 )
	{
	    serverName = pE->getServerName();
	    tableSet = pE->getTableSet();
	    portNo = pE->getPortNo();
	    authString = pE->getAuth();
	    promptString = pE->getPrompt();	 
	    protocol = pE->getProtocol();
	}
	else
	{
	    cerr << "Profile entry " << profile << " not found" << endl;
	    exit(1);
	}
    }
    else
    {	
	serverName = longOpt.getOptValue("server");
	tableSet = longOpt.getOptValue("tableset");
	portNo = longOpt.getOptValue("port").asInteger();    
	authString = longOpt.getOptValue("user");
	promptString = Chain(PROMPT);	
	protocol = longOpt.getOptValue("protocol");
    }

    Tokenizer authTok(authString, Chain("/")); 
    authTok.nextToken(user);
    authTok.nextToken(password);

    if ( user.length() == 0 )
    {
	cerr << "User not set" << endl;
	exit (1);
    }
    if ( password.length() == 0 )
    {
	cerr << "Password not set" << endl;
	exit (1);
    }
    if ( tableSet.length() == 0 )
    {
	cerr << "Tableset not set" << endl;
	exit (1);
    }

    if ( protocol == Chain("serial") )
    {
	protType = CegoDbHandler::SERIAL;
    }
    else if ( protocol == Chain("xml") )
    {
	protType = CegoDbHandler::XML;
    }
    else
    {
	cerr << "Invalid protocol " << protocol;
	exit (1);
    }

    // for localization
    char* lang = 0;
    if ( ( lang = getenv(LANGUAGE_ENV) ) != 0)
    {
	if ( setlocale(LOCALE_CATEGORY, lang) == 0)
	{
	    Chain msg = Chain("Cannot set locale ") + Chain(lang);
	    cerr << msg << endl;
	    exit(1);
	}
    }

    if ( logFile.length() > 0 )
	pModule = new CegoModule(logFile);
    else
	pModule = new CegoModule;

    
    modId = pModule->getModId("CegoClient");
    
    if ( debug )
    {
	pModule->logModule(modId, "CegoClient", Logger::DEBUG);
	unsigned long id = pModule->getModId("CegoDbHandler");
	pModule->logModule(id, "CegoDbHandler", Logger::DEBUG);
    }
    else
    {
	pModule->logModule(modId, "CegoClient", Logger::NOTICE);
	unsigned long id = pModule->getModId("CegoDbHandler");
	pModule->logModule(id, "CegoDbHandler", Logger::NOTICE);
    }

    int exitCode = 0;
    try
    {
	exitCode = execute();
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }

    delete pModule;
    exit(exitCode);
}

int execute()
{
    int exitCode = 0;
	
    switch ( runMode )
    {
	
    case COMMAND:
    {
	
	Net n (  NETMNG_MSG_BUFLEN, NETMNG_SIZEBUFLEN );
	NetHandler* pN;
	
	try
	{
	    pN = n.connect(serverName, portNo);	    
	}
	catch ( Exception e )
	{
	    cerr << "Cannot connect to database" << endl;
	    return 1;
	}
	
	CegoDbHandler* pSH = new CegoDbHandler(pN, protType, pModule);
	
	CegoDbHandler::ResultType res = pSH->requestSession(tableSet, user, password);
	if ( res != CegoDbHandler::DB_OK )
	{
	    Chain msg = pSH->getMsg();
	    delete pSH;
	    cerr << msg << endl;
	    return 1;
	}
			   		    
	Timer t(6,3);
	t.start();

	Chain msg;
	dispatchCmd(pSH, 0, command, rawMode, exitCode, msg);
	t.stop();

	pSH->closeSession();
	
	delete pSH;
	delete pN;

	if ( exitCode != 0 )
	{
	    cerr << "Command error : " << msg << endl;
	}
	else
	{
	    cout << msg << endl;
	    cout << "ok ( " << t << " s )" << endl;
	}

	break;

    }
    case BATCH:
    {
	int lineNo=0;
	    
	Net n (  NETMNG_MSG_BUFLEN, NETMNG_SIZEBUFLEN );
	NetHandler* pN;
	
	try
	{
	    pN = n.connect(serverName, portNo);	    
	}
	catch ( Exception e )
	{
	    cerr << "Cannot connect to database" << endl;
	    return 1;
	}
	
	CegoDbHandler* pSH = new CegoDbHandler(pN, protType, pModule);
	
	CegoDbHandler::ResultType res = pSH->requestSession(tableSet, user, password);
	if ( res != CegoDbHandler::DB_OK )
	{
	    Chain msg = pSH->getMsg();
	    delete pSH;
	    cerr << msg << endl;
	    return 1;
	}

	File cmdFile(batchFileName);
	if ( cmdFile.exists() == false )
	{
	    cerr << "File " << batchFileName << " does not exist" << endl;
	    return 1;
	}
	
	try 
	{
	    cmdFile.open(File::READ);	    
	}
	catch ( Exception e)
	{
	    cout << "Cannot open batchfile " + batchFileName << endl;
	    return 1;
	}
	
	try 
	{
	    
	    Chain cmd;
	    Chain line;
	    bool disableDelimiter=false;
	    bool goOn = true;

	    while (goOn && cmdFile.readLine(line, MAXCMDLEN))
	    {
		lineNo++;
		line = line.cutTrailing(" \t");
		
		bool isCommentLine = false;
		
		if ( line.length() >= 2 )
		{		    
		    if (line.cutTrailing(" \t").subChain(1, 2) == Chain("--"))
		    {
			isCommentLine = true;
		    }
		}
		
		if ( ! isCommentLine )
		{

		    if ( line == Chain("@") )
		    {
			if ( disableDelimiter == false )
			    disableDelimiter=true;
			else
			    disableDelimiter=false;
		    }
		    else
		    {
			// cmd = line;
			cmd = cmd + Chain(" ") + line;
			cmd = cmd.cutTrailing(" \t");
		    }
			 
		    if ( cmd.length() > 0 )
		    {

			if (cmd.subChain(cmd.length()-1, cmd.length()) == Chain(";") 
			    && disableDelimiter==false)
			{
			    		       
			    // send command to server here
			    
			    Timer t(6,3);
			    t.start();
			    			   
			    Chain msg;
			    goOn = dispatchCmd(pSH, 0, cmd, rawMode, exitCode, msg);
			    
			    t.stop();

			    if ( exitCode != 0 )
			    {
				cerr << "Line Number " + Chain(lineNo) + " : " << msg << endl;
			    }
			    else
			    {
				cout << msg << endl;
				cout << "ok ( " << t << " s )" << endl;
			    }

			    if ( exitCode != 0 && ignoreError )
				goOn = true;
			    
			    cmd = Chain("");
			    
			}
		    }
		}
	    }
	    if ( cmd.length() > 1 )
	    {
		cout << "Incomplete command ..<" << cmd << ">" << endl;
	    }
	    
	    cmdFile.close();

	    pSH->closeSession();

	    delete pSH;
	    delete pN;

	}
	catch ( Exception e)
	{
	    Chain msg;
	    e.pop(msg);
	    cerr << msg << endl;
	    return 1;
	}
	
	break;

    }
    case DUMP:
    {
	
	Chain logMode("notice");
	if ( debug )
	    logMode=Chain("debug");

	CegoNet *pCegoNet = new CegoNet( protType, logFile, logMode );	
	
	try
	{
	    pCegoNet->connect(serverName, portNo, tableSet, user, password);
	}
	catch ( Exception e )
	{
	    cerr << "Cannot connect to database" << endl;
	    delete pCegoNet;
	    return 1;
	}

	dumpTableSet(pCegoNet, dumpFileName, isStructure);
	
	delete pCegoNet;
	
	exitCode = 0;
	break;

    }
    case INTERACTIVE:
    {

	Net n (  NETMNG_MSG_BUFLEN, NETMNG_SIZEBUFLEN );
	NetHandler* pN;
	
	try
	{
	    pN = n.connect(serverName, portNo);
	}
	catch ( Exception e )
	{
	    cerr << "Cannot connect to database" << endl;
	    return 1;	    
	}

	CegoDbHandler* pSH = new CegoDbHandler(pN, protType, pModule);
	
#ifdef DEBUG
	pModule->log(modId, Logger::DEBUG, Chain("Requesting session ..."));
#endif

	CegoDbHandler::ResultType res = pSH->requestSession(tableSet, user, password);
	if ( res != CegoDbHandler::DB_OK )
	{
	    Chain msg = pSH->getMsg();
	    delete pSH;
	    cerr << msg << endl;
	    return 1;
	}

	
	try 
	{	    	    

	    long tid = pSH->getTid();
	    
	    AbortHandler *pAH = new AbortHandler(serverName, portNo, tableSet, user, password, tid, pModule);
	    
#ifdef DEBUG	
	    pModule->log(modId, Logger::DEBUG, Chain("Got session ..."));
#endif

	    read_history((char*)histPath);
 
	    bool goOn = true;
	    while ( goOn )
	    {
		
		char *pC = 0;
		
		Chain cs;
		
		pC = readline(promptString);
		
		if (pC)
		{
		    cs = Chain(pC);

		    bool stmtComplete = false;
		    while (! stmtComplete )
		    {		
			add_history(pC);	
			
			if (pC)
			    free(pC);	    
			
			cs = cs.cutTrailing(Chain(" \t"));
			
			if ( cs.length() > 1)
			{		
			    if ( cs[cs.length()-2] == ';')
				stmtComplete = true;
			}
			
			if ( ! stmtComplete )
			{
			    if ( cs.length() > 1 )
			    {
				pC = readline(PROMPT2);
			    }
			    else
			    {
				pC = readline(promptString);
			    }
			    if (pC)
				cs = cs + Chain(" ") + Chain(pC);	   
			}
			
		    }
		    
		    Timer t(6,3);
		    t.start();

		    Chain msg;
		    goOn = dispatchCmd(pSH, pAH, cs, rawMode, exitCode, msg);

		    t.stop();

		    if ( exitCode != 0 )
		    {
			cerr << "Error : " <<  msg << endl;
			cerr << "Query failed" << endl;
			goOn = true;
		    }
		    else
		    {
			cout << msg << endl;
			cout << "ok ( " << t << " s )" << endl;
		    }
		    write_history((char*)histPath);
		}
		else
		{
		    cout << "Bye" << endl;
		    goOn=false;
		}
	    }
	    
	    pSH->closeSession();

	    delete pSH;
	    delete pN;
	    delete pAH;
	    
	}
	catch ( Exception e)
	{
	    Chain msg;
	    e.pop(msg);
	    cerr << msg << endl;
	    return 1;
	}

	break;

    }    
    }
    return exitCode;
}


bool dispatchCmd(CegoDbHandler *pSH, AbortHandler *pAH, Chain cmd, bool outMode, int& exitCode, Chain& msg)
{
    
    exitCode = 0;

    if ( cmd.cutTrailing(" ;") == Chain("quit") )
    {
	msg = Chain("Goodbye");
	return false;
    }

    Tokenizer tok(cmd, Chain(" ;"));
    Chain flowTok;
    if ( tok.nextToken(flowTok) )
    {
	
	if ( flowTok == Chain("flowctl") )
	{
	    Chain flowOpt;
	    if ( tok.nextToken(flowOpt) )
	    {
		if ( flowOpt == Chain("off") )
		{
		    flowCtl=false;
		    cout << "Flow control disabled" << endl;
		}
		else
		{
		    flowCtl=true;
		    if ( flowOpt.asInteger() > 0 )
			flowCount=flowOpt.asInteger();
		    cout << "Flow control enabled" << endl;    
		}
	    }	    
	    return true;
	}
    }
    
    CegoDbHandler::ResultType res;

    if ( pAH )
	pAH->setQueryAbort(true);
    
    res = pSH->reqQueryOp(cmd);
    
    if ( pAH )
	pAH->setQueryAbort(false);


    if ( res == CegoDbHandler::DB_OK )
    {
	msg = pSH->getMsg();
	long affCount = pSH->getAffected();
	
	// if ( msg == Chain("Goodbye") )
	//     return true;

	// cout << msg << endl; // " ( " << affCount << " affected )" << endl;
	
    }
    else if ( res == CegoDbHandler::DB_FIN )
    {
	msg = Chain("No rows");
    }
    else if ( res == CegoDbHandler::DB_INFO )
    {

	CegoObject::ObjectType objType = pSH->getObjType();

	switch ( objType )
	{
	case CegoObject::SYSTEM:
	case CegoObject::TABLE:
	case CegoObject::PINDEX:
	case CegoObject::UINDEX:
	case CegoObject::INDEX:
	{   
	    CegoTableObject to;
	    to.putElement( pSH->getObjElement() );
	    cout << to.getFormatted();
	    break;
	}
	case CegoObject::VIEW:
	{
	    CegoViewObject vo;
	    vo.putElement( pSH->getObjElement() );
	    cout << vo.getFormatted();
	    break;
	}
	case CegoObject::PROCEDURE:
	{
	    CegoProcObject po;
	    po.putElement( pSH->getObjElement() );
	    cout << po.getFormatted();
	    break;
	}
	case CegoObject::FKEY:
	{
	    CegoKeyObject ko;
	    ko.putElement( pSH->getObjElement() );
	    cout << ko.getFormatted();
	    break;
	}
	default:
	    break;
	}
    }
    else if ( res == CegoDbHandler::DB_DATA )
    {
	ListT<CegoField> schema;
	Chain format;

	schema = pSH->getSchema();
	format = pSH->getFormat();
	
	CegoOutput output(schema, format);
	
	output.setRawMode(outMode);

	output.headOut();
	
	// CegoDbHandler::QuerySync querySync;

	// querySync=CegoDbHandler::QUERY_MOREDATA;

	ListT<CegoFieldValue> fvl;
	long count=0;
	
	while ( ( res = pSH->receiveTableData(schema, fvl) ) == CegoDbHandler::DB_DATA )
	{	    
	    count++;
	    
	    output.rowOut(fvl);
	    fvl.Empty();		    

	    if ( flowCtl )
	    {
		if ( count % flowCount == 0 )
		{
		    cout << "Next, Reset, Abort ? ( n, r, a ) ";
		    char resp;
		    cin >> resp;
		    if ( resp == 'a' )
		    {
			pSH->abortQuery();
			break;
		    }
		    if ( resp == 'r' )
		    {
			pSH->resetQuery();
		    }
		    else
		    {
			// nothing
		    }		    
		}
	    }
		
	}
	output.tailOut();
	msg=Chain(count) + Chain(" tuples");
    }
    if ( res == CegoDbHandler::DB_ERROR )
    {
	msg = pSH->getMsg();
	exitCode = 1;
	return false;	
    }
    
    return true;
}

void dumpTableSet(CegoNet *pCegoNet, const Chain& dumpFileName, bool isStructure)
{
 
    File* pDF = new File(dumpFileName);
    pDF->open(File::WRITE);

    try
    {
	dumpCounter(pCegoNet, pDF);	
	dumpTable(pCegoNet, pDF, isStructure);
	dumpIndex(pCegoNet, pDF);
	dumpKey(pCegoNet, pDF);
	dumpCheck(pCegoNet, pDF);
	dumpView(pCegoNet, pDF);
	dumpProcedure(pCegoNet, pDF);
    }
    catch ( Exception e)
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
    }

    pDF->close();
    delete pDF;

}

void dumpCounter(CegoNet *pCegoNet, File *pDF)
{
	
    long res;
    res = pCegoNet->doQuery(Chain("list counter;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain counterName = pFV->valAsChain();
	pFV = fvl.Next();
	Chain counterValue = pFV->valAsChain();

	Chain cmd;

	cmd=Chain("drop if exists counter ") + counterName + Chain(";\n");
	pDF->writeChain(cmd);


	cmd=Chain("create counter ") + counterName + Chain(";\n");
	pDF->writeChain(cmd);
	cmd=Chain("set counter ") + counterName + Chain(" to ") + counterValue + Chain(";\n");
	pDF->writeChain(cmd);
	fvl.Empty();	
    }
}

void dumpTable(CegoNet *pCegoNet, File *pDF, bool isStructure)
{
	
    long res;
    res = pCegoNet->doQuery(Chain("list table;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> tableList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain tableName = pFV->valAsChain();
	tableList.Insert(tableName);
	fvl.Empty();	
    }
    
    Chain *pTable = tableList.First();
    while ( pTable ) 
    {
	res = pCegoNet->doQuery(Chain("desc table ") + *pTable + Chain(";"));

	Chain cmd;

	cmd=Chain("drop if exists table ") + *pTable + Chain(";\n");
	pDF->writeChain(cmd);
	
	cmd=Chain("create table ") + *pTable + Chain("(");

	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	ListT<CegoFieldValue> fvl;
	bool isFirst=true;
	while ( pCegoNet->fetchData(schema, fvl)  )
	{
	    if ( isFirst == false )
		cmd += Chain(", ");
	    isFirst=false;

	    CegoFieldValue *pFV = fvl.First();
	    Chain attrName = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrType = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrSize = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrDef = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrNull = pFV->valAsChain();

	    cmd += attrName + Chain(" ") + attrType;
	    if ( attrType == Chain(XML_STRING_VALUE)
		 || attrType == Chain(XML_BIGINT_VALUE)
		 || attrType == Chain(XML_DECIMAL_VALUE)
		 || attrType == Chain(XML_FIXED_VALUE))
		cmd +=  Chain("(") + attrSize + Chain(")");
	    if ( attrDef != Chain(XML_NULL_VALUE))
		cmd += Chain(" default ") + attrDef;
	    if ( attrNull == Chain("no"))
		cmd += Chain(" not null");
	    fvl.Empty();	
	}

	cmd += Chain(");\n");
	pDF->writeChain(cmd);


	if ( isStructure == false )
	{
	    // dump table content
	    res = pCegoNet->doQuery(Chain("select * from ") + *pTable + Chain(";"));
	    
	    schema.Empty();
	    fvl.Empty();
	    
	    pCegoNet->getSchema(schema);
	    
	    while ( pCegoNet->fetchData(schema, fvl)  )
	    {		
		Chain cmd=Chain("insert into ") + *pTable + Chain(" values (");
		
		CegoFieldValue *pFV = fvl.First();
		while ( pFV )
		{
		    Chain val = pFV->toChain();
		    cmd += val;
		    pFV = fvl.Next();
		    
		    if ( pFV )
			cmd += Chain(", ");
		}
		cmd += Chain(");\n");
		pDF->writeChain(cmd);
		fvl.Empty();
	    }
	}

	pTable = tableList.Next();
    }
}


void dumpIndex(CegoNet *pCegoNet, File *pDF)
{
	
    long res;
    res = pCegoNet->doQuery(Chain("list index;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> indexList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain indexName = pFV->valAsChain();
	indexList.Insert(indexName);
	fvl.Empty();	
    }
    
    Chain *pIndex = indexList.First();
    while ( pIndex ) 
    {
	res = pCegoNet->doQuery(Chain("desc index ") + *pIndex + Chain(";"));


	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	Chain tableName;
	Chain indexType;
	ListT<Chain> attrList;
	
	ListT<CegoFieldValue> fvl;	
	while ( pCegoNet->fetchData(schema, fvl)  )
	{	    
	    CegoFieldValue *pFV = fvl.First();
	    Chain attrName = pFV->valAsChain();
	    attrList.Insert(attrName);
	    pFV = fvl.Next();
	    tableName = pFV->valAsChain();
	    pFV = fvl.Next();
	    indexType = pFV->valAsChain();

	    fvl.Empty();
	}

	Chain cmd;

	cmd=Chain("drop if exists index ") + *pIndex + Chain(";\n");
	pDF->writeChain(cmd);

	if ( indexType == Chain(XML_PINDEX_VALUE) )
	    cmd=Chain("create primary index ");
	else if ( indexType == Chain(XML_UINDEX_VALUE) )
	    cmd=Chain("create unique index ") + *pIndex; 
	else
	    cmd=Chain("create index ") + *pIndex;

	cmd += Chain(" on ") + tableName + Chain("(");
	
	Chain *pAttr = attrList.First();
	bool isFirst=true;
	while ( pAttr )
	{
	    if ( isFirst == false )
		cmd += Chain(",");
	    isFirst=false;

	    cmd += *pAttr;
	    pAttr = attrList.Next();
	}
	
	cmd += Chain(");\n");
	pDF->writeChain(cmd);

	pIndex = indexList.Next();
    }
}


void dumpKey(CegoNet *pCegoNet, File *pDF)
{
	
    long res;
    res = pCegoNet->doQuery(Chain("list key;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> keyList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain keyName = pFV->valAsChain();
	keyList.Insert(keyName);
	fvl.Empty();	
    }
    
    Chain *pKey = keyList.First();
    while ( pKey ) 
    {
	res = pCegoNet->doQuery(Chain("desc key ") + *pKey + Chain(";"));
	
	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);

	Chain keyTable;
	Chain refTable;
	ListT<Chain> keyAttrList;
	ListT<Chain> refAttrList;

	ListT<CegoFieldValue> fvl;
	while ( pCegoNet->fetchData(schema, fvl)  )
	{

	    CegoFieldValue *pFV = fvl.First();

	    Chain tableName = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrName = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrType = pFV->valAsChain();
	    
	    if ( attrType == Chain("key") )
	    {
		keyTable=tableName;
		keyAttrList.Insert(attrName);
	    }
	    else if ( attrType == Chain("reference") )
	    {
		refTable=tableName;
		refAttrList.Insert(attrName);		
	    }

	    fvl.Empty();	
	}

	Chain cmd;	

	cmd=Chain("drop if exists foreign key ") + *pKey + Chain(";\n");
	pDF->writeChain(cmd);


	cmd=Chain("alter table ") + keyTable + Chain(" add foreign key ") + *pKey + Chain("(");
	Chain *pKeyAttr = keyAttrList.First();
	while ( pKeyAttr )
	{
	    cmd += *pKeyAttr;
	    pKeyAttr = keyAttrList.Next();
	    if ( pKeyAttr )
		cmd += Chain(",");
	}

	cmd+=Chain(") references ") + refTable + Chain("(");
	Chain *pRefAttr = refAttrList.First();
	while ( pRefAttr )
	{
	    cmd += *pRefAttr;
	    pRefAttr = refAttrList.Next();
	    if ( pRefAttr )
		cmd += Chain(",");
	}

	cmd += Chain(");\n");
	pDF->writeChain(cmd);

	pKey = keyList.Next();
    }
}

void dumpCheck(CegoNet *pCegoNet, File *pDF)
{
	
    long res;
    res = pCegoNet->doQuery(Chain("list check;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> checkList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain checkName = pFV->valAsChain();
	checkList.Insert(checkName);
	fvl.Empty();	
    }
    
    Chain *pCheck = checkList.First();
    while ( pCheck ) 
    {
	res = pCegoNet->doQuery(Chain("desc check ") + *pCheck + Chain(";"));

	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	ListT<CegoFieldValue> fvl;
	bool isFirst=true;
	while ( pCegoNet->fetchData(schema, fvl) );

	CegoFieldValue *pFV = fvl.First();
	Chain checkTable = pFV->valAsChain();
	pFV = fvl.Next();
	Chain checkCondition = pFV->valAsChain();
	
	fvl.Empty();	
       
	Chain cmd;	

	cmd=Chain("drop if exists check ") + *pCheck + Chain(";\n");
	pDF->writeChain(cmd);

	cmd=Chain("alter table ") +  checkTable + Chain(" add check ") + *pCheck + Chain(" on ") + checkCondition + Chain(";\n");
	pDF->writeChain(cmd);

	pCheck = checkList.Next();
    }
}

void dumpView(CegoNet *pCegoNet, File *pDF)
{
	
    long res;
    res = pCegoNet->doQuery(Chain("list view;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> viewList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain viewName = pFV->valAsChain();
	viewList.Insert(viewName);
	fvl.Empty();	
    }
    
    Chain *pView = viewList.First();
    while ( pView ) 
    {
	res = pCegoNet->doQuery(Chain("show view ") + *pView + Chain(";"));


	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	ListT<CegoFieldValue> fvl;
	bool isFirst=true;
	Chain viewText;
	while ( pCegoNet->fetchData(schema, fvl)  )
	{
	    CegoFieldValue *pFV = fvl.First();
	    viewText = pFV->valAsChain();
	    fvl.Empty();	
	}

	Chain cmd;	

	cmd=Chain("drop if exists view ") + *pView + Chain(";\n");
	pDF->writeChain(cmd);

	cmd=Chain("create ") + viewText;
	cmd += Chain("\n");
	pDF->writeChain(cmd);

	pView = viewList.Next();
    }
}

void dumpProcedure(CegoNet *pCegoNet, File *pDF)
{
	
    long res;
    res = pCegoNet->doQuery(Chain("list procedure;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> procList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain procName = pFV->valAsChain();
	procList.Insert(procName);
	fvl.Empty();	
    }
    
    Chain *pProc = procList.First();
    while ( pProc ) 
    {
	res = pCegoNet->doQuery(Chain("show procedure ") + *pProc + Chain(";"));


	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	ListT<CegoFieldValue> fvl;
	Chain procText;
	while ( pCegoNet->fetchData(schema, fvl)  )
	{
	    CegoFieldValue *pFV = fvl.First();
	    procText = pFV->valAsChain();
	    fvl.Empty();	
	}

	Chain cmd;

	cmd=Chain("drop if exists procedure ") + *pProc + Chain(";\n");
	pDF->writeChain(cmd);

	cmd=Chain("@\ncreate ") + procText;
	cmd += Chain("\n@\n");
	pDF->writeChain(cmd);

	pProc = procList.Next();
    }
}

void loadProfile(const Chain& rcFileName, ListT<ProfileEntry>& pfeList)
{
    File rcFile(rcFileName);
    if ( rcFile.exists() )
    {
	
	rcFile.open(File::READ);

	Chain rcLine;
	while ( rcFile.readLine(rcLine) )
	{
	    Tokenizer tok(rcLine, Chain(":"));

	    Chain pid;
	    Chain serverName;
	    Chain portNo;
	    Chain protocol;
	    Chain tableSet;
	    Chain prompt;
	    Chain user;
	    Chain pwd;
	    
	    if ( tok.nextToken(pid) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(serverName) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(portNo) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(protocol) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(tableSet) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(prompt) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(user) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(pwd) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }

	    Chain auth = user + Chain("/") + pwd;
	    ProfileEntry pfe(pid, serverName, portNo.asInteger(), protocol, tableSet, auth, prompt);
	    pfeList.Insert(pfe);
	    
	}		
    }
}

AbortHandler::AbortHandler(const Chain& serverName, 
			   int portNo, 
			   const Chain& tableSet,
			   const Chain& user, 
			   const Chain& password,
			   long tid, 
			   CegoModule *pModule) :  SigHandler() 
{
    init();
    install(SIGINT);
#ifndef HAVE_MINGW32
    install(SIGPIPE);
#endif
    _tid = tid;
    _pModule = pModule;
    _portNo = portNo;
    _tableSet = tableSet;
    _user = user;
    _password = password;
    _serverName = serverName;
}

AbortHandler::~AbortHandler()
{
} 

void AbortHandler::sigCatch(int sig) 
{
    
    try {
	install(SIGINT);
#ifndef HAVE_MINGW32
	install(SIGPIPE);
#endif

	if ( _doAbort )
	{

	    cout << endl << "Aborting query .." << endl;
	    

	    Net n (  NETMNG_MSG_BUFLEN, NETMNG_SIZEBUFLEN );
	    NetHandler* pN;
	    
	    pN = n.connect(_serverName, _portNo);
	    
	    CegoDbHandler* pSH = new CegoDbHandler(pN, protType, _pModule);
	    	    
	    CegoDbHandler::ResultType res = pSH->requestSession(_tableSet, _user, _password);
	    if ( res != CegoDbHandler::DB_OK )
	    {
		Chain msg = pSH->getMsg();
		throw Exception(EXLOC, msg);
	    }
    
	    Chain msg;
	    	    
	    res = pSH->reqQueryAbort(_tid);
	    	    
	    if ( res == CegoDbHandler::DB_OK )
	    {
		Chain msg = pSH->getMsg();
	    }
	    
	    pSH->closeSession();

	    delete pSH;
	    delete pN;

	}
	else
	{
	    cout << "Use quit to quit" << endl;
	}
    }
    catch ( Exception e)
    {
	Chain msg;
	e.pop(msg);
	cout << "Error : " << msg << endl;
    }
    
}

void AbortHandler::setQueryAbort(bool doAbort)
{
    _doAbort = doAbort;
}


ProfileEntry::ProfileEntry()
{
}

ProfileEntry::ProfileEntry(const Chain& pid)
{
    _pid = pid;
}

ProfileEntry::ProfileEntry(const Chain& pid, 
			   const Chain& serverName, 
			   int portNo, 
			   const Chain& protocol, 
			   const Chain& tableSet,
			   const Chain& auth, 
			   const Chain& prompt)
{
    _pid = pid;
    _serverName = serverName;
    _portNo = portNo;
    _protocol = protocol;
    _tableSet = tableSet;
    _auth = auth;
    _prompt = prompt;
}

ProfileEntry::~ProfileEntry()
{
} 


const Chain& ProfileEntry::getId() const
{
    return _pid;
}

const Chain& ProfileEntry::getServerName() const
{
    return _serverName;
}

int ProfileEntry::getPortNo() const
{
    return _portNo;
}

const Chain& ProfileEntry::getProtocol() const
{
    return _protocol;
}

const Chain& ProfileEntry::getTableSet() const
{
    return _tableSet;
}

const Chain& ProfileEntry::getAuth() const
{
    return _auth;
}

const Chain& ProfileEntry::getPrompt() const
{
    return _prompt;
}

ProfileEntry& ProfileEntry::operator = ( const ProfileEntry& pe)
{
    _pid = pe._pid;
    _serverName = pe._serverName;
    _portNo = pe._portNo;
    _protocol = pe._protocol;
    _tableSet = pe._tableSet;
    _auth = pe._auth;
    _prompt = pe._prompt;
    return (*this);
}
    
bool ProfileEntry::operator == ( const ProfileEntry& pe)
{
    if ( _pid == pe._pid ) 
	return true;
    return false;
}
