#include "database.h"

#include <qsproject.h>
#include <qvariant.h>
#include <qdatatable.h>
#include <qgroupbox.h>
#include <qheader.h>
#include <qlabel.h>
#include <qlistview.h>
#include <qpushbutton.h>
#include <qsplitter.h>
#include <qtextedit.h>
#include <qlayout.h>
#include <qtooltip.h>
#include <qwhatsthis.h>
#include <qaction.h>
#include <qmenubar.h>
#include <qpopupmenu.h>
#include <qtoolbar.h>
#include <qiconset.h>
#include <qapplication.h>

#include <qsqldriver.h>
#include <qmessagebox.h>
#include <qsqldatabase.h>
#include <qlineedit.h>
#include <qcombobox.h>
#include <qspinbox.h>
#include <qsqlerror.h>
#include <qinputdialog.h>

#include "connect.h"

static const char* const chart_data[] = {
"22 22 5 1",
"       c None",
"+      c #F4F11A",
"@      c #F21515",
"#      c #079913",
"$      c #1F3DD3",
"                      ",
"                      ",
"      ++++@@@@@       ",
"     +++++@@@@@@      ",
"   ##+++++@@@@@@@@    ",
"   ###++++@@@@@@@@    ",
"  ####++++@@@@@@@$$   ",
" ######+++@@@@@@$$$$  ",
" ######+++@@@@@$$$$$  ",
" #######++@@@@$$$$$$  ",
" #######++@@@$$$$$$$  ",
" ########+@@$$$$$$$$  ",
" #########$$$$$$$$$$  ",
" ##########$$$$$$$$$  ",
" ###########$$$$$$$$  ",
" ###########$$$$$$$$  ",
"  ###########$$$$$$   ",
"   ##########$$$$$    ",
"   ###########$$$$    ",
"     #########$$      ",
"      ########$       ",
"                      "
};

static const char* const address_data[] = {
"22 22 4 1",
"       c None",
".      c #724309",
"+      c #E8BF86",
"@      c #000000",
"                      ",
"              ...... ",
"             ..++++..",
"...............++++++.",
".++++++++++++++++++++.",
".++@++++++@@++@+@@@++.",
".++@@@@+@+@+++@++@@++.",
".++@@+@+@@@+@@@+@++++.",
".++@@+@++@+++@++@@@++.",
".++++++++++++++++++++.",
".++++++++++++++++++++.",
".++++@+++++++++++++++.",
".++++@+@+@@+@++@++@++.",
".+++@@@@@@+@@+@@@@@++.",
".++++++++++++++++++++.",
".++++@+++++++@+++++++.",
".+++@@@+++@++@+@@+@++.",
".++++@+@+@@@@++@+@+++.",
".++++++++++++++++++++.",
".++++++++++++++++++++.",
"......................",
"                      "
};

static const char* const query_data[] = {
"22 22 2 1",
"       c None",
"@      c #F21515",
"                      ",
"        @@@@@@        ",
"      @@@@@@@@@@      ",
"     @@        @@     ",
"     @@        @@     ",
"    @@          @@    ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@            @@   ",
"   @@     @@     @@   ",
"   @@      @@    @@   ",
"    @@      @@  @@    ",
"     @@      @@@@     ",
"     @@       @@@     ",
"      @@@@@@@@@@@     ",
"        @@@@@@  @@    ",
"                      ",
"                      "
};



class QCustomSqlCursor : public QSqlCursor
{
public:
    QCustomSqlCursor( const QString & query = QString::null, bool autopopulate = TRUE,
		      QSqlDatabase* db = 0 )
	: QSqlCursor( QString::null, autopopulate, db )
    {

	exec( query );
	if ( isSelect() && autopopulate ) {
	    QSqlRecordInfo inf = db->recordInfo( *(QSqlQuery*)this );
	    if ( !inf.isEmpty() ) {
		for ( QSqlRecordInfo::iterator it = inf.begin(); it != inf.end(); ++it ) {
		    append( *it );
		}
	    }
	}
	setMode( QSqlCursor::ReadOnly );
    }
    QCustomSqlCursor( const QCustomSqlCursor & other ): QSqlCursor( other ) {}
    bool select( const QString & /*filter*/, const QSqlIndex & /*sort*/ = QSqlIndex() )
	{ return exec( lastQuery() ); }
    QSqlIndex primaryIndex( bool /*prime*/ = TRUE ) const
	{ return QSqlIndex(); }
    int insert( bool /*invalidate*/ = TRUE )
	{ return FALSE; }
    int update( bool /*invalidate*/ = TRUE )
	{ return FALSE; }
    int del( bool /*invalidate*/ = TRUE )
	{ return FALSE; }
    void setName( const QString& /*name*/, bool /*autopopulate*/ = TRUE ) {}
};

