/*****************************************************************************
 *
 * Testmanager - Graphical Automation and Visualisation Tool
 *
 * Copyright (C) 2019-2023  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 "TableViewPlugin.h"

#include "TableViewPluginPrivate.h"
#include "TableViewPluginDialog.h"

#include "DataSlot.h"
#include "DataModel.h"
#include "DataNode.h"
#include "SlotModel.h"

#include <QtPdWidgets2/TableView.h>

#include <QDebug>
#include <QJsonObject>
#include <QJsonArray>

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

QString TableViewPlugin::name() const
{
    return tr("Table View");
}

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

QIcon TableViewPlugin::icon() const
{
    return QIcon(":/images/plugin-table.svg");
}

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

void *TableViewPlugin::createPrivateData(QWidget *widget) const
{
    Pd::TableView *table = dynamic_cast<Pd::TableView *>(widget);
    if (!table) {
        qWarning() << __func__ << "failed to cast";
        return NULL;
    }

    auto priv = new TableViewPluginPrivate();
    priv->model = new QtPdCom::TableModel();
    table->setModel(priv->model);
    return priv;
}

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

void TableViewPlugin::readPrivateData(
        const QJsonObject &json,
        QWidget *widget,
        void *data) const
{
    auto table(dynamic_cast<Pd::TableView *>(widget));
    if (!table) {
        qWarning() << __func__ << "failed to cast";
        return;
    }

    auto priv(static_cast<TableViewPluginPrivate *>(data));

    priv->clearColumns();

    foreach (const QJsonValue &value, json["columns"].toArray()) {
        QJsonObject obj(value.toObject());
        auto col(new QtPdCom::TableColumn());
        col->setHeader(obj["header"].toString());
        int num (obj["decimals"].toInt());
        if (num < 0) {
            qWarning() << "Invalid value for decimals. Setting to zero.";
            num = 0;
        }
        col->setDecimals(num);
        priv->columns.append(col);
        priv->model->addColumn(col);
    }
}

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

QJsonObject
TableViewPlugin::writePrivateData(QWidget *, const void *data) const
{
    auto priv(static_cast<const TableViewPluginPrivate *>(data));

    QJsonArray columnsArray;

    for (auto col : priv->columns) {
        QJsonObject obj;
        obj["header"] = col->getHeader();
        obj["decimals"] = (int) col->getDecimals();
        columnsArray.append(obj);
    }

    QJsonObject ret;
    ret["columns"] = columnsArray;

    return ret;
}

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

void TableViewPlugin::deletePrivateData(QWidget *widget, void *data) const
{
    Pd::TableView *table = dynamic_cast<Pd::TableView *>(widget);
    if (table) {
        table->setModel(nullptr);
    }

    auto priv(static_cast<TableViewPluginPrivate *>(data));
    delete priv;
}

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

void TableViewPlugin::openEditor(QWidget *widget, void *data, QWidget *parent)
{
    auto table(dynamic_cast<Pd::TableView *>(widget));
    if (!table) {
        return;
    }
    auto priv(static_cast<TableViewPluginPrivate *>(data));

    TableViewPluginDialog dialog(priv, parent);
    dialog.exec();
}

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

int TableViewPlugin::getSlotCapacity() const
{
    return -1;  // unlimited
}

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

int TableViewPlugin::getFreeSlots(const SlotModel &slotModel) const
{
    Q_UNUSED(slotModel);
    return -1;  // unlimited
}

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

void TableViewPlugin::appendVariable(
        QWidget *widget,
        void *data,
        SlotModel *slotModel,
        DataNode *dataNode) const
{
    auto priv(static_cast<TableViewPluginPrivate *>(data));

    DataSlot *slot = new DataSlot(slotModel);
    slot->url = dataNode->nodeUrl();

    if (dataNode->getPeriod().count() > 0.0) {
        slot->period = std::chrono::milliseconds(200);
    }

    auto pv(dataNode->getVariable());
    if (pv.empty()) {
        return;
    }

    auto col {new QtPdCom::TableColumn {QString(), widget}};
    col->setVariable(pv, slot->period, slot->scale, slot->offset);
    priv->columns.append(col);
    priv->model->addColumn(col);

    dataNode->notifySubscribed(slot->period);
}

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

void TableViewPlugin::connectDataSlots(
        QWidget *widget,
        void *data,
        const SlotModel *slotModel,
        const DataModel *dataModel) const
{
    Q_UNUSED(widget);

    auto priv(static_cast<TableViewPluginPrivate *>(data));

    if (!slotModel->getChildCount()) {
        return;
    }

    for (int i = 0; i < slotModel->getChildCount(); i++) {
        auto slot(dynamic_cast<DataSlot *>(slotModel->getChildNode(i)));
        if (!slot) {
            continue;
        }

        if (priv->columns.size() <= i) {
            auto *col = new QtPdCom::TableColumn();
            priv->columns.append(col);
            priv->model->addColumn(col);
        }

        auto dataNode(dataModel->findDataNode(slot->url));
        if (dataNode) {
            auto pv(dataNode->getVariable());
            if (!pv.empty()) {
                priv->columns[i]->setVariable(
                        pv,
                        slot->period,
                        slot->scale,
                        slot->offset);
                dataNode->notifySubscribed(slot->period);
            }
        }
        else {
            qWarning() << tr("Url not found: %1").arg(slot->url.toString());
        }
    }
}

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

void TableViewPlugin::clearVariables(QWidget *widget, void *data) const
{
    Q_UNUSED(widget);
    auto priv(static_cast<TableViewPluginPrivate *>(data));
    for (auto *col : priv->columns) {
        col->clearVariable();
    }
}

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

QWidget *TableViewPlugin::createWidget(QWidget *parent) const
{
    return new Pd::TableView(parent);
}

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