/**********************************************************************************************
    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 "CMapDB.h"
#include "CMapWorld.h"
#include "CMapRaster.h"
#include "IGps.h"

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

CMapDB * CMapDB::m_self = 0;

CMapDB::CMapDB(QObject * parent)
: IDB(parent)
, zoomidx(6)
{
    m_self = this;

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

    defaultMap  = new CMapWorld(this);

    QSettings cfg;
    zoomidx = cfg.value("mapdb/zoomidx", zoomidx).toInt();

    zoomidx = zoomidx < 6 ? 6 : zoomidx;
    zoomidx = defaultMap->zoom(zoomidx);
}


CMapDB::~CMapDB()
{
    QSettings cfg;
    cfg.setValue("mapdb/zoomidx", zoomidx);

    db.close();
}


IMap& CMapDB::getMap()
{
    if(theMap.isNull()) {
        qreal lon1 = center.x();
        qreal lat1 = center.y();
        defaultMap->convertPt2Rad(lon1,lat1);

        //         qDebug() << "find map for" << lon1 << lat1 << zoomidx;

        QSqlQuery query(db);

        QString str = QString("SELECT filename FROM fileinfo, area WHERE "
            "fileinfo.idx = area.fileinfo AND "
            "%1 > area.lon1 AND "
            "%1 < area.lon2 AND "
            "%2 < area.lat1 AND "
            "%2 > area.lat2 AND "
            "%3 <= area.zmax AND "
            "%3 >= area.zmin "
            "ORDER BY area.zmax").arg(lon1).arg(lat1).arg(zoomidx);

        if(!query.exec(str)) {
            qDebug() << query.lastError();
        }

        if(query.next()) {
            QDir dir( QCoreApplication::applicationDirPath() +"/maps" );
            QString filename = query.value(0).toString();

            filename    = dir.filePath(filename);
            theMap      = new CMapRaster(filename, this);
            if(theMap->isValid()) {
                theMap->resize(buffer1.size());
                theMap->zoom(zoomidx);

                qreal u = center.x();
                qreal v = center.y();
                defaultMap->convertPt2Rad(u,v);
                theMap->convertRad2Pt(u,v);

                theMap->move(QPoint(u,v), center);

                connect(theMap, SIGNAL(sigAboutToDelete()), this, SLOT(slotDestroyDetailMap()));
            }
            else {
                delete theMap;
            }
        }

    }

    return theMap.isNull() ? *defaultMap : *theMap;
}


void CMapDB::updateOverlays()
{
    //     qDebug() << "CMapDB::updateOverlays()";

    if(theMap.isNull()) {

        buffer1.fill(QColor(0,0,0,0));

        QPainter p;
        p.begin(&buffer1);
        p.setPen(QColor(0,0,0,255));
        p.setBrush(Qt::NoBrush);

        qreal lon1 = -180.0     * DEG_TO_RAD;
        qreal lat1 =   85.0511  * DEG_TO_RAD;
        qreal lon2 =  180.0     * DEG_TO_RAD;
        qreal lat2 =  -85.0511  * DEG_TO_RAD;
        defaultMap->convertRad2Pt(lon1,lat1);
        defaultMap->convertRad2Pt(lon2,lat2);
        p.drawRect(QRectF(lon1, lat1, lon2 - lon1, lat2 - lat1));

        QSqlQuery query(db);
        if(!query.exec("SELECT lon1, lat1, lon2, lat2 FROM area")) {
            qDebug() << query.lastError();
        }

        p.setPen(QColor(0,0,0,255));
        p.setBrush(QColor(0,0,255,40));
        QRectF viewport = defaultMap->viewport;
        while(query.next()) {
            lon1 = query.value(0).toDouble();
            lat1 = query.value(1).toDouble();
            lon2 = query.value(2).toDouble();
            lat2 = query.value(3).toDouble();

            QRectF rect(lon1, lat1, lon2 - lon1, lat1 - lat2);

            qDebug() << rect << viewport << rect.intersects(viewport) <<  rect.contains(viewport) ;
            if(rect.intersects(viewport)) {

                defaultMap->convertRad2Pt(lon1,lat1);
                defaultMap->convertRad2Pt(lon2,lat2);
                p.drawRect(QRectF(lon1, lat1, lon2 - lon1, lat2 - lat1));
            }
        }

        p.end();
    }
}


void CMapDB::slotDestroyDetailMap()
{
    qDebug() << "void CMapDB::slotDestroyDetailMap()";

    qreal u = center.x();
    qreal v = center.y();
    theMap->convertPt2Rad(u,v);

    defaultMap->zoom(zoomidx);
    defaultMap->convertRad2Pt(u,v);
    defaultMap->move(QPoint(u,v), center);

    theMap = 0;

    updateOverlays();
}


void CMapDB::zoom(bool in)
{
    IMap& map = getMap();

    qreal u = center.x();
    qreal v = center.y();
    map.convertPt2Rad(u,v);

    zoom(in, QPointF(u,v));

    emit sigChanged();
}


void CMapDB::zoom(bool in, const QPointF& pos)
{
    IMap& map = getMap();

    qreal u1 = pos.x();
    qreal v1 = pos.y();

    qreal u2 = center.x();
    qreal v2 = center.y();

    zoomidx += in ? -1 : 1;
    zoomidx  = map.zoom(zoomidx);

    {
        IMap& map = getMap();
        map.convertRad2Pt(u1,v1);
        map.move(QPoint(u1,v1), QPoint(u2,v2));
    }
    updateOverlays();
    emit sigChanged();
}


void CMapDB::resize(const QSize& s)
{
    if(theMap) theMap->resize(s);
    defaultMap->resize(s);

    center  = QPoint(s.width()>>1, s.height()>>1);
    buffer1 = QPixmap(s);

    updateOverlays();
    emit sigChanged();
}


void CMapDB::move(const QPoint& old, const QPoint& next)
{
    getMap().move(old,next);
    updateOverlays();
    emit sigChanged();
}


void CMapDB::draw(QPainter& p, const QRectF& /*viewport*/)
{
    getMap().draw(p);
    if(theMap.isNull()) {
        p.drawPixmap(0,0,buffer1);
    }
}


