#include "ExplorerDock.hpp"
#include "ui_ExplorerDock.h"
#include "MainWindow.hpp"
#include "../logic/DagStructure.hpp"
#include "../logic/InputDag.hpp"

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

/**
 * Constructor
 */
ExplorerDock::ExplorerDock(MainWindow *parent) :
    QDockWidget(parent),
    _mainWindow(parent),
    _digDagData(0),
    _ui(new Ui::ExplorerDock),
    _patternsModel(0),
    _patternsProxyModel(0),
    _patternsSelectionModel(0)
{
  _ui->setupUi(this);

  setupModelsAndViews();

  connectSlots();

}

/**
 * Destructor
 */
ExplorerDock::~ExplorerDock() {
  delete _patternsSelectionModel;
  delete _ui;
}

//==============================================================================
// Getters & setters

/**
 *
 */
void ExplorerDock::setDigDagData(DigDagData *data) {
  _digDagData = data;

  // Clear previous data in model, if any. But dont remove column headers.
  if (_patternsModel->rowCount() > 0) {
    _patternsModel->removeRows(0, _patternsModel->rowCount());
  }

  if (_inputsModel->rowCount() > 0) {
    _inputsModel->removeRows(0, _inputsModel->rowCount());
  }

  // Update file properties model
  updateFileProperties();

  // Set patterns model content
  DagStructure* dagStructure;
  for (int i = 0; i < _digDagData->getNumberOfDagPatterns(); i++) {

    // Get pattern structure
    dagStructure = _digDagData->getPatternsList()->at(i)->getStructure();
    // Insert new row
    _patternsModel->insertRow(i);
    // Insert index
    _patternsModel->setData(_patternsModel->index(i, 0), i); // TODO getIndex()
    // Insert support
    _patternsModel->setData(_patternsModel->index(i, 1), _digDagData->getPatternsList()->at(i)->getSupport());
    // Insert nb nodes
    _patternsModel->setData(_patternsModel->index(i, 2), dagStructure->getNumberOfNodes());
    // Insert nb edges
    _patternsModel->setData(_patternsModel->index(i, 3), dagStructure->getNumberOfEdges());
    // Insert height
    _patternsModel->setData(_patternsModel->index(i, 4), dagStructure->getLength());
  } // end for i


  // Set inputs model content
  QList<InputDag*>* inputDagsList = _digDagData->getInputDagsList();
  InputDag *inputDag;
  for (int i = 0; i < inputDagsList->size(); i++) {
    // Get input DAG
    inputDag = inputDagsList->at(i);
    // Insert new row
    _inputsModel->insertRow(i);
    // Insert index
    _inputsModel->setData(_inputsModel->index(i, 0), inputDag->getIndex());
    // Insert number of supported patterns
    _inputsModel->setData(_inputsModel->index(i, 1), inputDag->getNumberOfSupportedPatterns());
  } // end for i

}


/**
 * Returns the current selected DAG pattern index.
 * If current selection is invalid, returns -1.
 *
 * @return DAG pattern integer index
 */
int ExplorerDock::getCurrentDagPatternIndex() {
  int index = -1;
  if (_currentPatternModelIndex.isValid()) {
    //_currentPatternModelIndex = _patternsModel->index(current.row(), 0);
    index = _patternsModel->data(_currentPatternModelIndex).toInt();
  }
  return index;
}


//==============================================================================
// Methods

/**
 * Make connections between objets and slots to handle events.
 * This function must be called when all object are created.
 */
void ExplorerDock::connectSlots() {

  connect(_patternsSelectionModel,
          SIGNAL(currentChanged(QModelIndex, QModelIndex)),
          this,
          SLOT(changeCurrentDagPatternItem(QModelIndex,QModelIndex)));

  connect(_inputsSelectionModel,
          SIGNAL(currentChanged(QModelIndex, QModelIndex)),
          this,
          SLOT(changeCurrentInputDagItem(QModelIndex,QModelIndex)));

}


/**
 * Initializes models & views used in the dock : creates objects, set headers,
 * create selection models, etc.
 */
