
#include "main.h"

// -----------------------------------------------------------------------------------------------

// Global list
QList<DataEntity> global;

// ===============================================================================================

DataEntity::DataEntity()
{
  name = QString("Entry %1").arg(rand()%1000);
  description = QString();
}

DataEntity::~DataEntity() { }

bool DataEntity::setData(const QString _name,
                         const QString _description)
{
  // Set the values
  name = _name;
  description = _description;
  return true;
}

// ===============================================================================================

ListModel::ListModel(QObject *parent, QList<DataEntity>*_local_list, QList<DataEntity>*_global_list) : QAbstractTableModel(parent)
{
  local_list = _local_list;
  global_list = _global_list;
}

ListModel::~ListModel() { }

int ListModel::listCount() const
{
  int totalCount = 0;
  if (local_list) totalCount += local_list->count();
  if (global_list) totalCount += global_list->count();
  return totalCount;
}

Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
{
  Qt::ItemFlags theFlags = QAbstractTableModel::flags(index);
  if (index.isValid())
    theFlags |= Qt::ItemIsSelectable | Qt::ItemIsDragEnabled |
        Qt::ItemIsEnabled;
  return theFlags;
}

QVariant ListModel::data(const QModelIndex &index, int role) const
{
  if (!index.isValid() ||
      index.row() < 0 || index.row() >= listCount() ||
      index.column() < 0 || index.column() >= 1)
    return QVariant();

  const DataEntity *item = NULL;
  int offset = 0;
  bool is_global = false;

  if (local_list)
  {
    offset = local_list->count();
    if (index.row() < offset) item = &local_list->at(index.row());
  }

  if (!item && global_list && index.row()-offset < global_list->count())
  {
    item = &global_list->at(index.row()-offset);
    is_global = true;
  }

  if (role == Qt::DisplayRole || role == Qt::EditRole)
  {
    switch (index.column())
    {
      case 0: return item->getName();
      default: Q_ASSERT(false);
    }
  }
  else if (role == Qt::FontRole)
  {
    if (is_global) { QFont font; font.setBold(true); return font; }
  }
  else if (role == Qt::UserRole)
  {
    return item->getName();
  }

  return QVariant();
}

int ListModel::rowCount(const QModelIndex &index) const
{
  return index.isValid() ? 0 : listCount();
}

int ListModel::columnCount(const QModelIndex &index) const
{
  return index.isValid() ? 0 : 1;
}

DataEntity *ListModel::getItem(const QModelIndex &index)
{
  if (!index.isValid() ||
      index.row() < 0 || index.row() >= listCount())
    return 0;

  DataEntity *item = NULL;
  int offset = 0;

  if (local_list)
  {
    offset = local_list->count();
    if (index.row() < offset) item = (DataEntity *)&local_list->at(index.row());
  }

  if (!item && global_list && index.row()-offset < global_list->count())
  {
    item = (DataEntity *)&global_list->at(index.row()-offset);
  }

  return item;
}

void ListModel::deleteItem(const QModelIndex &index)
{
  if (!index.isValid() ||
      index.row() < 0 || index.row() >= listCount())
    return;

  int offset = 0;

  beginResetModel();
  //beginRemoveRows(index, index.row(), index.row()); // Should this work too?

  if (local_list)
  {
    offset = local_list->count();
    if (index.row() < offset)
    {
      // Delete from local list
      local_list->removeAt(index.row());
      goto done;
    }
  }

  if (global_list && index.row()-offset < global_list->count())
  {
    // Delete from global list
    global_list->removeAt(index.row()-offset);
    goto done;
  }

 done:
  //endRemoveRows();
  endResetModel();
}

// ===============================================================================================

ProxyModel::ProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
{
  substr.clear();
}

void ProxyModel::clearFilters()
{
  substr.clear();
  invalidateFilter();
}

bool ProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
  return true;
}

void ProxyModel::setSubStr(const QString &new_substr)
{
  if (substr != new_substr)
  {
    substr = new_substr;
    invalidateFilter();
  }
}

// ===============================================================================================

DetailsDialog::DetailsDialog(QWidget *parent) : QDialog(parent)
{
  QGridLayout *gridLayout = new QGridLayout(this);

  QLabel *label_name = new QLabel(this);
  label_name->setText("Name");
  gridLayout->addWidget(label_name, 0, 0, 1, 1);
  lineEdit_name = new QLineEdit(this);
  gridLayout->addWidget(lineEdit_name, 0, 1, 1, 1);

  QLabel *label_desc = new QLabel(this);
  label_desc->setText("Description");
  gridLayout->addWidget(label_desc, 1, 0, 1, 1);

  textEdit_description = new QTextEdit(this);
  gridLayout->addWidget(textEdit_description, 1, 1, 1, 1);

  QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
  buttonBox->setOrientation(Qt::Vertical);
  buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
  gridLayout->addWidget(buttonBox, 0, 2, 2, 1);

  QObject::connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
  QObject::connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
}

DetailsDialog::~DetailsDialog() { }