static void showError( const QSqlError& err, QWidget* parent = 0 )
{
   QString errStr ( "The database reported an error\n" );
    if ( !err.databaseText().isEmpty() )
	errStr += err.databaseText();
    if ( !err.driverText().isEmpty() )
	errStr += err.driverText();
    QMessageBox::warning( parent, "Error", errStr );
}

// -------------------------------------------------------------------------


DataBase::DataBase( QWidget* parent,  const char* name, WFlags fl )
    : QMainWindow( parent, name, fl )
{
    (void)statusBar();
    if ( !name )
	setName( "DataBase" );
    resize( 660, 530 );
    setCaption( tr( "SQL-QSA-Example" ) );
    setCentralWidget( new QWidget( this, "qt_central_widget" ) );
    QHBoxLayout *DataBaseLayout = new QHBoxLayout( centralWidget(), 11, 6, "DataBaseLayout");

    QVBoxLayout *Layout1 = new QVBoxLayout( 0, 0, 6, "Layout1");

    QSplitter *vsplit = new QSplitter( centralWidget(), "vsplit" );
    vsplit->setOrientation( QSplitter::Vertical );

    QSplitter *hsplit = new QSplitter( vsplit, "hsplit" );
    hsplit->setOrientation( QSplitter::Horizontal );

    listView = new QListView( hsplit, "listView" );
    listView->addColumn( tr( "Tables" ) );
    listView->addColumn( tr( "Type" ) );
    listView->addColumn( tr( "Required" ) );
    listView->header()->setLabel( 0, tr( "Tables" ) );
    listView->header()->setLabel( 1, tr( "Type" ) );
    listView->header()->setLabel( 2, tr( "Required" ) );
    listView->setMargin( 0 );
    listView->setAllColumnsShowFocus( TRUE );
    listView->setRootIsDecorated( TRUE );
    listView->setResizeMode( QListView::LastColumn );

    sqlTable = new QDataTable( hsplit, "sqlTable" );

    QGroupBox *groupBox = new QGroupBox( vsplit, "groupBox" );
    groupBox->setTitle( tr( "SQL Query" ) );
    groupBox->setColumnLayout(0, Qt::Vertical );
    groupBox->layout()->setSpacing( 6 );
    groupBox->layout()->setMargin( 11 );
    QGridLayout *groupBoxLayout = new QGridLayout( groupBox->layout() );
    groupBoxLayout->setAlignment( Qt::AlignTop );

    QVBoxLayout *Layout2 = new QVBoxLayout( 0, 0, 6, "Layout2");

    queryTextEdit = new QTextEdit( groupBox, "queryTextEdit" );
    Layout2->addWidget( queryTextEdit );

    QHBoxLayout *Layout3 = new QHBoxLayout( 0, 0, 6, "Layout3");
    QSpacerItem* spacer = new QSpacerItem( 20, 20, QSizePolicy::Expanding,
					   QSizePolicy::Minimum );
    Layout3->addItem( spacer );

    submitButton = new QPushButton( groupBox, "submitButton" );
    submitButton->setText( tr( "&Submit" ) );
    submitButton->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5,
	    (QSizePolicy::SizeType)0, 0, 0, submitButton->sizePolicy().hasHeightForWidth() ) );
    submitButton->setMinimumSize( QSize( 80, 0 ) );
    submitButton->setEnabled( FALSE );
    Layout3->addWidget( submitButton );

    QPushButton *clearButton = new QPushButton( groupBox, "clearButton" );
    clearButton->setText( tr( "C&lear" ) );
    clearButton->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5,
	    (QSizePolicy::SizeType)0, 0, 0, clearButton->sizePolicy().hasHeightForWidth() ) );
    clearButton->setMinimumSize( QSize( 80, 0 ) );
    Layout3->addWidget( clearButton );
    Layout2->addLayout( Layout3 );

    groupBoxLayout->addLayout( Layout2, 0, 0 );
    Layout1->addWidget( vsplit );

    QHBoxLayout *Layout4 = new QHBoxLayout( 0, 0, 6, "Layout4");

    label = new QLabel( centralWidget(), "label" );
    label->setText( tr( "Press \"Connect\" to open a database" ) );
    Layout4->addWidget( label );

    QPushButton *connectButton = new QPushButton( centralWidget(), "connectButton" );
    connectButton->setText( tr( "&Connect..." ) );
    connectButton->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)0,
	    (QSizePolicy::SizeType)0, 0, 0, connectButton->sizePolicy().hasHeightForWidth() ) );
    connectButton->setMinimumSize( QSize( 80, 0 ) );
    Layout4->addWidget( connectButton );
    Layout1->addLayout( Layout4 );
    DataBaseLayout->addLayout( Layout1 );

    connect( clearButton, SIGNAL( clicked() ), queryTextEdit, SLOT( clear() ) );
    connect( connectButton, SIGNAL( clicked() ), this, SLOT( dbConnect() ) );
    connect( submitButton, SIGNAL( clicked() ), this, SLOT( execQuery() ) );
    connect( listView, SIGNAL( returnPressed(QListViewItem*) ),
	     this, SLOT( showTable(QListViewItem*) ) );
    connect( listView, SIGNAL( doubleClicked(QListViewItem*) ),
	     this, SLOT( showTable(QListViewItem*) ) );

    conDiag = new ConnectDialog( this, "Connection Dialog", TRUE );

    initQSA();

    setupActions();

    hsplit->setResizeMode( listView, QSplitter::KeepSize );
    vsplit->setResizeMode( groupBox, QSplitter::KeepSize );
}