void ExplorerDock::setupModelsAndViews() {

  //----------------------------------------------------------------------------
  // Patterns model/view
  _patternsModel = new QStandardItemModel(0, 5);

  // Set headers
  _patternsModel->setHeaderData(0, Qt::Horizontal, tr("Index"));
  _patternsModel->setHeaderData(1, Qt::Horizontal, tr("Support"));
  _patternsModel->setHeaderData(2, Qt::Horizontal, tr("Nodes"));
  _patternsModel->setHeaderData(3, Qt::Horizontal, tr("Edges"));
  _patternsModel->setHeaderData(4, Qt::Horizontal, tr("Height"));

  // Link model to the view
  _ui->patternsTreeView->setModel(_patternsModel);

  // Create selection model
  _patternsSelectionModel = new QItemSelectionModel(_patternsModel);

  // Link selection model to the view
  _ui->patternsTreeView->setSelectionModel(_patternsSelectionModel);

  // TODO Create proxy model
  //  _patternsProxyModel = new QSortFilterProxyModel;
  //  _patternsProxyModel->setDynamicSortFilter(true);


  //----------------------------------------------------------------------------
  // Inputs model/view
  _inputsModel = new QStandardItemModel(0, 2);

  // Set headers
  _inputsModel->setHeaderData(0, Qt::Horizontal, tr("Index"));
  _inputsModel->setHeaderData(1, Qt::Horizontal, tr("Supported patterns"));

  // Link model to the view
  _ui->inputsTreeView->setModel(_inputsModel);

  // Create selection model
  _inputsSelectionModel = new QItemSelectionModel(_inputsModel);

  // Link selection model to the view
  _ui->inputsTreeView->setSelectionModel(_inputsSelectionModel);


  //----------------------------------------------------------------------------
  // File properties model/view
  _filePropertiesModel = new QStandardItemModel(0, 2);

  // Set headers
  _filePropertiesModel->setHeaderData(0, Qt::Horizontal, tr("Property"));
  _filePropertiesModel->setHeaderData(1, Qt::Horizontal, tr("Value"));

  // Insert rows : properties names
  // TODO create labels vector
  _filePropertiesModel->insertRow(0);
  _filePropertiesModel->setData(_filePropertiesModel->index(0, 0), tr("File name"));
  _filePropertiesModel->insertRow(1);
  _filePropertiesModel->setData(_filePropertiesModel->index(1, 0), tr("DigDag command line"));
  _filePropertiesModel->insertRow(2);
  _filePropertiesModel->setData(_filePropertiesModel->index(2, 0), tr("Input DAGs folder"));
  _filePropertiesModel->insertRow(3);
  _filePropertiesModel->setData(_filePropertiesModel->index(3, 0), tr("Number of input DAGs"));
  _filePropertiesModel->insertRow(4);
  _filePropertiesModel->setData(_filePropertiesModel->index(4, 0), tr("Number of pattern DAGs"));
  _filePropertiesModel->insertRow(5);
  _filePropertiesModel->setData(_filePropertiesModel->index(5, 0), tr("Edge pruning threshold"));
  _filePropertiesModel->insertRow(6);
  _filePropertiesModel->setData(_filePropertiesModel->index(6, 0), tr("Min. support"));
  _filePropertiesModel->insertRow(7);
  _filePropertiesModel->setData(_filePropertiesModel->index(7, 0), tr("Min. support found"));
  _filePropertiesModel->insertRow(8);
  _filePropertiesModel->setData(_filePropertiesModel->index(8, 0), tr("Max. support found"));
  _filePropertiesModel->insertRow(9);
  _filePropertiesModel->setData(_filePropertiesModel->index(9, 0), tr("Min. size found"));
  _filePropertiesModel->insertRow(10);
  _filePropertiesModel->setData(_filePropertiesModel->index(10, 0), tr("Max. size found"));
  _filePropertiesModel->insertRow(11);
  _filePropertiesModel->setData(_filePropertiesModel->index(11, 0), tr("Min. length found"));
  _filePropertiesModel->insertRow(12);
  _filePropertiesModel->setData(_filePropertiesModel->index(12, 0), tr("Max. length found"));

  // Link model to the view
  _ui->fileTreeView->setModel(_filePropertiesModel);

} // end setupModelsAndViews


/**
 * Updates file properties model information and GUI from DigDagData.
 */
void ExplorerDock::updateFileProperties() {
  _filePropertiesModel->setData(_filePropertiesModel->index(0, 1), _digDagData->getFileName());
  _filePropertiesModel->setData(_filePropertiesModel->index(1, 1), _digDagData->getCommandLine());
  _filePropertiesModel->setData(_filePropertiesModel->index(2, 1), _digDagData->getInputFolder());
  _filePropertiesModel->setData(_filePropertiesModel->index(3, 1), _digDagData->getNumberOfInputDags());
  _filePropertiesModel->setData(_filePropertiesModel->index(4, 1), _digDagData->getNumberOfDagPatterns());
  _filePropertiesModel->setData(_filePropertiesModel->index(5, 1), _digDagData->getEPThreshold());
  _filePropertiesModel->setData(_filePropertiesModel->index(6, 1), _digDagData->getMinSupport());
  _filePropertiesModel->setData(_filePropertiesModel->index(7, 1), _digDagData->getMinDagSupport());
  _filePropertiesModel->setData(_filePropertiesModel->index(8, 1), _digDagData->getMaxDagSupport());
  _filePropertiesModel->setData(_filePropertiesModel->index(9, 1), _digDagData->getMinDagSize());
  _filePropertiesModel->setData(_filePropertiesModel->index(10, 1), _digDagData->getMaxDagSize());
  _filePropertiesModel->setData(_filePropertiesModel->index(11, 1), _digDagData->getMinDagLength());
  _filePropertiesModel->setData(_filePropertiesModel->index(12, 1), _digDagData->getMaxDagLength());
}