void CMapDB::slotRebuildDB()
{
    QSqlQuery query(db);

    // do a hard reset on the database
    if(!query.exec("DROP TABLE IF EXISTS fileinfo")) {
        qDebug() << query.lastError();
    }

    if(!query.exec("DROP TABLE IF EXISTS area")) {
        qDebug() << query.lastError();
    }

    // initialize table structure
    if(!query.exec( "CREATE TABLE fileinfo ("
        "idx            INTEGER PRIMARY KEY AUTOINCREMENT,"
        "name           TEXT ,"
        "filename       TEXT NOT NULL,"
        "date           TIMESTAMP NOT NULL,"
        "uptodate       INTEGER NOT NULL"
    ")")) {
        qDebug() << query.lastError();
    }

    // area information table
    if(!query.exec( "CREATE TABLE area ("
        "idx            INTEGER PRIMARY KEY AUTOINCREMENT,"
        "fileinfo       INTEGER NOT NULL,"
        "lon1           REAL NOT NULL,"
        "lat1           REAL NOT NULL,"
        "lon2           REAL NOT NULL,"
        "lat2           REAL NOT NULL,"
        "zmin           REAL NOT NULL,"
        "zmax           REAL NOT NULL,"
        "priority       INTEGER NOT NULL"
    ")")) {
        qDebug() << query.lastError();
    }

    /// @todo get path via file dialog
    QDir dir( QCoreApplication::applicationDirPath() +"/maps" );

    dir.setFilter(QDir::Files);
    dir.setSorting(QDir::Size | QDir::Reversed);
    QFileInfoList fileList = dir.entryInfoList();
    QFileInfo fileInfo;
    foreach(fileInfo, fileList) {
        QPointF ref1, ref2;
        qint32 zmin, zmax, idx;

        if(!CMapRaster::readHeader(fileInfo.absoluteFilePath(), ref1, ref2, zmin, zmax)) {
            continue;
        }

        qDebug() << fileInfo.absoluteFilePath() << ref1 << ref2 << zmax;

        query.prepare("INSERT INTO fileinfo (name,filename,date,uptodate) VALUES(:name,:filename,:date,:uptodate)");
        query.bindValue(":name", fileInfo.fileName());
        query.bindValue(":filename", fileInfo.fileName());
        query.bindValue(":date", fileInfo.lastModified());
        query.bindValue(":uptodate", 1);
        if(!query.exec()) {
            qDebug() << query.lastError();
            continue;
        }

        if(!query.exec( "SELECT max(idx) FROM fileinfo")) {
            qDebug() << query.lastError();
            continue;
        }
        query.next();
        idx = query.value(0).toInt();
        qDebug() << idx;

        query.prepare("INSERT INTO area (fileinfo, lon1, lat1, lon2, lat2, zmin, zmax, priority) VALUES(:fileinfo, :lon1, :lat1, :lon2, :lat2, :zmin, :zmax, :priority)");
        query.bindValue(":fileinfo", idx);
        query.bindValue(":lon1", ref1.x());
        query.bindValue(":lat1", ref1.y());
        query.bindValue(":lon2", ref2.x());
        query.bindValue(":lat2", ref2.y());
        query.bindValue(":zmin", zmin);
        query.bindValue(":zmax", zmax);
        query.bindValue(":priority", 0);
        if(!query.exec()) {
            qDebug() << query.lastError();
            continue;
        }
    }

    updateOverlays();

    emit sigChanged();
}


void CMapDB::getAvailableMaps(QList<map_list_item_t>& maps)
{
    maps.clear();

    QSqlQuery query(db);
    if(!query.exec("SELECT idx, name FROM fileinfo")) {
        qDebug() << query.lastError();
    }
    map_list_item_t item;

    while(query.next()) {
        item.id     = query.value(0).toInt();
        item.name   = query.value(1).toString();
        maps << item;
    }
}


QRectF CMapDB::getViewport()
{
    qreal u1 = 0;
    qreal v1 = 0;

    qreal u2 = center.x() << 1;
    qreal v2 = center.y() << 1;

    IMap& map = getMap();
    map.convertPt2Rad(u1,v1);
    map.convertPt2Rad(u2,v2);

    return QRectF(u1,v2, u2 - u1, v1 - v2);
}
