/*****************************************************************************
 *
 * Testmanager - Graphical Automation and Visualisation Tool
 *
 * Copyright (C) 2019  Florian Pose <fp@igh.de>
 *
 * This file is part of Testmanager.
 *
 * Testmanager 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.
 *
 * Testmanager 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 Testmanager. If not, see <http://www.gnu.org/licenses/>.
 *
 ****************************************************************************/

#include "TooltipMatrix.h"

#include <pdcom5/Variable.h>
#include <pdcom5/Exception.h>
#include <pdcom5/Subscriber.h>
#include <pdcom5/Subscription.h>

#include <QPainter>
#include <QResizeEvent>
#include <QDebug>

struct TooltipMatrix::Subscription : PdCom::Subscriber
{
    TooltipMatrix *parent_;
    PdCom::Subscription subscription_;

    Subscription(TooltipMatrix& parent, PdCom::Transmission t, PdCom::Variable const& var)
        : PdCom::Subscriber(t), parent_(&parent), subscription_(*this, var)
    {}

    void stateChanged(PdCom::Subscription const &subscription) override
    {
        if (subscription.getState() == PdCom::Subscription::State::Invalid)
        {
            qWarning() << tr("Variable subscription failed");
            parent_->rowCount = 0;
            parent_->columnCount = 0;
            subscription_ = {};
        } else if (subscription.getState() == PdCom::Subscription::State::Active
                    and getTransmission() == PdCom::event_mode)
        {
            try {
                subscription_.poll();
            }
            catch (PdCom::Exception &e) {
                QString msg(
                        tr("Variable polling"
                            " failed: %1").arg(e.what()));
                qWarning() << msg;
                parent_->rowCount = 0;
                parent_->columnCount = 0;
            }
        }
    }
    void newValues(std::chrono::nanoseconds) override
    {
        parent_->dataPresent = true;
        parent_->update();
    }
};

/****************************************************************************/

TooltipMatrix::TooltipMatrix(
        QWidget *parent
        ):
    QFrame(parent),
    variable(),
    dataPresent(false),
    rowCount(0),
    columnCount(0),
    fieldSize(20, 20)
{
}

/****************************************************************************/

TooltipMatrix::~TooltipMatrix()
{
    clearVariable();
}

/****************************************************************************/

void TooltipMatrix::setVariable(PdCom::Variable const& var, double period)
{
    clearVariable();
    variable = var;

    if (var.empty()) {
        return;
    }

    const auto dim = variable.getSizeInfo();
    if (dim.size() < 1 or dim.size() > 2) {
        qWarning() << "invalid dimension size " << dim.size();
        return;
    }

    if (dim.size() == 1) {
        rowCount = 1;
        columnCount = dim[0];
    }
    else { // 2
        rowCount = dim[0];
        columnCount = dim[1];
    }

    try {
        subscription.reset(new Subscription(*this, PdCom::Transmission::fromDouble(period), var));
    }
    catch (PdCom::Exception &e) {
        QString msg(
                tr("Variable subscription"
                    " failed: %1").arg(e.what()));
        qWarning() << msg;
        rowCount = 0;
        columnCount = 0;
        return;
    }

    if (period == 0.0) {

    }

    setMinimumSize(columnCount * fieldSize.width(),
            rowCount * fieldSize.height());
}

/****************************************************************************/

void TooltipMatrix::clearVariable()
{
    if (!variable.empty()) {
        subscription.reset();
        variable = {};
        dataPresent = false;
        rowCount = 0;
        columnCount = 0;
        fieldSize = QSize(20, 20);
        setMinimumSize(0, 0);
        update();
    }
}

/****************************************************************************/

void TooltipMatrix::resizeEvent(QResizeEvent *)
{
}

/****************************************************************************/

void TooltipMatrix::paintEvent(QPaintEvent *event)
{
    QFrame::paintEvent(event);

    if (rowCount <= 0 or columnCount <= 0) {
        return;
    }

    QPainter painter(this);

    int rowHeight = contentsRect().height() / rowCount;
    int colWidth = contentsRect().width() / columnCount;

    QLocale loc;
    QRect bound;
    int maxWidth = 0;
    int maxHeight = 0;

    for (int row = 0; row < rowCount; row++) {
        for (int col = 0; col < columnCount; col++) {
            QRect rect(contentsRect().left() + colWidth * col,
                    contentsRect().top() + rowHeight * row,
                    colWidth, rowHeight);
            rect.adjust(1, 1, -1, -1);
            painter.fillRect(rect, QPalette().base());

            if (dataPresent) {
                double value;
                subscription->subscription_.getValue(value, row * columnCount + col);
                rect.adjust(1, 1, -1, -1);
                QString text(loc.toString(value, 'g', 3));
                painter.drawText(rect, Qt::AlignRight | Qt::AlignVCenter,
                        text, &bound);
                if (bound.width() > maxWidth) {
                    maxWidth = bound.width();
                }
                if (bound.height() > maxHeight) {
                    maxHeight = bound.height();
                }
            }
        }
    }

    maxWidth += 4;
    maxHeight += 4;
    if (maxWidth > fieldSize.width()) {
        fieldSize.setWidth(maxWidth);
        setMinimumSize(columnCount * fieldSize.width(),
                rowCount * fieldSize.height());
    }
    if (maxHeight > fieldSize.height()) {
        fieldSize.setHeight(maxHeight);
        setMinimumSize(columnCount * fieldSize.width(),
                rowCount * fieldSize.height());
    }
}
