/**
 ******************************************************************************
 *
 * @file       monitorwidget.cpp
 * @author     The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
 *             The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
 * @brief
 * @see        The GNU Public License (GPL) Version 3
 * @defgroup   telemetryplugin
 * @{
 *
 *****************************************************************************/
/*
 * 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 "monitorwidget.h"

#include <utils/stylehelper.h>

#include <QObject>
#include <QDebug>
#include <QtGui/QFont>

namespace {
/**
 * Create an SVG item and connect it to an element of the SVG file previously loaded into the parent item.
 * This then allows to show, hide, move, scale and rotate the element.
 * Opacity can also be changed.
 * Other characteristics (color, ...) of the element cannot be modified.
 */
// TODO move to some utility class that can be reused by other SVG manipulating code
QGraphicsSvgItem *createSvgItem(QGraphicsSvgItem *parent, QString elementId)
{
    QGraphicsSvgItem *item = new QGraphicsSvgItem(parent);

    QSvgRenderer *renderer = parent->renderer();

    // connect item to its corresponding element
    item->setSharedRenderer(renderer);
    item->setElementId(elementId);

    // move item to its location
    QMatrix elementMatrix = renderer->matrixForElement(elementId);
    QRectF elementRect    = elementMatrix.mapRect(renderer->boundsOnElement(elementId));
    item->setPos(elementRect.x(), elementRect.y());

    return item;
}

/**
 * Create a text item based on a svg rectangle.
 * The rectangle must be in the correct location (hint: use a "text" layer on top of the "background layer")
 * The font size will be set to match as well as possible the rectangle height but it is not guaranteed.
 *
 * It is possible to show the text rectangle to help understand layout issues.
 *
 */
// TODO move to some utility class that can be reused by other SVG manipulating code
QGraphicsTextItem *createTextItem(QGraphicsSvgItem *parent, QString elementId, QString fontName,
                                  bool showRect = false)
{
    if (showRect) {
        // create and display the text rectangle
        // needs to be done first otherwise the rectangle will blank out the text.
        createSvgItem(parent, elementId);
    }

    QGraphicsTextItem *item = new QGraphicsTextItem();

    QSvgRenderer *renderer  = parent->renderer();

    // move new text item to location of rectangle element
    QMatrix elementMatrix   = renderer->matrixForElement(elementId);
    QRectF elementRect = elementMatrix.mapRect(renderer->boundsOnElement(elementId));

    qreal fontPointSizeF    = elementRect.height();

    QTransform matrix;
    matrix.translate(elementRect.x(), elementRect.y() - (fontPointSizeF / 2.0));

    item->setParentItem(parent);
    item->setTransform(matrix, false);
    // to right align or center text we must provide a text width
    // item->setTextWidth(elementRect.width());

    // create font to match the rectangle height
    // there is not guaranteed that all fonts will play well...
    QFont font(fontName);
    // not sure if PreferMatch helps to get the correct font size (i.e. that fits the text rectangle nicely)
    font.setStyleStrategy(QFont::PreferMatch);
    font.setPointSizeF(fontPointSizeF);

    item->setFont(font);

#ifdef DEBUG_FONT
    // just in case
    qDebug() << "Font point size: " << fontPointSizeF;
    qDebug() << "Font pixel size: " << font.pixelSize();
    qDebug() << "Font point size: " << font.pointSize();
    qDebug() << "Font point size F: " << font.pointSizeF();
    qDebug() << "Font exact match: " << font.exactMatch();

    QFontInfo fontInfo(font);
    qDebug() << "Font info pixel size: " << fontInfo.pixelSize();
    qDebug() << "Font info point size: " << fontInfo.pointSize();
    qDebug() << "Font info point size F: " << fontInfo.pointSizeF();
    qDebug() << "Font info exact match: " << fontInfo.exactMatch();
#endif
    return item;
}
} // anonymous namespace

