#include "columnforeignkeypanel.h"
#include "ui_columnforeignkeypanel.h"
#include "schemaresolver.h"
#include "uiutils.h"
#include <QDebug>
#include <QSignalMapper>

ColumnForeignKeyPanel::ColumnForeignKeyPanel(QWidget *parent) :
    ConstraintPanel(parent),
    ui(new Ui::ColumnForeignKeyPanel)
{
    ui->setupUi(this);
    init();
}

ColumnForeignKeyPanel::~ColumnForeignKeyPanel()
{
    delete ui;
}

void ColumnForeignKeyPanel::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);
    switch (e->type()) {
        case QEvent::LanguageChange:
            ui->retranslateUi(this);
            break;
        default:
            break;
    }
}


bool ColumnForeignKeyPanel::validate()
{
    bool tableOk = (ui->fkTableCombo->currentIndex() > -1);
    bool columnOk = (ui->fkColumnCombo->currentIndex() > -1);
    bool nameOk = !ui->namedCheckBox->isChecked() || !ui->nameEdit->text().isEmpty();

    setValidState(ui->fkTableCombo, tableOk, tr("Pick the foreign table."));
    setValidState(ui->fkColumnCombo, columnOk, tr("Pick the foreign column."));
    setValidState(ui->nameEdit, nameOk, tr("Enter a name of the constraint."));

    if (columnOk)
    {
        QString warn = validateForWarning();
        if (!warn.isEmpty())
            setValidStateWarning(ui->fkColumnCombo, warn);
    }

    return tableOk && columnOk && nameOk;
}

QString ColumnForeignKeyPanel::validateForWarning()
{
    return isColumnOkForFk() ? QString() : tr("The referenced column should be a PRIMARY KEY or UNIQUE.");
}

bool ColumnForeignKeyPanel::isColumnOkForFk()
{
    int tableIdx = ui->fkTableCombo->currentIndex();
    if (tableIdx == -1)
        return true;

    QString tableName = ui->fkTableCombo->currentText();

    int columnIdx = ui->fkColumnCombo->currentIndex();
    if (columnIdx == -1)
        return true;

    QStandardItem* colItem = fkColumnsModel.item(columnIdx);
    QString colName = colItem->text();
    if (colItem->data().toBool())
        colName = columnStmt->name;

    SchemaResolver resolver(db);
    return resolver.isColumnPkOrUnique(tableName, colName);
}

void ColumnForeignKeyPanel::constraintAvailable()
{
    readTables();
    readConstraint();
}

void ColumnForeignKeyPanel::init()
{
    setFocusProxy(ui->fkTableCombo);

    ui->fkTableCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);

    ui->fkColumnCombo->setModel(&fkColumnsModel);
    ui->fkColumnCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
    connect(ui->fkColumnCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateValidation()));

    connect(ui->namedCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
    connect(ui->nameEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
    connect(ui->fkTableCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFkColumns()));
    connect(ui->fkTableCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateState()));
    connect(ui->fkTableCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateValidation()));
    connect(ui->onDeleteCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
    connect(ui->onUpdateCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
    connect(ui->matchCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));

    ui->deferrableCombo->addItems({
                                      "",
                                      sqliteDeferrable(SqliteDeferrable::DEFERRABLE),
                                      sqliteDeferrable(SqliteDeferrable::NOT_DEFERRABLE)
                                  });
    ui->initiallyCombo->addItems({
                                     "",
                                     sqliteInitially(SqliteInitially::DEFERRED),
                                     sqliteInitially(SqliteInitially::IMMEDIATE),
                                 });

    QStringList reactions = {
        SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::NO_ACTION),
        SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::SET_NULL),
        SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::SET_DEFAULT),
        SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::CASCADE),
        SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::RESTRICT)
    };
    ui->onUpdateCombo->addItems(reactions);
    ui->onDeleteCombo->addItems(reactions);
    ui->matchCombo->addItems({"SIMPLE", "FULL", "PARTIAL"});

    connect(ui->namedCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
    updateState();
}

void ColumnForeignKeyPanel::updateState()
{
    bool tableSelected = (ui->fkTableCombo->currentIndex() > -1);
    ui->fkColumnCombo->setEnabled(tableSelected);
    ui->deferrableCombo->setEnabled(tableSelected);
    ui->initiallyCombo->setEnabled(tableSelected);
    ui->namedCheckBox->setEnabled(tableSelected);
    ui->nameEdit->setEnabled(tableSelected && ui->namedCheckBox->isChecked());
    ui->onDeleteCheckBox->setEnabled(tableSelected);
    ui->onUpdateCheckBox->setEnabled(tableSelected);
    ui->matchCheckBox->setEnabled(tableSelected);
    ui->onDeleteCombo->setEnabled(tableSelected && ui->onDeleteCheckBox->isChecked());
    ui->onUpdateCombo->setEnabled(tableSelected && ui->onUpdateCheckBox->isChecked());
    ui->matchCombo->setEnabled(tableSelected && ui->matchCheckBox->isChecked());
}

void ColumnForeignKeyPanel::updateFkColumns()
{
    QStringList columns;
    if (ui->fkTableCombo->currentIndex() == -1)
    {
        fkColumnsModel.clear();
        updateState();
        return;
    }

    SchemaResolver resolver(db);
    columns = resolver.getTableColumns(ui->fkTableCombo->currentText()); // TODO named db attach not supported
    for (const QString& col : columns)
        fkColumnsModel.appendRow(new QStandardItem(col));

    auto item = new QStandardItem(tr("Column with the same name"));
    QFont font = ui->fkColumnCombo->font();
    font.setItalic(true);
    item->setData(font, Qt::FontRole);
    item->setData(true);
    fkColumnsModel.insertRow(0, item);
}

