/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "database.h"
#include <QtGui>
#include <QtXml>

// Database schema definition and open/create functions

QString resultsTable = QString("(TestName varchar, TestCaseName varchar, Series varchar, Idx varchar, ") + 
                       QString("Result varchar, ChartType varchar, Title varchar, ChartWidth varchar, ") + 
                       QString("ChartHeight varchar, TestTitle varchar, QtVersion varchar, Iterations varchar") +
                       QString(")");

void execQuery(QSqlQuery query, bool warnOnFail)
{
    bool ok = query.exec();
    if (!ok && warnOnFail) {
        qDebug() << "FAIL:" << query.lastQuery() << query.lastError().text();
    }
}

void execQuery(const QString &spec, bool warnOnFail)
{
    QSqlQuery query;
    query.prepare(spec);
    execQuery(query, warnOnFail);
}

QSqlDatabase openDataBase(const QString &databaseFile)
{
//    qDebug() << "open data base";
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(databaseFile);
    bool ok = db.open(); 
    if (!ok)
        qDebug() << "FAIL: could not open database";
    return db;
}

QSqlDatabase createDataBase(const QString &databaseFile)
{
//    qDebug() << "create data base";
    QSqlDatabase db = openDataBase(databaseFile);

    execQuery("DROP TABLE Results", false);
    execQuery("CREATE TABLE Results " + resultsTable);

    return db;
}

struct Tag
{
    Tag(QString key, QString value)
    : key(key.trimmed()), value(value.trimmed())
    {
    
    }

    QString key;
    QString value;
};

QList<Tag> parseTag(const QString &tag)
{
    // Format: key1=value ; key2=value
    //         key1=value key2=value
    //         value--value
    
    QList<Tag> keyValues;

    QString keyValuePairSeparator("");
    if (tag.contains(";"))
        keyValuePairSeparator = ';';
    if (tag.contains("--"))
        keyValuePairSeparator = "--";

    foreach (QString keyValue, tag.split(keyValuePairSeparator)) {
        if (keyValue.contains("=")) {
            QStringList parts = keyValue.split("=");
            keyValues.append(Tag(parts.at(0), parts.at(1)));
        } else {
            keyValues.append(Tag(QString(), keyValue)); // no key, just a value.
        }
    }

    return keyValues;
}

void loadXml(const QStringList &fileNames)
{
    foreach(const QString &fileName, fileNames) {
        QFileInfo fi( fileName );
        loadXml(fileName, fi.fileName());
    }
}

void loadXml(const QString &fileName, const QString &context)
{
    QFile f(fileName);
    f.open(QIODevice::ReadOnly);
    loadXml(f.readAll(), context);
}

void loadXml(const QByteArray &xml, const QString& context)
{
    QDomDocument doc;

    int line;
    int col;
    QString errorMsg;
    if (doc.setContent(xml, &errorMsg, &line, &col) == false) {
        qDebug() << "dom setContent failed" << line << col << errorMsg;
    }
    
    // Grab "Value" from <Environment><QtVersion>Value</QtVersion></Environment>
    QString qtVersion = doc.elementsByTagName("Environment").at(0).toElement().elementsByTagName("QtVersion")
                        .at(0).toElement().childNodes().at(0).nodeValue();
    QString testCase = doc.elementsByTagName("TestCase").at(0).toElement().attributeNode("name").value();
        
//    qDebug() << "qt version" << qtVersion;
//    qDebug() << "test case" << testCase;

    DataBaseWriter writer;
    writer.testName = testCase; // testCaseName and testName is mixed up in the database writer class
    writer.qtVersion = qtVersion;
    
    QDomNodeList testFunctions = doc.elementsByTagName("TestFunction");
    for (int i = 0; i < testFunctions.count(); ++i) {
        QDomElement function = testFunctions.at(i).toElement();
        QString functionName = function.attributeNode("name").value();
        writer.testCaseName = functionName; // testCaseName and testName is mixed up in the database writer class
        
//        qDebug() << "fn" << functionName;

        QDomNodeList results = function.elementsByTagName("BenchmarkResult");
        for (int j = 0; j < results.count(); ++j) {    
            QDomElement result = results.at(j).toElement();
            QString tag = result.attributeNode("tag").value();

                Q_UNUSED(context);
//            if (!context.isEmpty())
//                tag += QString(" (%1)").arg(context);

            QString series;
            QString index;

            // By convention, "--" separates series and indexes in tags.
            if (tag.contains("--")) {
                QStringList parts = tag.split("--");
                series = parts.at(0);
                index = parts.at(1);
            } else {
                series = tag;
            }

            QString resultString = result.attributeNode("value").value();
            QString iterationCount = result.attributeNode("iterations").value();
            double resultNumber = resultString.toDouble() / iterationCount.toDouble();
            writer.addResult(series, index, QString::number(resultNumber), iterationCount);
//            qDebug() << "result" << series  << index << tag << resultString << iterationCount;
        }
    }
}