/**
 * For given model, selection model, item selection and model index, this function
 * selects the row in the model.
 * Selecting a row automatically triggers the currentChanged signal and calls
 * the connected slot. Therefore display is updated after the function.
 *
 * @param model The model in which the row is going to be selected
 * @param selectionModel The model's item selection model
 * @param itemSelection The model's item selection
 * @param persistentModelIndex The model's persistent model index
 * @param row The row to select in the model
 */
void ExplorerDock::selectItemAtRow(QStandardItemModel *model,
                                   QItemSelectionModel *itemSelectionModel,
                                   QItemSelection &itemSelection,
                                   QPersistentModelIndex &persistentModelIndex,
                                   int row) {

  persistentModelIndex = model->index(row, 0);

  if (persistentModelIndex.isValid()) {
    // Clear selection
    itemSelection.clear();
    itemSelectionModel->clearSelection();

    // Set selected item
    QModelIndex topLeft;
    QModelIndex bottomRight;
    topLeft = model->index(row, 0, QModelIndex());
    bottomRight = model->index(row, model->columnCount() - 1, QModelIndex());
    itemSelection.select(topLeft, bottomRight);
    itemSelectionModel->select(itemSelection, QItemSelectionModel::Select);

    // Set current item
    itemSelectionModel->setCurrentIndex(persistentModelIndex, QItemSelectionModel::Select);
  }

}

//==============================================================================
// Slots

/**
 *
 */
void ExplorerDock::changeCurrentDagPatternItem(const QModelIndex &current, const QModelIndex &previous) {

  // TODO use previous to build history
  Q_UNUSED(previous);

  // Get model index from selection (pattern index is stored in column 0)
  _currentPatternModelIndex = _patternsModel->index(current.row(), 0);


  if (_currentPatternModelIndex.isValid()) {
    // Get index value
    int index = _patternsModel->data(_currentPatternModelIndex).toInt();

    // Display selected pattern
    _mainWindow->displayDagPattern(index);
  }

}


/**
 * Selects the first pattern item in the model.
 */
void ExplorerDock::selectFirstDagPattern() {
  selectItemAtRow(_patternsModel,
                  _patternsSelectionModel,
                  _patternsItemSelection,
                  _currentPatternModelIndex,
                  0);
}


/**
 * Selects the last pattern item in the model.
 */
void ExplorerDock::selectLastDagPattern() {
  selectItemAtRow(_patternsModel,
                  _patternsSelectionModel,
                  _patternsItemSelection,
                  _currentPatternModelIndex,
                  _patternsModel->rowCount() - 1);
}


/**
 * Selects the pattern item before the current selected pattern.
 */
void ExplorerDock::selectPreviousDagPattern() {
  if (_currentPatternModelIndex.row() >= 1) {
    selectItemAtRow(_patternsModel,
                    _patternsSelectionModel,
                    _patternsItemSelection,
                    _currentPatternModelIndex,
                    _currentPatternModelIndex.row() - 1);
  }
}


/**
 * Selects the pattern item that follows the current selected pattern.
 */
void ExplorerDock::selectNextDagPattern() {

  // TODO faire que la fonction s'adate suivant le tab actif

  if (_currentPatternModelIndex.row() < _patternsModel->rowCount() - 1) {
    selectItemAtRow(_patternsModel,
                    _patternsSelectionModel,
                    _patternsItemSelection,
                    _currentPatternModelIndex,
                    _currentPatternModelIndex.row() + 1);
  }
}


/**
 * Finds the pattern associated with the given index, and selects it in the
 * model.
 *
 * @param index The DAG pattern index to select
 */
void ExplorerDock::selectDagPattern(int index) {

  // Look for the index value in the Index column (0)
  // Patterns indexes are unique, so there is only one found item.
  QList<QStandardItem*> foundItems = _patternsModel->findItems(QString::number(index),
                                                               Qt::MatchExactly,
                                                               0);

  // For security, check if there are found items, and proceed.
  if (foundItems.size() > 0) {
    // Get found item's model index and set as current model index
    _currentPatternModelIndex = foundItems[0]->index();
    selectItemAtRow(_patternsModel,
                    _patternsSelectionModel,
                    _patternsItemSelection,
                    _currentPatternModelIndex,
                    _currentPatternModelIndex.row());
  }


}

/**
 *
 */
void ExplorerDock::changeCurrentInputDagItem(const QModelIndex &current, const QModelIndex &previous) {

  // TODO use previous to build history
  Q_UNUSED(previous);

  // Get model index from selection (input index is stored in column 0)
  _currentInputModelIndex = _inputsModel->index(current.row(), 0);


  if (_currentInputModelIndex.isValid()) {
    // Get index value
    int index = _inputsModel->data(_currentInputModelIndex).toInt();

    // Display selected input dag
    _mainWindow->displayInputDag(index);
  }

}


// TODO indexation
void ExplorerDock::search() {
    /*
      faire la recherche : retourne une liste d'index de patterns
      vider la liste courante des patterns : _patternsModel
      remplir la liste avec le résultat de la recherche : il faut aller chercher dans DigDagData.getPatternsList chaque pattern
        voir fonction setDigDagData pour voir comment se remplit le modèle

        */


}
