/**
 ******************************************************************************
 *
 * @file       modelmapproxy.cpp
 * @author     The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
 * @addtogroup GCSPlugins GCS Plugins
 * @{
 * @addtogroup OPMapPlugin OpenPilot Map Plugin
 * @{
 * @brief The OpenPilot Map plugin
 *****************************************************************************/
/*
 * 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 3 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.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "modelmapproxy.h"

modelMapProxy::modelMapProxy(QObject *parent, OPMapWidget *map, flightDataModel *model, QItemSelectionModel *selectionModel) : QObject(parent), myMap(map), model(model), selection(selectionModel)
{
    connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(rowsInserted(const QModelIndex &, int, int)));
    connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(rowsRemoved(const QModelIndex &, int, int)));
    connect(selection, SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this, SLOT(currentRowChanged(QModelIndex, QModelIndex)));
    connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(dataChanged(QModelIndex, QModelIndex)));
    connect(myMap, SIGNAL(selectedWPChanged(QList<WayPointItem *>)), this, SLOT(selectedWPChanged(QList<WayPointItem *>)));
    connect(myMap, SIGNAL(WPValuesChanged(WayPointItem *)), this, SLOT(WPValuesChanged(WayPointItem *)));
}

void modelMapProxy::WPValuesChanged(WayPointItem *wp)
{
    QModelIndex index;

    index = model->index(wp->Number(), flightDataModel::LATPOSITION);
    if (!index.isValid()) {
        return;
    }
    model->setData(index, wp->Coord().Lat(), Qt::EditRole);
    index = model->index(wp->Number(), flightDataModel::LNGPOSITION);
    model->setData(index, wp->Coord().Lng(), Qt::EditRole);

    index = model->index(wp->Number(), flightDataModel::ALTITUDE);
    model->setData(index, wp->Altitude(), Qt::EditRole);

    index = model->index(wp->Number(), flightDataModel::DISRELATIVE);
    model->setData(index, wp->getRelativeCoord().distance, Qt::EditRole);
    index = model->index(wp->Number(), flightDataModel::BEARELATIVE);
    model->setData(index, wp->getRelativeCoord().bearingToDegrees(), Qt::EditRole);
    index = model->index(wp->Number(), flightDataModel::ALTITUDERELATIVE);
    model->setData(index, wp->getRelativeCoord().altitudeRelative, Qt::EditRole);
}

void modelMapProxy::currentRowChanged(QModelIndex current, QModelIndex previous)
{
    Q_UNUSED(previous);

    QList<WayPointItem *> list;
    WayPointItem *wp = findWayPointNumber(current.row());
    if (!wp) {
        return;
    }
    list.append(wp);
    myMap->setSelectedWP(list);
}

void modelMapProxy::selectedWPChanged(QList<WayPointItem *> list)
{
    selection->clearSelection();
    foreach(WayPointItem * wp, list) {
        QModelIndex index = model->index(wp->Number(), 0);

        selection->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
    }
}

modelMapProxy::overlayType modelMapProxy::overlayTranslate(int type)
{
    switch (type) {
    case MapDataDelegate::MODE_GOTOENDPOINT:
    case MapDataDelegate::MODE_FOLLOWVECTOR:
    case MapDataDelegate::MODE_VELOCITY:
    case MapDataDelegate::MODE_LAND:
    case MapDataDelegate::MODE_AUTOTAKEOFF:
    case MapDataDelegate::MODE_BRAKE:
        return OVERLAY_LINE;

    case MapDataDelegate::MODE_CIRCLERIGHT:
        return OVERLAY_CIRCLE_RIGHT;

    case MapDataDelegate::MODE_CIRCLELEFT:
    default:
        return OVERLAY_CIRCLE_LEFT;
    }
}

void modelMapProxy::createOverlay(WayPointItem *from, WayPointItem *to, modelMapProxy::overlayType type, QColor color, bool dashed, int width)
{
    if (from == NULL || to == NULL || from == to) {
        return;
    }
    switch (type) {
    case OVERLAY_LINE:
        myMap->WPLineCreate(from, to, color, dashed, width);
        break;
    case OVERLAY_CIRCLE_RIGHT:
        myMap->WPCircleCreate(to, from, true, color, dashed, width);
        break;
    case OVERLAY_CIRCLE_LEFT:
        myMap->WPCircleCreate(to, from, false, color, dashed, width);
        break;
    default:
        break;
    }
}
void modelMapProxy::createOverlay(WayPointItem *from, HomeItem *to, modelMapProxy::overlayType type, QColor color, bool dashed, int width)
{
    if (from == NULL || to == NULL) {
        return;
    }
    switch (type) {
    case OVERLAY_LINE:
        myMap->WPLineCreate(to, from, color, dashed, width);
        break;
    case OVERLAY_CIRCLE_RIGHT:
        myMap->WPCircleCreate(to, from, true, color, dashed, width);
        break;
    case OVERLAY_CIRCLE_LEFT:
        myMap->WPCircleCreate(to, from, false, color, dashed, width);
        break;
    default:
        break;
    }
}
void modelMapProxy::refreshOverlays()
{
    myMap->deleteAllOverlays();
    if (model->rowCount() < 1) {
        return;
    }
    WayPointItem *wp_current = NULL;
    WayPointItem *wp_next    = NULL;
    int wp_jump;
    int wp_error;
    overlayType wp_next_overlay;
    overlayType wp_jump_overlay;
    overlayType wp_error_overlay;
    wp_current = findWayPointNumber(0);
    overlayType wp_current_overlay = overlayTranslate(model->data(model->index(0, flightDataModel::MODE)).toInt());
    createOverlay(wp_current, myMap->Home, wp_current_overlay, Qt::green);
    for (int x = 0; x < model->rowCount(); ++x) {
        wp_current = findWayPointNumber(x);
        wp_jump    = model->data(model->index(x, flightDataModel::JUMPDESTINATION)).toInt() - 1;
        wp_error   = model->data(model->index(x, flightDataModel::ERRORDESTINATION)).toInt() - 1;
        wp_next_overlay  = overlayTranslate(model->data(model->index(x + 1, flightDataModel::MODE)).toInt());
        wp_jump_overlay  = overlayTranslate(model->data(model->index(wp_jump, flightDataModel::MODE)).toInt());
        wp_error_overlay = overlayTranslate(model->data(model->index(wp_error, flightDataModel::MODE)).toInt());
        createOverlay(wp_current, findWayPointNumber(wp_error), wp_error_overlay, Qt::red, true, 1);
        switch (model->data(model->index(x, flightDataModel::COMMAND)).toInt()) {
        case MapDataDelegate::COMMAND_ONCONDITIONNEXTWAYPOINT:
            wp_next = findWayPointNumber(x + 1);
            createOverlay(wp_current, wp_next, wp_next_overlay, Qt::green);
            break;
        case MapDataDelegate::COMMAND_ONCONDITIONJUMPWAYPOINT:
            wp_next = findWayPointNumber(wp_jump);
            createOverlay(wp_current, wp_next, wp_jump_overlay, Qt::green);
            break;
        case MapDataDelegate::COMMAND_ONNOTCONDITIONJUMPWAYPOINT:
            wp_next = findWayPointNumber(wp_jump);
            createOverlay(wp_current, wp_next, wp_jump_overlay, Qt::yellow);
            break;
        case MapDataDelegate::COMMAND_ONNOTCONDITIONNEXTWAYPOINT:
            wp_next = findWayPointNumber(x + 1);
            createOverlay(wp_current, wp_next, wp_next_overlay, Qt::yellow);
            break;
        case MapDataDelegate::COMMAND_IFCONDITIONJUMPWAYPOINTELSENEXTWAYPOINT:
            wp_next = findWayPointNumber(wp_jump);
            createOverlay(wp_current, wp_next, wp_jump_overlay, Qt::green);
            wp_next = findWayPointNumber(x + 1);
            createOverlay(wp_current, wp_next, wp_next_overlay, Qt::green);
            break;
        }
    }
}

WayPointItem *modelMapProxy::findWayPointNumber(int number)
{
    if (number < 0) {
        return NULL;
    }
    return myMap->WPFind(number);
}

void modelMapProxy::rowsRemoved(const QModelIndex &parent, int first, int last)
{
    Q_UNUSED(parent);

    for (int x = last; x > first - 1; x--) {
        myMap->WPDelete(x);
    }
    refreshOverlays();
}

void modelMapProxy::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
    Q_UNUSED(bottomRight);

    WayPointItem *item = findWayPointNumber(topLeft.row());
    if (!item) {
        return;
    }
    internals::PointLatLng latlng;
    int x = topLeft.row();
    distBearingAltitude distBearing;
    double altitude;
    bool relative;
    QModelIndex index;
    QString desc;
    switch (topLeft.column()) {
    case flightDataModel::COMMAND:
    case flightDataModel::CONDITION:
    case flightDataModel::JUMPDESTINATION:
    case flightDataModel::ERRORDESTINATION:
    case flightDataModel::MODE:
        refreshOverlays();
        break;
    case flightDataModel::WPDESCRITPTION:
        index = model->index(x, flightDataModel::WPDESCRITPTION);
        desc  = index.data(Qt::DisplayRole).toString();
        item->SetDescription(desc);
        break;
    case flightDataModel::LATPOSITION:
        latlng = item->Coord();
        index  = model->index(x, flightDataModel::LATPOSITION);
        latlng.SetLat(index.data(Qt::DisplayRole).toDouble());
        item->SetCoord(latlng);
        break;
    case flightDataModel::LNGPOSITION:
        latlng = item->Coord();
        index  = model->index(x, flightDataModel::LNGPOSITION);
        latlng.SetLng(index.data(Qt::DisplayRole).toDouble());
        item->SetCoord(latlng);
        break;
    case flightDataModel::BEARELATIVE:
        distBearing = item->getRelativeCoord();
        index = model->index(x, flightDataModel::BEARELATIVE);
        distBearing.setBearingFromDegrees(index.data(Qt::DisplayRole).toDouble());
        item->setRelativeCoord(distBearing);
        break;
    case flightDataModel::DISRELATIVE:
        distBearing = item->getRelativeCoord();
        index = model->index(x, flightDataModel::DISRELATIVE);
        distBearing.distance = index.data(Qt::DisplayRole).toDouble();
        item->setRelativeCoord(distBearing);
        break;
    case flightDataModel::ALTITUDERELATIVE:
        distBearing = item->getRelativeCoord();
        index = model->index(x, flightDataModel::ALTITUDERELATIVE);
        distBearing.altitudeRelative = index.data(Qt::DisplayRole).toFloat();
        item->setRelativeCoord(distBearing);
        break;
    case flightDataModel::ISRELATIVE:
        index    = model->index(x, flightDataModel::ISRELATIVE);
        relative = index.data(Qt::DisplayRole).toBool();
        if (relative) {
            item->setWPType(mapcontrol::WayPointItem::relative);
        } else {
            item->setWPType(mapcontrol::WayPointItem::absolute);
        }
        break;
    case flightDataModel::ALTITUDE:
        index    = model->index(x, flightDataModel::ALTITUDE);
        altitude = index.data(Qt::DisplayRole).toDouble();
        item->SetAltitude(altitude);
        break;
    case flightDataModel::LOCKED:
        index = model->index(x, flightDataModel::LOCKED);
        item->setFlag(QGraphicsItem::ItemIsMovable, !index.data(Qt::DisplayRole).toBool());
        break;
    }
}

void modelMapProxy::rowsInserted(const QModelIndex &parent, int first, int last)
{
    Q_UNUSED(parent);

    for (int x = first; x < last + 1; x++) {
        QModelIndex index;
        internals::PointLatLng latlng;
        distBearingAltitude distBearing;
        double altitude;
        bool relative;
        index    = model->index(x, flightDataModel::WPDESCRITPTION);
        QString desc = index.data(Qt::DisplayRole).toString();
        index    = model->index(x, flightDataModel::LATPOSITION);
        latlng.SetLat(index.data(Qt::DisplayRole).toDouble());
        index    = model->index(x, flightDataModel::LNGPOSITION);
        latlng.SetLng(index.data(Qt::DisplayRole).toDouble());
        index    = model->index(x, flightDataModel::DISRELATIVE);
        distBearing.distance = index.data(Qt::DisplayRole).toDouble();
        index    = model->index(x, flightDataModel::BEARELATIVE);
        distBearing.setBearingFromDegrees(index.data(Qt::DisplayRole).toDouble());
        index    = model->index(x, flightDataModel::ALTITUDERELATIVE);
        distBearing.altitudeRelative = index.data(Qt::DisplayRole).toFloat();
        index    = model->index(x, flightDataModel::ISRELATIVE);
        relative = index.data(Qt::DisplayRole).toBool();
        index    = model->index(x, flightDataModel::ALTITUDE);
        altitude = index.data(Qt::DisplayRole).toDouble();
        if (relative) {
            myMap->WPInsert(distBearing, desc, x);
        } else {
            myMap->WPInsert(latlng, altitude, desc, x);
        }
    }

    refreshOverlays();
}
void modelMapProxy::deleteWayPoint(int number)
{
    model->removeRow(number, QModelIndex());
}

void modelMapProxy::createWayPoint(internals::PointLatLng coord)
{
    model->insertRow(model->rowCount(), QModelIndex());
    QModelIndex index = model->index(model->rowCount() - 1, flightDataModel::LATPOSITION, QModelIndex());
    model->setData(index, coord.Lat(), Qt::EditRole);
    index = model->index(model->rowCount() - 1, flightDataModel::LNGPOSITION, QModelIndex());
    model->setData(index, coord.Lng(), Qt::EditRole);
    index = model->index(model->rowCount() - 1, flightDataModel::JUMPDESTINATION, QModelIndex());
    model->setData(index, 1, Qt::EditRole);
    index = model->index(model->rowCount() - 1, flightDataModel::ERRORDESTINATION, QModelIndex());
    model->setData(index, 1, Qt::EditRole);
}

void modelMapProxy::deleteAll()
{
    if (model->rowCount() > 0) {
        model->removeRows(0, model->rowCount(), QModelIndex());
    }
}