void DetailsDialog::setFormValues(DataEntity * const data)
{
  lineEdit_name->setText(data->getName());
  textEdit_description->setText(data->getDescription());
}

bool DetailsDialog::getFormValues(DataEntity * const data)
{
  return data->setData(lineEdit_name->text(),
                       textEdit_description->toPlainText());
}

// ===============================================================================================

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
  // Create window widgets

  for (uint i=0; i<20; ++i) { DataEntity *de = new DataEntity(); local.append(*de); }

  model = new ListModel(0, &local, &global);

  proxyModel = new ProxyModel();
  proxyModel->setSourceModel(model);
  proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
  proxyModel->setSortRole(Qt::UserRole);
  proxyModel->setFilterRole(Qt::UserRole);
  proxyModel->sort(1, Qt::AscendingOrder);
  proxyModel->setDynamicSortFilter(true);

  QTableView *tableView = new QTableView();
  tableView->setModel(proxyModel);

  tableView->setSortingEnabled(true);
  tableView->horizontalHeader()->setStretchLastSection(true);
  //tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
  tableView->horizontalHeader()->setSectionResizeMode(0,QHeaderView::Stretch);
  tableView->horizontalHeader()->hide();
  tableView->verticalHeader()->hide();
  tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
  tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
  tableView->setSelectionMode(QAbstractItemView::SingleSelection);
  tableView->setShowGrid(false);
  tableView->setDragDropMode(QAbstractItemView::DragOnly); // QAbstractItemView::DragDrop
  tableView->setAlternatingRowColors(true);

  connect(tableView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(editListEntry(const QModelIndex&)));

  QGridLayout *layout = new QGridLayout();
  layout->addWidget(tableView, 0,0,3,1);

  QPushButton *addButton = new QPushButton();
  addButton->setText("Add");
  layout->addWidget(addButton, 0,1,1,1);
  connect(addButton, SIGNAL(clicked()), this, SLOT(addListEntry()));

  QPushButton *delButton = new QPushButton();
  delButton->setText("Delete");
  layout->addWidget(delButton, 1,1,1,1);
  connect(delButton, SIGNAL(clicked()), this, SLOT(deleteListEntry()));

  QWidget* central_widget = new QWidget(this);
  central_widget->setLayout(layout);
  setCentralWidget(central_widget);
}

MainWindow::~MainWindow() { }

void MainWindow::editListItem(const QModelIndex *index)
{
  DetailsDialog *dialog;
  DataEntity *newItem = 0;
  bool add_item = (index == 0);

  qDebug("editListItem: %u %s", (index?index->row():0), (add_item ? "Add New" : "Edit"));

  QAbstractItemView *view = this->findChild<QTableView *>();
  if (!view) return;

  QModelIndex sourceIndex;
  if (!add_item) sourceIndex = proxyModel->mapToSource(*index);

  dialog = new DetailsDialog(this);
  if (add_item)
    newItem = new DataEntity();
  else
  {
    qDebug() << "TABLE NAME=" << model->getItem(*index)->getName();
    qDebug() << "PROXY NAME=" << model->getItem(sourceIndex)->getName();
    dialog->setFormValues(model->getItem(sourceIndex));
  }

  if (dialog)
  {
    while (1)
    {
      bool result = false;

      // Execute the dialog (and leave loop, if aborted)
      if (dialog->exec() == QDialog::Rejected) break;

      // Get values from dialog (and leave loop, if successful)
      if (add_item)
      {
        result = dialog->getFormValues(newItem);
        if (result) local.append(*newItem);
      }
      else
      {
        result = dialog->getFormValues(model->getItem(sourceIndex));
      }

      //emit model->dataChanged(*index, *index);
      //emit model->dataChanged(sourceIndex, sourceIndex);
      //emit proxyModel->dataChanged(*index, *index);

      // Force resorting of list
      //proxyModel->sort(1, Qt::DescendingOrder);
      //proxyModel->sort(1, Qt::AscendingOrder);
      //model->reset();
      //proxyModel->reset();
      if (result) break;
    }

    // Destroy the used dialog
    delete dialog;
  }
}

void MainWindow::addListEntry()
{
  editListItem(0);
}

void MainWindow::editListEntry(const QModelIndex &index)
{
  qDebug("editListEntry: %u", index.row());
  editListItem(&index);
}

void MainWindow::deleteListEntry()
{
  qDebug("deleteListEntry called");

  QAbstractItemView *view = this->findChild<QTableView *>();
  if (!view) return;

  QModelIndex index = view->currentIndex();

  if (index.isValid())
  {
    QModelIndex sourceIndex = proxyModel->mapToSource(index);

    qDebug("deleteListEntry: %u", sourceIndex.row());
    model->deleteItem(sourceIndex);
  }
}

// ===============================================================================================

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);

  for (uint i=0; i<20; ++i) { DataEntity *de = new DataEntity(); global.append(*de); }

  MainWindow *mainWin = new MainWindow;
  mainWin->resize(800, 640);
  mainWin->show();
  return app.exec();
}
