/*
    SPDX-FileCopyrightText: 2003-2008 Cies Breijs <cies AT kde DOT nl>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "inspector.h"

#include "interpreter/translator.h" // for getting the translated ArgumentSeparator

#include <QDebug>
#include <QFontDatabase>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QTabWidget>
#include <QTableWidget>
#include <QTreeWidget>

#include <KLocalizedString>

Inspector::Inspector(QWidget *parent)
    : QWidget(parent)
{
    mainLayout = new QHBoxLayout(this);
    mainLayout->setSpacing(0);
    mainLayout->setContentsMargins(0, 0, 0, 0);
    tabWidget = new QTabWidget(this);
    tabWidget->setDocumentMode(true);

    variableTab = new QWidget();
    variableLayout = new QHBoxLayout(variableTab);
    variableTable = new QTableWidget(variableTab);
    variableTable->setAlternatingRowColors(true);
    variableLayout->addWidget(variableTable);
    tabWidget->addTab(variableTab, i18n("Variables"));

    functionTab = new QWidget();
    functionLayout = new QHBoxLayout(functionTab);
    functionTable = new QTableWidget(functionTab);
    functionTable->setAlternatingRowColors(true);
    functionLayout->addWidget(functionTable);
    tabWidget->addTab(functionTab, i18n("Functions"));

    treeTab = new QWidget();
    treeLayout = new QHBoxLayout(treeTab);
    treeView = new QTreeWidget(treeTab);
    treeView->header()->setVisible(false);
    treeLayout->addWidget(treeView);
    tabWidget->addTab(treeTab, i18n("Tree"));

    mainLayout->addWidget(tabWidget);

    // our syntax highlighter (this does not do any markings)
    highlighter = new Highlighter();

    // 	// the maps used when marking table/tree items later
    // 	variableMap = new QHash<QString, QTableWidgetItem*>();
    // 	functionMap = new QHash<QString, QTableWidgetItem*>();
    // 	treeMap = new QHash<TreeNode*, QTableWidgetItem*>();

    currentlyMarkedTreeItem = nullptr;

    disable();

    clear();
}

Inspector::~Inspector()
{
    delete highlighter;
}

void Inspector::disable()
{
    variableTable->setEnabled(false);
    functionTable->setEnabled(false);
    treeView->setEnabled(false);
    // enabling happened when the inspector gets filled with data
}

void Inspector::clear()
{
    clearAllMarks();

    // Question: is the code duplication below enough
    // for a subclass-of-QTableWidget based approach?

    variableMap.clear();
    variableTableEmpty = true;
    variableTable->clear();
    QStringList headers;
    headers << i18n("name") << i18n("value") << i18n("type");
    variableTable->setColumnCount(3);
    variableTable->setHorizontalHeaderLabels(headers);
    variableTable->setSelectionMode(QAbstractItemView::SingleSelection);
    variableTable->setSelectionBehavior(QAbstractItemView::SelectRows);
    variableTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
    variableTable->verticalHeader()->setVisible(false);
    variableTable->setShowGrid(false);
    variableTable->setRowCount(1);
    QTableWidgetItem *emptyItem;
    emptyItem = new QTableWidgetItem(i18n("No variables"));
    variableTable->setItem(0, 0, emptyItem);
    variableTable->resizeColumnsToContents();

    functionMap.clear();
    functionTableEmpty = true;
    functionTable->clear();
    headers.clear();
    headers << i18n("name") << i18n("parameters");
    functionTable->setColumnCount(2);
    functionTable->setHorizontalHeaderLabels(headers);
    functionTable->setSelectionMode(QAbstractItemView::SingleSelection);
    functionTable->setSelectionBehavior(QAbstractItemView::SelectRows);
    functionTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
    functionTable->verticalHeader()->setVisible(false);
    functionTable->setShowGrid(false);
    functionTable->setRowCount(1);
    emptyItem = new QTableWidgetItem(i18n("No learned functions"));
    functionTable->setItem(0, 0, emptyItem);
    functionTable->resizeColumnsToContents();

    // Treeview gets cleared in updateTree()
    disable();
}

void Inspector::updateVariable(const QString &name, const Value &value)
{
    // Check if the variable has already been added to the table
    int row = findVariable(name);
    if (row == -1) {
        // We are dealing with a new variable
        if (variableTableEmpty) { // Check whether we have to add a new row
            variableTableEmpty = false;
        } else {
            variableTable->insertRow(0);
        }
        row = 0;
    }

    QTableWidgetItem *nameItem;
    nameItem = new QTableWidgetItem(name);
    nameItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    QTextCharFormat *format = highlighter->formatForStatement(name);
    nameItem->setFont(format->font());
    nameItem->setForeground(format->foreground());
    variableTable->setItem(row, 0, nameItem);
    variableMap[name] = nameItem;

    QTableWidgetItem *valueItem;
    valueItem = new QTableWidgetItem(value.string());
    valueItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    // TODO variable coloring when tokens are available in the inspector!
    // 	format = highlighter->formatForStatement(value.string());
    // 	valueItem->setFont(format->font());
    // 	valueItem->setForeground(format->foreground());

    QTableWidgetItem *typeItem;
    switch (value.type()) {
    case Value::Empty: {
        typeItem = new QTableWidgetItem(i18nc("undefined type of a variable", "empty"));
        QFont font = typeItem->font();
        font.setItalic(true);
        typeItem->setFont(font);
    } break;
    case Value::Bool:
        typeItem = new QTableWidgetItem(i18n("boolean"));
        break;
    case Value::Number:
        typeItem = new QTableWidgetItem(i18n("number"));
        break;
    case Value::String:
        typeItem = new QTableWidgetItem(i18n("string"));
        break;
    default:
        // should never happen
        typeItem = new QTableWidgetItem(QStringLiteral("ERROR! please report to KTurtle developers"));
        break;
    }
    typeItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    variableTable->setItem(row, 1, valueItem);
    variableTable->setItem(row, 2, typeItem);

    variableTable->sortItems(0);
    variableTable->resizeColumnsToContents();
    variableTable->setEnabled(true);
}

void Inspector::updateFunction(const QString &name, const QStringList &parameters)
{
    // When there is already a the 'Nothing to show' line re-use that one and don't add another
    if (functionTableEmpty) {
        functionTableEmpty = false;
    } else {
        functionTable->insertRow(0);
    }

    QTableWidgetItem *nameItem = new QTableWidgetItem(name);
    nameItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    functionTable->setItem(0, 0, nameItem);
    functionMap[name] = nameItem;

    QTableWidgetItem *paramItem;
    if (parameters.empty()) {
        paramItem = new QTableWidgetItem(i18n("None"));
        QFont font = paramItem->font();
        font.setItalic(true);
        paramItem->setFont(font);
    } else {
        QString paramList = parameters.join(Translator::instance()->default2localized(QStringLiteral(",")));
        paramItem = new QTableWidgetItem(paramList);
    }

    paramItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    functionTable->setItem(0, 1, paramItem);
    functionTable->sortItems(0);
    functionTable->resizeColumnsToContents();
    functionTable->setEnabled(true);
}

void Inspector::updateTree(TreeNode *rootNode)
{
    treeMap.clear();
    treeView->clear();
    QTreeWidgetItem *rootItem = walkTree(rootNode);
    const auto rootItemChildren = rootItem->takeChildren();
    for (QTreeWidgetItem *item : rootItemChildren) {
        treeView->addTopLevelItem(item);
    }
    delete rootItem;
    treeView->expandAll();
    treeView->setEnabled(true);
}

QTreeWidgetItem *Inspector::walkTree(TreeNode *node)
{
    QTreeWidgetItem *result = new QTreeWidgetItem();
    result->setText(0, node->token()->look());
    QTextCharFormat *format = highlighter->tokenToFormat(node->token());
    if (format) {
        result->setForeground(0, format->foreground());
        QFont font(QFontDatabase::systemFont(QFontDatabase::FixedFont));
        font.setBold(format->font().bold());
        result->setFont(0, font);
    }
    treeMap[node] = result;
    if (node->hasChildren()) {
        for (uint i = 0; i < node->childCount(); i++) {
            result->addChild(walkTree(node->child(i)));
        }
    }
    return result;
}

int Inspector::findVariable(const QString &name)
{
    QTableWidgetItem *item = variableMap[name];
    if (!item)
        return -1;
    return item->row();

    // old implementation before we had a variableMap

    // 	// This function will search for a specified variable and return the row number of this variable.
    // 	QList<QTableWidgetItem*> matches = variableTable->findItems(name, Qt::MatchExactly);
    // 	QList<QTableWidgetItem*>::iterator i;
    // 	for (i = matches.begin(); i != matches.end(); ++i) {
    // 		if ((*i)->column() == 0) // only check the first column
    // 			return (*i)->row();
    // 	}
    // 	return -1;
}

void Inspector::markVariable(const QString &name)
{
    Q_UNUSED(name);
    // qDebug() << variableMap[name]->row();
}

void Inspector::markFunction(const QString &name)
{
    Q_UNUSED(name);
    // qDebug() << functionMap[name]->row();
}

void Inspector::markTreeNode(TreeNode *node)
{
    // 	//qDebug() << treeMap[node]->text(0);
    clearTreeMark();
    currentlyMarkedTreeItem = treeMap[node];
    previousTreeBackground = currentlyMarkedTreeItem->background(0);
    currentlyMarkedTreeItem->setBackground(0, QBrush(WORD_HIGHLIGHT_COLOR));
}

void Inspector::clearTreeMark()
{
    if (!currentlyMarkedTreeItem)
        return;
    currentlyMarkedTreeItem->setBackground(0, previousTreeBackground);
    currentlyMarkedTreeItem = nullptr;
}

void Inspector::clearAllMarks()
{
    clearTreeMark();
}

#include "moc_inspector.cpp"
