/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright 2009--2026 by Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the MsXpertSuite project.
 *
 * The MsXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


/////////////////////// StdLib includes


/////////////////////// Qt includes
#include <QDebug>
#include <QObject>
#include <QAction>
#include <QSettings>
#include <QKeySequenceEdit>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QScrollArea>
#include <QMessageBox>


/////////////////////// pappsomspp includes


/////////////////////// libXpertMassCore includes


/////////////////////// Local includes
#include "MsXpS/libXpertMassGui/ActionManagerTableWidget.hpp"

namespace MsXpS
{
namespace libXpertMassGui
{

ActionShortcutDelegate::ActionShortcutDelegate(QObject *parent)
  : QStyledItemDelegate(parent)
{
  // parent is ActionManagerTableWidget::mp_tableWidget.
}

QWidget *
ActionShortcutDelegate::createEditor(QWidget *parent,
                                     const QStyleOptionViewItem &,
                                     const QModelIndex &) const
{
  auto *editor = new QKeySequenceEdit(parent);
  editor->setClearButtonEnabled(true);
  return editor;
}

void
ActionShortcutDelegate::setEditorData(QWidget *editor,
                                      const QModelIndex &index) const
{
  auto *keyEdit = qobject_cast<QKeySequenceEdit *>(editor);
  if(!keyEdit)
    return;

  keyEdit->setKeySequence(QKeySequence(index.data(Qt::EditRole).toString()));
}

void
ActionShortcutDelegate::setModelData(QWidget *editor,
                                     QAbstractItemModel *model,
                                     const QModelIndex &index) const
{
  if(!index.isValid())
    return;

  auto *keyEdit = qobject_cast<QKeySequenceEdit *>(editor);
  if(!keyEdit)
    return;

  QString key_sequence =
    keyEdit->keySequence().toString(QKeySequence::NativeText);

  qDebug() << "The edited shortcut is now:" << key_sequence;

  // We need to check if there is already another key sequence
  // in another row of the model data. However, note that we accept
  // that the key sequence be empty and that there may be other
  // actions with an empty shortcut.
  if(!key_sequence.isEmpty())
    {
      int row_count = model->rowCount();
      int row_index = index.row();

      for(int iter = 0; iter < row_count; ++iter)
        {
          // Skip the current row if checking for edit validation
          if(iter == row_index)
            {
              continue;
            }

          QModelIndex iter_index =
            model->index(iter, ActionManagerTableWidgetColumns::ShortcutColumn);
          if(iter_index.isValid())
            {
              QVariant existingValue = model->data(iter_index, Qt::DisplayRole);

              if(existingValue == key_sequence)
                {
                  QMessageBox::information(
                    static_cast<QWidget *>(parent()),
                    "Editing of a new shortcut key sequence",
                    QString("The entered shortcut key "
                            "sequence (%1) exists already.\n"
                            "Please, fix this problem first.")
                      .arg(key_sequence));
                  return;
                }
            }
        }
    }

  // At this point we know the shortcut is unique.
  model->setData(index, key_sequence, Qt::EditRole);

  ActionManagerTableWidget *action_manager_table_widget_p = nullptr;

  QVariant variant = property("action_manager_table_widget");
  if(variant.canConvert<ActionManagerTableWidget *>())
    {
      action_manager_table_widget_p =
        variant.value<ActionManagerTableWidget *>();

      if(action_manager_table_widget_p == nullptr)
        {
          qFatal() << "Programming error.";
          return;
        }
    }
  else
    {
      qFatal() << "Programming error.";
      return;
    }


  ActionId action_id(action_manager_table_widget_p->actionId(index));

  // Now that we have the action_id, we can get the corresponding action
  // and modify it in place with the new QKeySequence.

  // QAction *action_p =
  //   action_manager_table_widget_p->getActionManager()->getAction(action_id);
  // action_p->setShortcut(keyEdit->keySequence());

  // Same as commented above.
  action_manager_table_widget_p->updateShortcut(action_id,
                                                keyEdit->keySequence());
}

void
ActionShortcutDelegate::updateEditorGeometry(QWidget *editor,
                                             const QStyleOptionViewItem &option,
                                             const QModelIndex &) const
{
  editor->setGeometry(option.rect);
}

ActionManagerTableWidget::ActionManagerTableWidget(QWidget *parent)
  : QWidget(parent)
{
  mp_groupBox = new QGroupBox(this);

  QVBoxLayout *scroll_area_layout_p = new QVBoxLayout(mp_groupBox);

  mp_tableWidget = new QTableWidget(mp_groupBox);
  mp_tableWidget->setColumnCount(ColumnCount);
  mp_tableWidget->setHorizontalHeaderLabels(
    {"Context", "Category", "Unit", "Shortcut", "Label"});

  mp_tableWidget->horizontalHeader()->setStretchLastSection(true);
  mp_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
  mp_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
  mp_tableWidget->setEditTriggers(QAbstractItemView::DoubleClicked |
                                  QAbstractItemView::EditKeyPressed);

  ActionShortcutDelegate *action_shortcut_delegate_p =
    new ActionShortcutDelegate(mp_tableWidget);

  action_shortcut_delegate_p->setProperty("action_manager_table_widget",
                                          QVariant::fromValue(this));

  mp_tableWidget->setItemDelegateForColumn(
    ActionManagerTableWidgetColumns::ShortcutColumn,
    action_shortcut_delegate_p);

  scroll_area_layout_p->addWidget(mp_tableWidget);

  QVBoxLayout *widget_layout_p = new QVBoxLayout(this);
  widget_layout_p->addWidget(mp_groupBox);
  setLayout(widget_layout_p);
}

void
ActionManagerTableWidget::setActionManager(ActionManager *action_manager_p)
{
  Q_ASSERT(action_manager_p != nullptr);

  mp_actionManager = action_manager_p;
}

ActionManager *
ActionManagerTableWidget::getActionManager()
{
  return mp_actionManager;
}

int
ActionManagerTableWidget::fillInTableWithData()
{
  Q_ASSERT(mp_actionManager != nullptr);

  const QMap<libXpertMassGui::ActionId, QAction *> &actions =
    mp_actionManager->getActionMap();

  // qDebug() << "There are" << actions.size() << "action pairs in the map.";

  QMap<libXpertMassGui::ActionId, QAction *>::const_iterator the_iterator =
    actions.begin();

  while(the_iterator != actions.end())
    {
      libXpertMassGui::ActionId action_id = the_iterator.key();
      QAction *action_p                   = the_iterator.value();

      // qDebug() << "Going to add another row:" << action_id.context
      //          << action_id.category << action_id.unit << action_id.label
      //          << action_p->shortcut();

      addActionRow(action_id.context,
                   action_id.category,
                   action_id.unit,
                   action_id.label,
                   action_p->shortcut());

      ++the_iterator;
    }

  return mp_tableWidget->rowCount();
}

void
ActionManagerTableWidget::addActionRow(const QString &context,
                                       const QString &category,
                                       const QString &unit,
                                       const QString &label,
                                       const QKeySequence &shortcut)
{
  // ActionId action_id(context, category, unit, label);
  addActionRow({context, category, unit, label}, shortcut);
}

void
ActionManagerTableWidget::addActionRow(const ActionId action_id,
                                       const QKeySequence &shortcut)
{
  const int row = mp_tableWidget->rowCount();
  mp_tableWidget->insertRow(row);

  QTableWidgetItem *non_shortcut_item_p;

  non_shortcut_item_p = new QTableWidgetItem(action_id.context);
  // Not editable!
  non_shortcut_item_p->setFlags(non_shortcut_item_p->flags() ^
                                Qt::ItemIsEditable);
  mp_tableWidget->setItem(
    row, ActionManagerTableWidgetColumns::ContextColumn, non_shortcut_item_p);

  non_shortcut_item_p = new QTableWidgetItem(action_id.category);
  // Not editable!
  non_shortcut_item_p->setFlags(non_shortcut_item_p->flags() ^
                                Qt::ItemIsEditable);
  mp_tableWidget->setItem(
    row, ActionManagerTableWidgetColumns::CategoryColumn, non_shortcut_item_p);

  non_shortcut_item_p = new QTableWidgetItem(action_id.unit);
  // Not editable!
  non_shortcut_item_p->setFlags(non_shortcut_item_p->flags() ^
                                Qt::ItemIsEditable);
  mp_tableWidget->setItem(
    row, ActionManagerTableWidgetColumns::UnitColumn, non_shortcut_item_p);

  non_shortcut_item_p = new QTableWidgetItem(action_id.label);
  // Not editable!
  non_shortcut_item_p->setFlags(non_shortcut_item_p->flags() ^
                                Qt::ItemIsEditable);
  mp_tableWidget->setItem(
    row, ActionManagerTableWidgetColumns::LabelColumn, non_shortcut_item_p);

  // Only the cells in the ShortcutColumn are editable.
  QTableWidgetItem *shortcut_item_p =
    new QTableWidgetItem(shortcut.toString(QKeySequence::NativeText));
  // Editable!
  shortcut_item_p->setFlags(shortcut_item_p->flags() | Qt::ItemIsEditable);

  mp_tableWidget->setItem(
    row, ActionManagerTableWidgetColumns::ShortcutColumn, shortcut_item_p);
}

ActionId
ActionManagerTableWidget::actionId(const QModelIndex &index)
{
  ActionId action_id;

  if(!index.isValid())
    return action_id;

  const QAbstractItemModel *model = index.model();

  action_id.context =
    model->index(index.row(), ActionManagerTableWidgetColumns::ContextColumn)
      .data()
      .toString();

  action_id.category =
    model->index(index.row(), ActionManagerTableWidgetColumns::CategoryColumn)
      .data()
      .toString();

  action_id.unit =
    model->index(index.row(), ActionManagerTableWidgetColumns::UnitColumn)
      .data()
      .toString();

  action_id.label =
    model->index(index.row(), ActionManagerTableWidgetColumns::LabelColumn)
      .data()
      .toString();

  return action_id;
}

void
ActionManagerTableWidget::updateShortcut(const ActionId &action_id,
                                         const QKeySequence &shortcut)
{
  if(!action_id.isValid())
    {
      qFatal() << "The ActionId is not valid.";
      return;
    }

  QAction *action_p = mp_actionManager->getAction(action_id);

  if(action_p != nullptr)
    {
      action_p->setShortcut(shortcut);
    }
  else
    {
      qFatal() << "Failed to get action for shortcut" << shortcut;
    }
}

} // namespace libXpertMassGui
} // namespace MsXpS
