/**********************************************************************************************
    Copyright (C) 2009 Oliver Eichler oliver.eichler@gmx.de

    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 of the License, 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; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

**********************************************************************************************/
#include "CWptDB.h"
#include "CWpt.h"
#include "CMapDB.h"
#include "IMap.h"
#include "CCanvas.h"
#include "GeoMath.h"
#include "WptIcons.h"
#include "CDlgWptEdit.h"
#include "CDlgTrackEdit.h"

#include <QtGui>
#include <QSqlQuery>
#include <QSqlError>
#include <proj_api.h>

CWptDB * CWptDB::m_self = 0;

CWptDB::CWptDB(QObject * parent)
: IDB(parent)
{
    m_self = this;

    db = QSqlDatabase::database("qlandkarte");

    QSqlQuery query(db);
    if(!query.exec("SELECT version FROM versioninfo")) {
        initDB();
    }
    else if(query.next()) {
        QString version = query.value(0).toString();
        if(version != DB_VERSION) {
            migrateDB(version);
        }
    }
    else {
        initDB();
    }


    QDir dir( QCoreApplication::applicationDirPath());
    if(!dir.exists("images")){
        dir.mkdir("images");
    }



}


CWptDB::~CWptDB()
{

}


void CWptDB::initDB()
{
    QSqlQuery query(db);

    if(!query.exec( "CREATE TABLE waypoint ("
        "idx            INTEGER PRIMARY KEY AUTOINCREMENT,"
        "key            TEXT NOT NULL UNIQUE,"
        "date           TIMESTAMP NOT NULL,"
        "icon           TEXT NOT NULL,"
        "name           TEXT NOT NULL,"
        "comment        TEXT ,"
        "lat            REAL NOT NULL,"
        "lon            REAL NOT NULL,"
        "ele            REAL DEFAULT 1e+25,"
        "prx            REAL DEFAULT 1e+25,"
        "link           TEXT,"
        "sticky         BOOLEAN NOT NULL"
    ")")) {
        qDebug() << query.lastError();
    }

    if(!query.exec( "CREATE TABLE images ("
        "idx            INTEGER PRIMARY KEY AUTOINCREMENT,"
        "file           INTEGER NOT NULL,"
        "info           TEXT,"
        "wptidx         INTEGER NOT NULL"
    ")")) {
        qDebug() << query.lastError();
    }

}

void CWptDB::migrateDB(QString &version)
{
    /*
      Do Db migration here and don't forget to update the initdb function
      To migrate Db just do like that

      // Test initial version
      if (version=="1.0") {
         if(!query.exec( "ALTER TABLE waypoint (.....)")
      }

      if (version=="1.1") {
         if(!query.exec( "ALTER TABLE waypoint (.....)")
      }

      */

    QSqlQuery query(db);

    if (version=="1.1") {
        if(!query.exec( "ALTER TABLE images RENAME TO images_old" )){
            qDebug() << query.lastError();
        }
        if(!query.exec( "CREATE TABLE images ("
            "idx            INTEGER PRIMARY KEY AUTOINCREMENT,"
            "file           INTEGER NOT NULL,"
            "info           TEXT,"
            "wptidx         INTEGER NOT NULL"
        ")")) {
            qDebug() << query.lastError();
        }
        if(!query.exec( "DROP TABLE images_old")) {
            qDebug() << query.lastError();
        }
    }
}