MonitorWidget::MonitorWidget(QWidget *parent) :
    QGraphicsView(parent), aspectRatioMode(Qt::KeepAspectRatio)
{
    setMinimumSize(195, 25);

    QGraphicsScene *scene = new QGraphicsScene();

    setScene(scene);

    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);

    // no scroll bars
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    setStyleSheet("QGraphicsView{background:transparent;}");

    setBackgroundBrush(QBrush(Utils::StyleHelper::baseColor()));

    setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

    QSvgRenderer *renderer = new QSvgRenderer();

    if (renderer->load(QString(":/telemetry/images/tx-rx.svg"))) {
        // create graph
        graph = new QGraphicsSvgItem();
        graph->setSharedRenderer(renderer);
        graph->setElementId("background");

        graph->setFlags(QGraphicsItem::ItemClipsChildrenToShape | QGraphicsItem::ItemClipsToShape);

        scene->addItem(graph);

        int i;

        // create tx nodes
        i = 0;
        while (true) {
            QString id   = QString("tx%0").arg(i);
            QString bgId = QString("tx_bg%0").arg(i);
            if (!renderer->elementExists(id) || !renderer->elementExists(bgId)) {
                break;
            }
            QGraphicsSvgItem *item = createSvgItem(graph, bgId);
            item->setElementId(id);
            txNodes.append(item);
            i++;
        }

        // create rx nodes
        i = 0;
        while (true) {
            QString id   = QString("rx%0").arg(i);
            QString bgId = QString("rx_bg%0").arg(i);
            if (!renderer->elementExists(id) || !renderer->elementExists(bgId)) {
                break;
            }
            QGraphicsSvgItem *item = createSvgItem(graph, bgId);
            item->setElementId(id);
            rxNodes.append(item);
            i++;
        }

        if (renderer->elementExists("txSpeed")) {
            txSpeed = createTextItem(graph, "txSpeed", "Helvetica");
            txSpeed->setDefaultTextColor(Qt::white);
        } else {
            txSpeed = NULL;
        }

        if (renderer->elementExists("rxSpeed")) {
            rxSpeed = createTextItem(graph, "rxSpeed", "Helvetica");
            rxSpeed->setDefaultTextColor(Qt::white);
        } else {
            rxSpeed = NULL;
        }
        // scene->setSceneRect(graph->boundingRect());
    }

    connected = false;

    setMin(0.0);
    setMax(1200.0);

    telemetryUpdated(0.0, 0.0);
}

MonitorWidget::~MonitorWidget()
{
    while (!txNodes.isEmpty()) {
        delete txNodes.takeFirst();
    }
    while (!rxNodes.isEmpty()) {
        delete rxNodes.takeFirst();
    }
    if (txSpeed) {
        delete txSpeed;
    }
    if (rxSpeed) {
        delete rxSpeed;
    }
}

void MonitorWidget::telemetryConnected()
{
    qDebug() << "MonitorWidget::telemetryConnected";
    if (!connected) {
        // flash the lights
        setToolTip(tr("Connected"));
        telemetryUpdated(maxValue, maxValue);
        connected = true;
    }
}

void MonitorWidget::telemetryDisconnected()
{
    qDebug() << "MonitorWidget::telemetryDisconnected";
    if (connected) {
        connected = false;

        setToolTip(tr("Disconnected"));

        // flash the lights???
        telemetryUpdated(maxValue, maxValue);

        telemetryUpdated(0.0, 0.0);
    }
}

/*!
   \brief Called by the UAVObject which got updated

   Updates the numeric value and/or the icon if the dial wants this.
 */
void MonitorWidget::telemetryUpdated(double txRate, double rxRate)
{
    double txIndex = (txRate - minValue) / (maxValue - minValue) * txNodes.count();
    double rxIndex = (rxRate - minValue) / (maxValue - minValue) * rxNodes.count();

    if (connected) {
        this->setToolTip(QString("Tx: %0 bytes/s, Rx: %1 bytes/s").arg(txRate).arg(rxRate));
    }

    for (int i = 0; i < txNodes.count(); i++) {
        QGraphicsItem *node = txNodes.at(i);
        bool visible = ( /*connected &&*/ (i < txIndex));
        if (visible != node->isVisible()) {
            node->setVisible(visible);
            node->update();
        }
    }

    for (int i = 0; i < rxNodes.count(); i++) {
        QGraphicsItem *node = rxNodes.at(i);
        bool visible = ( /*connected &&*/ (i < rxIndex));
        if (visible != node->isVisible()) {
            node->setVisible(visible);
            node->update();
        }
    }

    if (txSpeed) {
        if (connected) {
            txSpeed->setPlainText(QString("%0").arg(txRate));
        }
        txSpeed->setVisible(connected);
        txSpeed->update();
    }

    if (rxSpeed) {
        if (connected) {
            rxSpeed->setPlainText(QString("%0").arg(rxRate));
        }
        rxSpeed->setVisible(connected);
        rxSpeed->update();
    }

    update();
}

void MonitorWidget::showEvent(QShowEvent *event)
{
    Q_UNUSED(event);

    fitInView(graph, aspectRatioMode);
}

void MonitorWidget::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);

    fitInView(graph, aspectRatioMode);
}