void displayTable(const QString &table)
{
    QSqlTableModel *model = new QSqlTableModel();
    model->setTable(table);
    model->select();
    QTableView *view = new QTableView();
    view->setModel(model);
    view->show();
}

void printDataBase()
{
   QSqlQuery query;
   query.prepare("SELECT TestName, TestCaseName, Result FROM Results;");
   bool ok  = query.exec(); 
   qDebug() <<  "printDataBase ok?" <<  ok;

   query.next();
   qDebug() << "";
   qDebug() << "Benchmark" << query.value(0).toString();
   query.previous();

   while (query.next()) {
       //  QString country = query.value(fieldNo).toString();
       //  doSomething(country);
       qDebug() << "result for" << query.value(1).toString() << query.value(2).toString();
   } 
}

// TempTable implementation

static int tempTableIdentifier = 0;
TempTable::TempTable(const QString &spec)
{
    m_name = "TempTable" + QString::number(tempTableIdentifier++);
    execQuery("CREATE TEMP TABLE " + m_name + " " + spec);
}

TempTable::~TempTable()
{
    // ref count and drop it?
}

QString TempTable::name()
{
    return m_name;
}

// DataBaseWriter implementation

DataBaseWriter::DataBaseWriter()
{
    disable = false;
    chartSize = QSize(800, 400);
    databaseFileName = ":memory:";
    qtVersion = QT_VERSION_STR;
}

void DataBaseWriter::openDatabase()
{
    db = openDataBase(databaseFileName);
}

void DataBaseWriter::createDatabase()
{
    db = createDataBase(databaseFileName);
}

void DataBaseWriter::beginTransaction()
{
    if (db.transaction() == false) {
        qDebug() << db.lastError();
        qFatal("no transaction support");
    }
}

void DataBaseWriter::commitTransaction()
{
    db.commit();
}

void DataBaseWriter::rollbackTransaction()
{
    db.rollback();
}

void DataBaseWriter::addResult(const QString &result)
{
	return addResult(QString(), QString(), result);
}

void DataBaseWriter::addResult(const QString &series, const QString &index, const QString &result, const QString &iterations)
{
    if (disable)
        return;

     QSqlQuery query;

     query.prepare("INSERT INTO Results (TestName, TestCaseName, Series, Idx, Result, ChartWidth, ChartHeight, Title, TestTitle, ChartType, QtVersion, Iterations) "
                    "VALUES (:TestName, :TestCaseName, :Series, :Idx, :Result, :ChartWidth, :ChartHeight, :Title, :TestTitle, :ChartType, :QtVersion, :Iterations)");
     query.bindValue(":TestName", testName);
     query.bindValue(":TestCaseName", testCaseName);
     query.bindValue(":Series", series);
     query.bindValue(":Idx", index);
     query.bindValue(":Result", result);
     query.bindValue(":ChartWidth", chartSize.width());
     query.bindValue(":ChartHeight", chartSize.height());
     query.bindValue(":Title", chartTitle);
     query.bindValue(":TestTitle", testTitle);
     query.bindValue(":QtVersion", qtVersion);
     query.bindValue(":Iterations", iterations);


    if (chartType == LineChart)
        query.bindValue(":ChartType", "LineChart");
    else
        query.bindValue(":ChartType", "BarChart");
    execQuery(query);
}