DataBase::~DataBase()
{

}

void DataBase::setupActions()
{
    QToolBar *toolBar = new QToolBar( "", this, DockTop );
    toolBar->setLabel( tr( "Tools" ) );

    QAction *fileExitAction = new QAction( this, "fileExitAction" );
    fileExitAction->setText( tr( "Exit" ) );
    fileExitAction->setMenuText( tr( "E&xit" ) );
    connect( fileExitAction, SIGNAL( activated() ), this, SLOT( exit() ) );

    QAction *scriptOpenDevAction = new QAction( this, "scriptsOpenDevAction" );
    scriptOpenDevAction->setText( tr( "Open Qt Script for Applications Developer..." ) );
    scriptOpenDevAction->setMenuText( tr( "Open Qt Script for Applications Developer..." ) );
    connect( scriptOpenDevAction, SIGNAL( activated() ), this, SLOT( openDeveloper() ) );

    QAction *scriptRunAction = new QAction( this, "scriptsRunAction" );
    scriptRunAction->setText( tr( "Run Script..." ) );
    scriptRunAction->setMenuText( tr( "Run Script..." ) );

    QString s = "show age statistcs";
    QAction *a;
    a = new QAction( tr( s ), QPixmap( (const char**)chart_data ), tr( s ), 0, this, s.latin1() );
    scripts.insert( a, "AgeStatistics" );
    a->addTo( toolBar );
    connect( a, SIGNAL( activated() ), this, SLOT( runScript() ) );

    s = "show all addresses";
    a = new QAction( tr( s ), QPixmap( ( const char**)address_data ), tr( s ), 0, this, s.latin1() );
    scripts.insert( a, "Addresses" );
    a->addTo( toolBar );
    connect( a, SIGNAL( activated() ), this, SLOT( runScript() ) );

    s = "run a certain query";
    a = new QAction( tr( s ), QPixmap( ( const char**)query_data ), tr( s ), 0, this, s.latin1() );
    scripts.insert( a, "Query" );
    a->addTo( toolBar );
    connect( a, SIGNAL( activated() ), this, SLOT( runScript() ) );

    QMenuBar *menubar = new QMenuBar( this, "menubar" );

    QPopupMenu *fileMenu = new QPopupMenu( this );
    fileExitAction->addTo( fileMenu );

    QPopupMenu *scriptMenu = new QPopupMenu( this );
    scriptOpenDevAction->addTo( scriptMenu );
    scriptRunAction->addTo( scriptMenu );

    menubar->insertItem( tr( "&File" ), fileMenu );
    menubar->insertItem( tr( "&Scripts" ), scriptMenu );

    connect( fileExitAction, SIGNAL( activated() ), this, SLOT( exit() ) );
    connect( scriptOpenDevAction, SIGNAL( activated() ), this, SLOT( openDeveloper() ) );
    connect( scriptRunAction, SIGNAL( activated() ), this, SLOT( scriptChooser() ) );
}