void ColumnForeignKeyPanel::readConstraint()
{
    if (constraint.isNull())
        return;

    SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
    if (!constr->foreignKey)
        return;

    // Table
    if (!constr->foreignKey->foreignTable.isNull())
        ui->fkTableCombo->setCurrentText(constr->foreignKey->foreignTable);

    // Conditions
    for (SqliteForeignKey::Condition* condition : constr->foreignKey->conditions)
        readCondition(condition);

    // Initially, Deferrable
    ui->deferrableCombo->setCurrentText(sqliteDeferrable(constr->foreignKey->deferrable));
    ui->initiallyCombo->setCurrentText(sqliteInitially(constr->foreignKey->initially));

    // Name
    if (!constr->name.isNull())
    {
        ui->namedCheckBox->setChecked(true);
        ui->nameEdit->setText(constr->name);
    }

    // Column
    if (constr->foreignKey->indexedColumns.size() > 1)
    {
        qWarning() << "More than one referenced column in the column foreign key:" << constr->detokenize();
        return;
    }

    if (constr->foreignKey->indexedColumns.size() == 1)
        ui->fkColumnCombo->setCurrentText(constr->foreignKey->indexedColumns.first()->name);
    else if (fkColumnsModel.rowCount() > 0)
        ui->fkColumnCombo->setCurrentIndex(0);
    else
        ui->fkColumnCombo->setCurrentIndex(-1);
}

void ColumnForeignKeyPanel::readCondition(SqliteForeignKey::Condition* condition)
{
    switch (condition->action)
    {
        case SqliteForeignKey::Condition::UPDATE:
            ui->onUpdateCheckBox->setChecked(true);
            ui->onUpdateCombo->setCurrentText(SqliteForeignKey::Condition::toString(condition->reaction));
            break;
        case SqliteForeignKey::Condition::INSERT:
            // INSERT is not officially supported.
            break;
        case SqliteForeignKey::Condition::DELETE:
            ui->onDeleteCheckBox->setChecked(true);
            ui->onDeleteCombo->setCurrentText(SqliteForeignKey::Condition::toString(condition->reaction));
            break;
        case SqliteForeignKey::Condition::MATCH:
            ui->matchCheckBox->setChecked(true);
            ui->matchCombo->setCurrentText(SqliteForeignKey::Condition::toString(condition->reaction));
            break;
    }
}

void ColumnForeignKeyPanel::storeConfiguration()
{
    if (constraint.isNull())
        return;

    // Type
    SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
    constr->type = SqliteCreateTable::Column::Constraint::FOREIGN_KEY;

    // Cleanup & initial setup
    delete constr->foreignKey;

    constr->foreignKey = new SqliteForeignKey();
    constr->foreignKey->setParent(constr);

    // Foreign table
    constr->foreignKey->foreignTable = ui->fkTableCombo->currentText();

    // Column
    if (fkColumnsModel.rowCount() > 0 && ui->fkColumnCombo->currentIndex() == 0)
    {
        // Implicit column - same name as local column.
    }
    else
    {
        SqliteIndexedColumn* idxCol = new SqliteIndexedColumn(ui->fkColumnCombo->currentText());
        idxCol->setParent(constr->foreignKey);
        constr->foreignKey->indexedColumns << idxCol;
    }

    // Actions/reactions
    if (ui->onDeleteCheckBox->isChecked())
        storeCondition(SqliteForeignKey::Condition::DELETE, ui->onDeleteCombo->currentText());

    if (ui->onUpdateCheckBox->isChecked())
        storeCondition(SqliteForeignKey::Condition::UPDATE, ui->onUpdateCombo->currentText());

    if (ui->matchCheckBox->isChecked())
        storeMatchCondition(ui->matchCombo->currentText());

    // Deferred/initially
    constr->foreignKey->deferrable = sqliteDeferrable(ui->deferrableCombo->currentText());
    constr->foreignKey->initially = sqliteInitially(ui->initiallyCombo->currentText());

    // Name
    constr->name = QString();
    if (ui->namedCheckBox->isChecked())
        constr->name = ui->nameEdit->text();
}

void ColumnForeignKeyPanel::storeCondition(SqliteForeignKey::Condition::Action action, const QString& reaction)
{
    SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());

    SqliteForeignKey::Condition* condition = new SqliteForeignKey::Condition(
                action,
                SqliteForeignKey::Condition::toEnum(reaction)
            );
    condition->setParent(constr->foreignKey);
    constr->foreignKey->conditions << condition;
}

void ColumnForeignKeyPanel::storeMatchCondition(const QString& reaction)
{
    SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());

    SqliteForeignKey::Condition* condition = new SqliteForeignKey::Condition(reaction);
    condition->setParent(constr->foreignKey);
    constr->foreignKey->conditions << condition;
}

void ColumnForeignKeyPanel::readTables()
{
    SchemaResolver resolver(db);
    resolver.setIgnoreSystemObjects(true);
    QStringList tables = resolver.getTables(); // TODO named db attach not supported

    tables.sort(Qt::CaseInsensitive);

    ui->fkTableCombo->addItems(tables);
    ui->fkTableCombo->setCurrentIndex(-1);
}
