/**********************************************************************************************
    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 "CCanvas.h"
#ifdef WINCE
#include "CGpsd.h"
#else
#include "CDlgEdit.h"
#endif
#include "CNMEA.h"
#include "CNMEA_Mtk.h"
#include "CDlgMenu.h"
#include "CUnitMetric.h"

#include "CMapDB.h"
#include "CWptDB.h"
#include "CTrackDB.h"
#include "IMap.h"

#include "CTransferAgent.h"
#include "CBacklight.h"

#include "CMenu.h"
#include "CDlgWptEdit.h"

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

#include <QSqlQuery>
#include <QSqlError>

CCanvas * CCanvas::m_self = 0;

#ifdef DISPLAY_SMALL
static const QString styleSheetMenu =
"QMenu {"
"   border: 2px solid #ffffff;"
"   border-radius: 6px;"
"   color: white ;"
"   font: 75 9pt 'Sans Serif';"
"   background-color: rgba(0, 0, 255, 200);"
"}"
"QMenu::item {"
"   padding: 15px;"
"   border: 1px solid transparent;"
"}"
"QMenu::item:selected {"
"   border-color: darkblue;"
"   background: rgba(100, 100, 100, 150);"
"}";
#else
static const QString styleSheetMenu =
"QMenu::item {"
"   padding: 20px;"
"}"
"QMenu::item:selected {"
"   background: rgba(100, 100, 100, 150);"
"}";

#endif

CCanvas::CCanvas()
: largeIcons(false)
, initialized(false)
, needRedraw(true)
, cursorMode(eLocked)
, moveMap(false)
, lockInterface(false)

{
    m_self = this;
    setupUi(this);
    setupKeys(this);
#ifdef DISPLAY_SMALL
    labelTime->setFlag(Qt::AlignLeft|Qt::AlignVCenter);
    labelDate->setFlag(Qt::AlignLeft|Qt::AlignVCenter);
    labelZoom->setFlag(Qt::AlignRight|Qt::AlignVCenter);
    innerArea = QRect(0,0,100,100);
#else
    innerArea = QRect(0,0,200,200);
#endif                       //DISPLAY_LARGE

    innerArea.moveCenter(rect().center());

    labelTime->setText("--:--:--");
    labelDate->setText("--------");
    labelZoom->setText("zoom ---");

    // Database connection one time
    QDir path( QCoreApplication::applicationDirPath() );
    dbName = path.filePath("qlandkarte.db");

    db = QSqlDatabase::addDatabase("QSQLITE","qlandkarte");
    db.setDatabaseName(dbName);
    db.open();

    QSqlQuery query(db);
    if(!query.exec("PRAGMA locking_mode=EXCLUSIVE")) {
      return;
    }

    if(!query.exec("PRAGMA temp_store=MEMORY")) {
      return;
    }

    if(!query.exec("PRAGMA default_cache_size=50")) {
      return;
    }

    if(!query.exec("PRAGMA page_size=8192")) {
      return;
    }

    dbMap = new CMapDB(this);
    connect(dbMap, SIGNAL(sigChanged()), this, SLOT(slotContentChanged()));

    dbWpt = new CWptDB(this);
    connect(dbWpt, SIGNAL(sigChanged()), this, SLOT(slotContentChanged()));

    dbTrack = new CTrackDB(this);
    connect(dbTrack, SIGNAL(sigChanged()), this, SLOT(slotContentChanged()));

    if(query.exec( "CREATE TABLE versioninfo ( version TEXT )")) {
        query.prepare( "INSERT INTO versioninfo (version) VALUES(:version)");
        query.bindValue(":version", DB_VERSION);
        if(!query.exec()) {
            qDebug() << query.lastError();
        }
    }
    else
    {
        query.prepare( "UPDATE versioninfo set version=:version");
        query.bindValue(":version", DB_VERSION);
        if(!query.exec()) {
            qDebug() << query.lastError();
        }
    }


    pushKey1->setText(tr("menu"));
    connect(pushKey1, SIGNAL(clicked()), this, SLOT(slotKey1()));
    pushKey2->setText(tr(""));
    pushKey2->setIcon(QIcon(":/icons/menu32x32"));
    connect(pushKey2, SIGNAL(clicked()), this, SLOT(slotKey2()));
    pushKey3->setText(tr(""));
    pushKey3->setIcon(QIcon(":/icons/zoom-32x32"));
    connect(pushKey3, SIGNAL(clicked()), this, SLOT(slotKey3()));
    pushKey4->setText(tr(""));
    pushKey4->setIcon(QIcon(":/icons/zoom+32x32"));
    connect(pushKey4, SIGNAL(clicked()), this, SLOT(slotKey4()));

    unit = new CUnitMetric(this);

    QSettings cfg;
    IGps::dev_e dev = (IGps::dev_e)cfg.value("gps/device",IGps::eSerial).toInt();
    IGps::src_e src = (IGps::src_e)cfg.value("gps/source",IGps::eNMEA).toInt();
#ifdef WINCE
    QString port    = cfg.value("gps/port","com0:").toString();
#else
    QString port    = cfg.value("gps/port","/dev/gps").toString();
#endif
    int rate        = cfg.value("gps/rate",9600).toInt();
    QString name    = cfg.value("gps/name","").toString();
    QString mac     = cfg.value("gps/mac","").toString();

    setGPS(src, port, rate, dev, name, mac);

    transferAgent = new CTransferAgent(this);
    connect( transferAgent, SIGNAL(syncStarted()), dlgTransfer, SLOT(show()) );
    connect( transferAgent, SIGNAL(syncStopped()), dlgTransfer, SLOT(hide()) );

    backlight = new CBacklight(this);
#ifndef WINCE
#ifdef QWS
    dlgEdit = new CDlgEdit(this);
#endif
#endif

    largeIcons = cfg.value("environment/largeIcons", largeIcons).toBool();

    qApp->installEventFilter(this);
}


CCanvas::~CCanvas()
{
    IMap& map = dbMap->getMap();
    qreal u = rect().center().x();
    qreal v = rect().center().y();
    map.convertPt2Rad(u,v);

    QSettings cfg;
    cfg.setValue("canvas/lon", u);
    cfg.setValue("canvas/lat", v);
    cfg.setValue("environment/largeIcons", largeIcons);
}

bool CCanvas::eventFilter(QObject *obj, QEvent *ev)
{
    if (ev->type() == QEvent::KeyPress){
        qDebug() << obj << this;
        if(obj == this){
            return IKeys::eventFilter(obj,ev);
        }
        if(obj->objectName() == "IDlgWptEdit"){
            QWidget * p = qobject_cast<QWidget*>(obj);
            if(p){
                return p->eventFilter(obj,ev);
            }
        }
        else if(obj->objectName() == "IDlgTrackEdit"){
            QWidget * p = qobject_cast<QWidget*>(obj);
            if(p){
                return p->eventFilter(obj,ev);
            }
        }
    }
    return QWidget::eventFilter(obj,ev);
}


CDlgTransfer& CCanvas::getDlgTransfer()
{
    return *dlgTransfer;
}

void CCanvas::slotContentChanged()
{
    needRedraw = true;
    update();
}

void CCanvas::lock(bool yes)
{
    qDebug() << "CCanvas::lock()" << yes;
    lockInterface = yes;
    pushKey1->setDisabled(yes);
    pushKey2->setDisabled(yes);
    pushKey3->setDisabled(yes);
    pushKey4->setDisabled(yes);
}


void CCanvas::resizeEvent(QResizeEvent * e)
{
    QSize s = e->size();
    dbMap->resize(s);
    innerArea.moveCenter(rect().center());

    if(!initialized) {
        QSettings cfg;
        IMap& map = dbMap->getMap();
        qreal u = cfg.value("canvas/lon", 0).toDouble();
        qreal v = cfg.value("canvas/lat", 0).toDouble();
        map.convertRad2Pt(u,v);

        dbMap->move(QPoint(u,v), innerArea.center());

        initialized = true;
    }

    buffer = QPixmap(s);

}


void CCanvas::paintEvent(QPaintEvent * e)
{
    if(lockInterface) return;

    QPainter p;

    p.begin(this);

    if(needRedraw){
        QPainter p;
        p.begin(&buffer);
        QRectF viewport = dbMap->getViewport();
        dbMap->draw(p, viewport);
        dbWpt->draw(p, viewport);
        dbTrack->draw(p, viewport);
        p.end();

        needRedraw = false;
    }

    p.drawPixmap(0,0,buffer);
    drawCursor(p);
    drawBacklightCtrl(p);
    p.end();

    QWidget::paintEvent(e);
}


void CCanvas::drawCursor(QPainter& p)
{
    IMap& map = dbMap->getMap();

    qreal u1 = gpsPos.x();
    qreal v1 = gpsPos.y();
    map.convertRad2Pt(u1, v1);
    p.drawPixmap(u1 - 15, v1 - 15, QPixmap(":/cursors/cursor2.png"));

    if(cursorMode == eUser) {
        qreal u = usrPos.x();
        qreal v = usrPos.y();
        map.convertRad2Pt(u, v);
        p.setPen(QPen(Qt::white, 5));
        p.drawLine(u1,v1,u,v);
        p.setPen(QPen(Qt::black, 3));
        p.drawLine(u1,v1,u,v);
        p.drawPixmap(u - 15, v - 15, QPixmap(":/cursors/cursor1.png"));

    }
}

void CCanvas::drawBacklightCtrl(QPainter& p)
{
    int total   = CBacklight::self().getTimeout();
    int expired = CBacklight::self().getTimer();

    if(total == -1){
        return;
    }

    qDebug() << CBacklight::self().getTimeout() << CBacklight::self().getTimer();

    QPoint p1 = pushKey1->rect().topLeft();
    QPoint p2 = infoScreen->rect().bottomLeft();

    p1 = pushKey1->mapToGlobal(p1);
    p2 = infoScreen->mapToGlobal(p2);

    p1 += QPoint(15,-15);
    p2 += QPoint(15, 30);

    QPoint p3 = p2;

    int h = (p1.y() - p2.y()) * expired / total;
    p2  = p1 + QPoint(0,-h);

    p.setPen(QPen(Qt::red,5));
    p.setBrush(Qt::red);
    p.drawLine(p1,p2);

    p.setPen(Qt::NoPen);
    p.drawEllipse(QPointF(p1),8.0,8.0);
    p.drawEllipse(QPointF(p3),8.0,8.0);
}


void CCanvas::slotKey1()
{
    if(lockInterface) return;
    CDlgMenu dlg(this);
    dlg.exec();
}


void CCanvas::slotKey2()
{
    if(lockInterface) return;

    CMenu menu(this);

    if(cursorMode == eUser){
        menu.addAction(1,tr("gps"), QIcon(":/icons/gps64x64"), this, SLOT(leaveUserMode()));
    }

    if (CTrackDB::self().getState() == CTrackDB::ePause) {
        menu.addAction(7,tr("pause"), QIcon(":/icons/pause32x32"), &CTrackDB::self(), SLOT(pause()));
        menu.addAction(8,tr("stop"), QIcon(":/icons/stop32x32"), &CTrackDB::self(), SLOT(stop()));
    }
    else if (CTrackDB::self().getState() == CTrackDB::eRecord) {
        menu.addAction(7,tr("pause"), QIcon(":/icons/pause32x32"), &CTrackDB::self(), SLOT(pause()));
        menu.addAction(8,tr("stop"), QIcon(":/icons/stop32x32"), &CTrackDB::self(), SLOT(stop()));
    }
    else {
        menu.addAction(7,tr("rec."), QIcon(":/icons/record32x32"), &CTrackDB::self(), SLOT(record()));
    }

    menu.addAction(5,tr("waypoint"), QIcon(":/icons/waypoint64x64"), &CWptDB::self(), SLOT(addWptToPos()));
    menu.addAction(4, tr("back"), QIcon(":/icons/back64x64"));

    int y = infoScreen->height();
    int x = (width() - menu.width()) >> 1;
    menu.exec(QPoint(x,y+5));

}


void CCanvas::slotKey3()
{
    if(lockInterface) return;

    if(cursorMode == eUser) {
        dbMap->zoom(false, usrPos);
    }
    else {
        dbMap->zoom(false);
    }
    labelZoom->setText(tr("zoom %1").arg(dbMap->getZoomLevel()));
}


void CCanvas::slotKey4()
{
    if(lockInterface) return;

    if(cursorMode == eUser) {
        dbMap->zoom(true, usrPos);
    }
    else {
        dbMap->zoom(true);
    }

    labelZoom->setText(tr("zoom %1").arg(dbMap->getZoomLevel()));
}


void CCanvas::slotNewGpsData(IGps& gps)
{
    qreal u = 0, v = 0;
    IGps::fix_e fix = gps.getPosition(u, v);

    if(fix != IGps::eNoFix) {
        const QDateTime datetime = gps.getDateTime().toLocalTime();
        labelDate->setText(datetime.date().toString("dd-MM-yy"));
        labelTime->setText(datetime.time().toString());
    }
    else {
        labelTime->setText("--:--:--");
        labelDate->setText("--------");
    }
    labelZoom->setText(tr("zoom %1").arg(dbMap->getZoomLevel()));

    gpsPos = QPointF(u,v);

    IMap& map = dbMap->getMap();
    map.convertRad2Pt(u,v);
    QPoint pos(u,v);

    if((fix != IGps::eNoFix) && (cursorMode == eLocked) && !innerArea.contains(pos)) {
        dbMap->move(pos, innerArea.center());
        usrPos = gpsPos;
    }

    if(largeIcons){
        switch(CTrackDB::self().getState()){
            case CTrackDB::eRecord:
                labelTrackStatus->setPixmap(QPixmap(":/icons/record32x32"));
                break;

            case CTrackDB::ePause:
                labelTrackStatus->setPixmap(QPixmap(":/icons/pause32x32"));
                break;

            case CTrackDB::eStop:
                labelTrackStatus->setPixmap(QPixmap(":/icons/stop32x32"));
                break;

        }
    }
    else{
        switch(CTrackDB::self().getState()){
            case CTrackDB::eRecord:
                labelTrackStatus->setPixmap(QPixmap(":/icons/record16x16"));
                break;

            case CTrackDB::ePause:
                labelTrackStatus->setPixmap(QPixmap(":/icons/pause16x16"));
                break;

            case CTrackDB::eStop:
                labelTrackStatus->setPixmap(QPixmap(":/icons/stop16x16"));
                break;

        }
    }
    update();
}


void CCanvas::setGPS(IGps::src_e src, const QString& port, int baudrate, IGps::dev_e dev, const QString& name, const QString& mac)
{

    if(gps) {
        delete gps; gps = 0;
    }

    if(src == IGps::eNoGps) {
        QProcess::execute("/etc/init.d/gps stop");
    }
    else if(dev == IGps::eBluetooth) {
        QProcess::execute(QString("/etc/init.d/gps bluetooth %1").arg(mac));
    }
    else if(dev == IGps::eSerial) {
        QProcess::execute("/etc/init.d/gps start");
    }
    else {

    }

    switch(src) {
        case IGps::eNMEA:
            gps = new CNMEA(port, baudrate, this);
            break;
        case IGps::eNMEAMtk:
            gps = new CNMEA_Mtk(port, baudrate, this);
            break;
#ifdef WINCE
        case IGps::eGpsd:
            gps = new CGpsd(port, baudrate, this);
            break;
#endif
        case IGps::eNoGps:
        default:
            gps = new IGps(this);
            break;
    }
    connect(gps, SIGNAL(sigNewData(IGps&)), this, SLOT(slotNewGpsData(IGps& )));

    QSettings cfg;
    cfg.setValue("gps/source",src);
    cfg.setValue("gps/port",port);
    cfg.setValue("gps/rate",baudrate);
    cfg.setValue("gps/device",dev);
    cfg.setValue("gps/name",name);
    cfg.setValue("gps/mac",mac);

    emit sigNewGps(gps);

    slotNewGpsData(*gps);
}


void CCanvas::enterUserMode()
{
    cursorMode = eUser;
    emit sigUsrPos(usrPos, cursorMode);
}


void CCanvas::leaveUserMode()
{
    cursorMode = eLocked;
    emit sigUsrPos(usrPos, cursorMode);
}


void CCanvas::setUsrPos(const QPoint& pos)
{
    IMap& map = dbMap->getMap();
    qreal u = pos.x();
    qreal v = pos.y();
    map.convertPt2Rad(u,v);

    usrPos  = QPointF(u,v);

    emit sigUsrPos(usrPos, cursorMode);

    update();
}

void CCanvas::setUsrPos(const qreal lon, const qreal lat)
{
    usrPos = QPointF(lon, lat);

    enterUserMode();

    qreal u = lon;
    qreal v = lat;
    IMap& map = dbMap->getMap();
    map.convertRad2Pt(u,v);
    dbMap->move(QPoint(u,v), innerArea.center());
}


void CCanvas::mousePressEvent(QMouseEvent * e)
{

    if(!lockInterface){
        moveMap = true;
        usrPosPixel = e->pos();
        enterUserMode();

        setUsrPos(e->pos());
    }
}


void CCanvas::mouseMoveEvent(QMouseEvent * e)
{
    if(moveMap && !lockInterface) {
        dbMap->move(usrPosPixel, e->pos());

        usrPosPixel = e->pos();
        setUsrPos(e->pos());
    }
}


void CCanvas::mouseReleaseEvent(QMouseEvent * e)
{
    if(!lockInterface){
        moveMap = false;

        usrPosPixel = e->pos();
        setUsrPos(e->pos());
        update();
    }
}

void CCanvas::drawText(const QString& str, QPainter& p, const QPoint& center, const QColor& color)
{
    QFont           f = p.font();
    QFontMetrics    fm(f);
    QRect           r = fm.boundingRect(str);

    r.moveCenter(center);

    p.setPen(Qt::white);
    p.setFont(f);

    p.drawText(r.topLeft() - QPoint(-1,-1), str);
    p.drawText(r.topLeft() - QPoint( 0,-1), str);
    p.drawText(r.topLeft() - QPoint(+1,-1), str);

    p.drawText(r.topLeft() - QPoint(-1, 0), str);
    p.drawText(r.topLeft() - QPoint(+1, 0), str);

    p.drawText(r.topLeft() - QPoint(-1,+1), str);
    p.drawText(r.topLeft() - QPoint( 0,+1), str);
    p.drawText(r.topLeft() - QPoint(+1,+1), str);

    p.setPen(color);
    p.drawText(r.topLeft(),str);

}

int CCanvas::getCurrentPos(qreal& x, qreal& y, qreal &ele)
{
    if(cursorMode == eUser) {
        x = usrPos.x();
        y = usrPos.y();
        ele = 0;
        return CCanvas::eUser;
    }
    else
    {
        IGps::self().getPosition(x, y);
        IGps::self().getElevation(ele);
        return CCanvas::eLocked;
    }
}