void DataBase::initQSA()
{
    interpreter.addObject( sqlTable );
    interpreter.addObject( queryTextEdit );
    interpreter.addObject( submitButton );
    interpreter.addObject( label );
    interpreter.addObject( conDiag->editHostname );
    interpreter.addObject( conDiag->editDatabase );
    interpreter.addObject( conDiag->portSpinBox );
    interpreter.addObject( conDiag->editUsername );
    interpreter.addObject( conDiag->editPassword );
    interpreter.addObject( conDiag->comboDriver );
    interpreter.currentProject()->open( "database.qsa" );
}

void DataBase::dbConnect()
{
    if ( conDiag->exec() != QDialog::Accepted )
	return;
    if ( sqlTable->sqlCursor() ) {
	sqlTable->setSqlCursor( 0 );
    }

    if ( QSqlDatabase::contains( "DemoDB" ) ) {
	QSqlDatabase* oldDb = QSqlDatabase::database( "DemoDB" );
	oldDb->close();
	QSqlDatabase::removeDatabase( "DemoDB" );
    }

    QSqlDatabase *db = QSqlDatabase::addDatabase( conDiag->comboDriver->currentText(), "DemoDB" );
    if ( !db ) {
	QMessageBox::warning( this, "Error", "Could not open database" );
	return;
    }
    db->setHostName( conDiag->editHostname->text() );
    db->setDatabaseName( conDiag->editDatabase->text() );
    db->setPort( conDiag->portSpinBox->value() );
    db->setUserName( conDiag->editUsername->text() );
    db->setPassword( conDiag->editPassword->text() );
    if ( !db->open() ) {
	showError( db->lastError(), this );
	return;
    }

    label->setText( "Double-Click on a table-name to view the contents" );
    listView->clear();

    QStringList tables = db->tables();
    for ( QStringList::Iterator it = tables.begin(); it != tables.end(); ++it ) {
	QListViewItem* listItem = new QListViewItem( listView, *it );
	QSqlRecordInfo ri = db->recordInfo ( *it );
	for ( QSqlRecordInfo::Iterator it = ri.begin(); it != ri.end(); ++it ) {
	    QString req;
	    if ( (*it).isRequired() > 0 ) {
		req = "Yes";
	    } else if ( (*it).isRequired() == 0 ) {
		req = "No";
	    } else {
		req = "?";
	    }
	    QListViewItem* fi = new QListViewItem( listItem, (*it).name(),
				+ QVariant::typeToName( (*it).type() ), req );
	    listItem->insertItem( fi );
	}
	listView->insertItem( listItem );
    }
    submitButton->setEnabled( TRUE );
}

void DataBase::execQuery()
{
    QCustomSqlCursor *cursor = new QCustomSqlCursor( queryTextEdit->text(),
			       TRUE, QSqlDatabase::database( "DemoDB", TRUE ) );

    if ( cursor->isSelect() ) {
	sqlTable->setSqlCursor( cursor, TRUE, TRUE );
	sqlTable->refresh( QDataTable::RefreshAll );
	QString txt( "Query OK" );
	if ( cursor->size() >= 0 )
	    txt += ", returned rows: " + QString::number( cursor->size() );
	label->setText( txt );
    } else {
	if ( !cursor->isActive() ) {
	    showError( cursor->lastError(), this );
	} else {
	    label->setText( QString("Query OK, affected rows: %1")
			    .arg( cursor->numRowsAffected() ) );
	}
    }
}

void DataBase::showTable( QListViewItem *item )
{
    QListViewItem *i = item->parent();
    if ( !i ) {
	i = item;
    }

    QSqlCursor *cursor = new QSqlCursor( i->text( 0 ), TRUE,
			 QSqlDatabase::database( "DemoDB", TRUE ) );
    sqlTable->setSqlCursor( cursor, TRUE, TRUE );
    sqlTable->setSort( cursor->primaryIndex() );
    sqlTable->refresh( QDataTable::RefreshAll );
    label->setText( "Displaying table " + i->text( 0 ) );
}

void DataBase::exit()
{
    qApp->quit();
}

void DataBase::openDeveloper()
{
    interpreter.currentProject()->openDeveloper();
}

void DataBase::scriptChooser()
{
    bool ok = FALSE;
    QString func = QInputDialog::getItem( "Choose a function", "Choose a function",
			interpreter.globalFunctions(), 0, FALSE, &ok );
    if ( !ok || func.isEmpty() )
	return;
    interpreter.call( func, QValueList<QVariant>() );
}

void DataBase::runScript()
{
    QString s = *scripts.find( (QAction*)sender() );
    interpreter.call( s, QValueList<QVariant>() );
}