void CWptDB::draw(QPainter& p, const QRectF& viewport)
{
    IMap& map = CMapDB::self().getMap();

    qreal lon1 = viewport.left() * RAD_TO_DEG;
    qreal lat1 = viewport.top() * RAD_TO_DEG;
    qreal lon2 = viewport.right() * RAD_TO_DEG;
    qreal lat2 = viewport.bottom() * RAD_TO_DEG;

    QSqlQuery query(db);
    query.prepare("SELECT lon, lat, icon, name FROM waypoint WHERE :lon1 < lon AND lon < :lon2 AND :lat1 < lat AND lat < :lat2");
    query.bindValue(":lon1", lon1);
    query.bindValue(":lat1", lat1);
    query.bindValue(":lon2", lon2);
    query.bindValue(":lat2", lat2);
    if(!query.exec()){
        qDebug() << query.lastError();
    }

    bool largeIcons = CCanvas::self().largeIcons;

    while(query.next()){
        qreal x         = query.value(0).toDouble() * DEG_TO_RAD;
        qreal y         = query.value(1).toDouble() * DEG_TO_RAD;
        QString icon    = query.value(2).toString();
        QString name    = query.value(3).toString();

        map.convertRad2Pt(x,y);

        if(largeIcons){
            p.drawPixmap(x - 7, y - 7, getWptIconByName(icon).scaled(32,32,Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
        }
        else{
            p.drawPixmap(x - 7, y - 7, getWptIconByName(icon));
        }

        CCanvas::drawText(name, p, QPoint(x,y), Qt::darkBlue);
    }
}

bool CWptDB::add(CWpt& wpt, QString& errmsg)
{
    int wptidx = -1;
    QSqlQuery query(db);

    // try to get index for existing waypoint
    if(!query.exec( QString("SELECT idx FROM waypoint WHERE key=\"%1\"").arg(wpt.key()))){
        errmsg = query.lastError().text();
        return false;
    }

    // if there is an existing waypoint update waypoint
    if(query.next()){
        wptidx = query.value(0).toInt();
        query.prepare("UPDATE waypoint SET date=:date, icon=:icon, name=:name, comment=:comment, lat=:lat, lon=:lon, ele=:ele, prx=:prx, sticky=:sticky WHERE idx=:idx");

        QDateTime date = QDateTime::fromTime_t( wpt.timestamp);
        date.setTimeSpec(Qt::UTC);
        query.bindValue(":date",date.toString("yyyy-MM-dd hh:mm:ss"));
        query.bindValue(":icon", wpt.icon);
        query.bindValue(":name", wpt.name);
        query.bindValue(":comment", wpt.comment);
        query.bindValue(":lat", wpt.lat);
        query.bindValue(":lon", wpt.lon);
        query.bindValue(":ele", wpt.ele);
        query.bindValue(":prx", wpt.prx);
        query.bindValue(":idx", wptidx);
        query.bindValue(":sticky", wpt.sticky);

    }
    else{
        // insert new waypoint
        query.prepare("INSERT INTO waypoint (key, date, icon, name, comment, lat, lon, ele, prx, sticky) VALUES (:key, :date, :icon, :name, :comment, :lat, :lon, :ele, :prx, :sticky)");
        query.bindValue(":key", wpt.key());

        QDateTime date = QDateTime::fromTime_t( wpt.timestamp);
        date.setTimeSpec(Qt::UTC);
        query.bindValue(":date",date.toString("yyyy-MM-dd hh:mm:ss"));
        query.bindValue(":icon", wpt.icon);
        query.bindValue(":name", wpt.name);
        query.bindValue(":comment", wpt.comment);
        query.bindValue(":lat", wpt.lat);
        query.bindValue(":lon", wpt.lon);
        query.bindValue(":ele", wpt.ele);
        query.bindValue(":prx", wpt.prx);
        query.bindValue(":sticky", wpt.sticky);

    }

    // push changes to the db
    if(!query.exec()) {
        errmsg = query.lastError().text();
        return false;
    }

    // get waypoint index of the waypoint
    if(!query.exec( QString("SELECT idx FROM waypoint WHERE key=\"%1\"").arg(wpt.key()))){
        errmsg = query.lastError().text();
        return false;
    }
    query.next();
    wptidx = query.value(0).toInt();

    // handle  attached pixmaps

    // 1. delete all entries attached to this waypoint
    if(!delWptImg(wptidx, errmsg)){
        return false;
    }

    // 2. add current images as new entries.

    quint32 count = 1;
    QDir dir( QCoreApplication::applicationDirPath() + "/images");
    CWpt::image_t image;
    foreach(image, wpt.images){
        quint32 hash = qHash(wpt.key()) ^ count++;
        image.pixmap.save(dir.filePath(QString("%1.png").arg(hash)));
        query.prepare("INSERT INTO images (file, info, wptidx) VALUES (:file, :info, :wptidx)");
        query.bindValue(":file", hash);
        query.bindValue(":info", image.info);
        query.bindValue(":wptidx", wptidx);

        if(!query.exec()) {
            errmsg = query.lastError().text();
            return false;
        }
    }

    emit sigChanged();
    return true;
}

bool CWptDB::delWptImg(int wptidx, QString& errmsg)
{
    QSqlQuery query(db);

    // get filenames from all entries for the given waypoint
    if(!query.exec( QString("SELECT file FROM images WHERE wptidx=%1").arg(wptidx))){
        errmsg = query.lastError().text();
        return false;
    }

    // remove files from disc
    QDir dir( QCoreApplication::applicationDirPath() + "/images");
    while(query.next()){
        QString file = query.value(0).toString();
        QFile::remove(dir.filePath(file) + ".png");
    }

    // remove entries from db
    if(!query.exec( QString("DELETE FROM images WHERE wptidx=%1").arg(wptidx))){
        errmsg = query.lastError().text();
        return false;
    }

    return true;
}


bool CWptDB::del(const QString& key, QString& errmsg)
{
    QSqlQuery query(db);
    if(key == "*"){
        // remove images
        if(!query.exec( QString("SELECT idx FROM waypoint"))){
            errmsg = query.lastError().text();
            return false;
        }
        while(query.next()){
            if(!delWptImg(query.value(0).toInt(),errmsg)){
                return false;
            }
        }

        // remove waypoints
        if(!query.exec( QString("DELETE FROM waypoint"))){
            errmsg = query.lastError().text();
            return false;
        }
    }
    else{
        // remove images
        if(!query.exec( QString("SELECT idx FROM waypoint WHERE key=\"%1\"").arg(key))){
            errmsg = query.lastError().text();
            return false;
        }
        while(query.next()){
            if(!delWptImg(query.value(0).toInt(),errmsg)){
                return false;
            }
        }

        // remove waypoint
        if(!query.exec( QString("DELETE FROM waypoint WHERE key=\"%1\"").arg(key))){
            errmsg = query.lastError().text();
            return false;
        }
    }
    emit sigChanged();
    return true;
}

bool CWptDB::delByIcon(const QString& icon, QString& errmsg)
{
    QSqlQuery query(db);
    if(!query.exec( QString("SELECT idx FROM waypoint WHERE icon=\"%1\"").arg(icon))){
        errmsg = query.lastError().text();
        return false;
    }
    while(query.next()){
        if(!delWptImg(query.value(0).toInt(),errmsg)){
            return false;
        }
    }


    if(!query.exec( QString("DELETE FROM waypoint WHERE icon=\"%1\"").arg(icon))){
        errmsg = query.lastError().text();
        return false;
    }

    emit sigChanged();
    return true;
}

bool CWptDB::list(QStringList& keys, QStringList& names, QStringList& icons, QString& errmsg)
{
    keys.clear();
    names.clear();
    icons.clear();

    QSqlQuery query(db);
    if(!query.exec("SELECT key, name, icon FROM waypoint")) {
        errmsg = query.lastError().text();
        return false;
    }

    while(query.next()){
        keys  << query.value(0).toString();
        names << query.value(1).toString();
        icons << query.value(2).toString();
    }

    return true;
}

bool CWptDB::get(const QString& key, CWpt& wpt, QString& errmsg)
{
    QSqlQuery query(db);
    query.prepare("SELECT date, icon, name, comment, lat, lon, ele, prx, sticky, idx FROM waypoint WHERE key = :key");
    query.bindValue(":key", key);

    if(!query.exec()) {
        errmsg = query.lastError().text();
        return false;
    }

    if(!query.next()){
        errmsg = tr("Waypoint not found.");
        return false;
    }

    wpt._key_       = key;
    QDateTime date  = QDateTime::fromString(query.value(0).toString(),"yyyy-MM-dd hh:mm:ss");
    wpt.timestamp   = date.toTime_t();
    wpt.icon        = query.value(1).toString();
    wpt.name        = query.value(2).toString();
    wpt.comment     = query.value(3).toString();
    wpt.lat         = query.value(4).toDouble();
    wpt.lon         = query.value(5).toDouble();
    wpt.ele         = query.value(6).toDouble();
    wpt.prx         = query.value(7).toDouble();
    wpt.sticky      = query.value(8).toBool();
    quint32 wptidx  = query.value(9).toInt();

    query.prepare("SELECT file, info FROM images WHERE wptidx = :wptidx");
    query.bindValue(":wptidx", wptidx);

    if(!query.exec()) {
        errmsg = query.lastError().text();
        return false;
    }

    QDir dir( QCoreApplication::applicationDirPath() + "/images");
    while(query.next()){
        CWpt::image_t image;
        image.pixmap    = QPixmap(dir.filePath(QString("%1.png").arg(query.value(0).toInt())));
        image.info      = query.value(1).toString();

        wpt.images << image;
    }
    return true;
}

void CWptDB::addWptToPos()
{
    QSettings cfg;
    QString errmsg;
    qreal lon=0;
    qreal lat=0;
    qreal ele=0;
    CWpt wpt;
    if (CCanvas::self().getCurrentPos(lon , lat, ele) == CCanvas::eLocked)
    {
        wpt.ele = ele;
    }
    wpt.lon = lon * RAD_TO_DEG;
    wpt.lat = lat * RAD_TO_DEG;
    wpt.name = tr("New waypoint");
    wpt.icon = cfg.value("environment/icon", "City (Capitol)").toString();
    CDlgWptEdit *wptEdit = new CDlgWptEdit(wpt, NULL);
    wptEdit->exec();
}

bool  CWptDB::getUsedIcons(QSet<QString>& icons, QString& errmsg)
{

    QSqlQuery query(db);
    if(!query.exec("select distinct icon from waypoint;")) {
        errmsg = query.lastError().text();
        return false;
    }

    while(query.next()){
        icons  << query.value(0).toString();
    }

    return true;
}
