/**
 * @file MainWindow.hpp
 *
 * Created on 2010/05/XX by Anthony Nemoff
 * Last update 2010/06/11 by Anthony Nemoff
 *
 */

#include "MainWindow.hpp"
#include "ui_MainWindow.h"
#include "../gui/GraphicDag.hpp"

#include <QtGui>


/**
 * Custom message handler
 */
void messageHandler(QtMsgType type, const char *msg) {

  switch (type) {

  case QtDebugMsg:
    fprintf(stderr, "Debug: %s\n", msg);
    break;

  case QtWarningMsg:
    fprintf(stderr, "Warning: %s\n", msg);
    break;

  case QtCriticalMsg:
    fprintf(stderr, "Critical: %s\n", msg);
    break;

  case QtFatalMsg:
    fprintf(stderr, "Fatal: %s\n", msg);
    abort(); // XXX handle fatal error

  }

}


/**
 * Constructor
 */
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    _ui(new Ui::MainWindow),
    _graphicScene(0),
    _graphicView(0),
    _runDigDagDialog(0),
    _digDagData(0)
{

  // Install message handler
  qInstallMsgHandler(messageHandler);

  // Setup GUI
  _ui->setupUi(this);

  // Create scene
  _graphicScene = new QGraphicsScene(this);
  _graphicScene->setBackgroundBrush(Qt::white);

  // Create view and link scene
  _graphicView = new QGraphicsView(_graphicScene, this);
  _graphicView->setBackgroundBrush(Qt::white);
  _graphicView->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
  _graphicView->setDragMode(QGraphicsView::ScrollHandDrag);

  // Set view as central widget
  setCentralWidget(_graphicView);

  // Create explorer dock
  _explorerDock = new ExplorerDock(this);
  addDockWidget(Qt::LeftDockWidgetArea, _explorerDock);

  // Create Current DAG dock
  _currentDagDock = new CurrentDagDock(this);
  addDockWidget(Qt::RightDockWidgetArea, _currentDagDock);

  // Create TID list dock
  _tidListDock = new TidListDockWidget(this);
  addDockWidget(Qt::BottomDockWidgetArea, _tidListDock);

  // Create connections between GUI objects and slots
  // WARNING : ALL GUI OBJECTS MUST BE CREATED IN ORDER TO CONNECT THEM
  connectSlots();

}


/**
 * Destructor
 */
MainWindow::~MainWindow() {

  delete _explorerDock;
  delete _currentDagDock;
  delete _tidListDock;
  delete _graphicView;
  delete _graphicScene;

  if (_runDigDagDialog != 0)
    delete _runDigDagDialog;

  delete _digDagData;
  delete _ui;

}


//////////////////////////////////////////////////////////////////////////////
// METHODS

/**
 * @reimp
 */
void MainWindow::changeEvent(QEvent *e) {

  QMainWindow::changeEvent(e);

  switch (e->type()) {

  case QEvent::LanguageChange:
    _ui->retranslateUi(this);
    break;

  default:
    break;

  }
}


/**
 * Connects UI objects with their respective slots
 */
void MainWindow::connectSlots() {

  // --- File menu slots
  QObject::connect(_ui->actionRunDigDag, SIGNAL(triggered()), this, SLOT(runDigDag()));
  QObject::connect(_ui->actionOpenFile, SIGNAL(triggered()), this, SLOT(openFile()));
  QObject::connect(_ui->actionQuit, SIGNAL(triggered()), this, SLOT(quitApp()));

  // --- Navigation menu slots
  QObject::connect(_ui->actionPreviousDag, SIGNAL(triggered()), _explorerDock, SLOT(selectPreviousDagPattern()));
  QObject::connect(_ui->actionNextDag, SIGNAL(triggered()), _explorerDock, SLOT(selectNextDagPattern()));
  QObject::connect(_ui->actionJumpToDagPattern, SIGNAL(triggered()), this, SLOT(jumpToDagPattern()));

  // --- Display menu slots
  QObject::connect(_ui->actionShowToolbar, SIGNAL(toggled(bool)), this, SLOT(showToolbar(bool)));
  QObject::connect(_ui->actionShowDataPanel, SIGNAL(toggled(bool)), this, SLOT(showExplorerDock(bool)));
  QObject::connect(_ui->actionShowCurrentDagPanel, SIGNAL(toggled(bool)), this, SLOT(showDagPropertiesDock(bool)));
  QObject::connect(_ui->actionShowTidListBar, SIGNAL(toggled(bool)), this, SLOT(showTidListDock(bool)));
  QObject::connect(_ui->actionEnableAntialiasing, SIGNAL(toggled(bool)), this, SLOT(enableAntialiasing(bool)));

  // --- Tools menu slots
  // TODO

  // --- Help menu slots
  // TODO

  // --- Widgets slots
  // Closing docks checks/uncheks the related action menu item
  QObject::connect(_explorerDock, SIGNAL(visibilityChanged(bool)), _ui->actionShowDataPanel, SLOT(setChecked(bool)));
  QObject::connect(_currentDagDock, SIGNAL(visibilityChanged(bool)), _ui->actionShowCurrentDagPanel, SLOT(setChecked(bool)));
  QObject::connect(_tidListDock, SIGNAL(visibilityChanged(bool)), _ui->actionShowTidListBar, SLOT(setChecked(bool)));

}


/**
 * Displays the DAG pattern with the given index.
 * Default layout is "dot".
 *
 * @todo implement dynamic layout selection
 * @todo typeless display function : displayDag(input dag or dag pattern) ???
 */
void MainWindow::displayDagPattern(int patternIndex) {

  if (patternIndex >= 0 && patternIndex < _digDagData->getNumberOfDagPatterns()) {

    _ui->statusBar->showMessage(
        tr("Displaying DAG pattern %1 / %2")
        .arg(patternIndex)
        .arg(_digDagData->getNumberOfDagPatterns() - 1));

    // Clear scene (remove and delete all objects)
    _graphicScene->clear();

    // Load DAG Pattern
    _digDagData->loadDagPattern(patternIndex);

    // Get pattern
    DagPattern* dagPattern = _digDagData->getPatternsList()->at(patternIndex);

    // Calculate layout in model
    // TODO implement layout selection in menu, and get layout value
    dagPattern->getStructure()->calculateLayout("dot");
    //_digDagData->layoutDagStructure(patternIndex, "dot");

    // Build pattern view
    GraphicDag *graphicDag = new GraphicDag(dagPattern->getStructure());
    graphicDag->build(); // TODO useless ?

    // Update TID list dock contents
    _tidListDock->setTidList(dagPattern->getTidList());

    // TODO Ipdate current DAG dock contents
    _currentDagDock->showDagPattern(patternIndex);

    // Add the graph object object to the scene
    _graphicScene->addItem(graphicDag);

  }

}


/**
 * Displays the input DAG pattern with the given index.
 * Default layout is "dot".
 *
 * @todo implement dynamic layout selection
 */
void MainWindow::displayInputDag(int inputDagIndex) {

  // TODO factoriser code avec displayDagPattern ?

  if (inputDagIndex >= 0 && inputDagIndex < _digDagData->getNumberOfInputDags()) {

    _ui->statusBar->showMessage(
        tr("Displaying input DAG %1 / %2")
        .arg(inputDagIndex)
        .arg(_digDagData->getNumberOfInputDags() - 1));

    // Clear scene (remove and delete all objects)
    _graphicScene->clear();

    // Load DAG data
    if (_digDagData->loadInputDag(inputDagIndex)) {
      // Get input DAG
      InputDag* inputDag = _digDagData->getInputDagsList()->at(inputDagIndex);

      // Calculate layout in model
      // TODO implement layout selection in menu, and get layout value
      inputDag->getStructure()->calculateLayout("dot");

      // Build pattern view
      GraphicDag *graphicDag = new GraphicDag(inputDag->getStructure());
      graphicDag->build(); // TODO useless ?

      // TODO Update current DAG dock contents
      //_currentDagDock->showDagPattern(inputDagIndex);

      // Add the graph object object to the scene
      _graphicScene->addItem(graphicDag);
    }

  }

}


//////////////////////////////////////////////////////////////////////////////
// File menu slots

/**
 * Slot
 * Opens the "Run DigDag" dialog and runs DigDag with the given user settings if
 * accepted.
 */
void MainWindow::runDigDag() {

  if (_runDigDagDialog == 0)
    _runDigDagDialog = new RunDigDagDialog(this);

  // If dialog accepted, run DigDag
  if (_runDigDagDialog->exec()) {
    QString digDagPath = _runDigDagDialog->getDigDagPath();
    QString inputFolderName = _runDigDagDialog->getInputFolderName();
    QString sortFiles = _runDigDagDialog->isSortEnabled() ? "true" : "false";
    int minSupport = _runDigDagDialog->getMinSupport();
    int epThreshold = _runDigDagDialog->getEPThreshold();
    QString outputPrefix = _runDigDagDialog->getOutputPrefix();

    QStringList arguments;
    arguments << "-f" << "dags"
        << "-i" << inputFolderName
        << "-t" << QString::number(minSupport)
        << "-g" << QString::number(epThreshold)
        << "-o" << outputPrefix
        << "-z" << sortFiles
        << "-s" << "true";

    qDebug() << arguments;

    QProcess *digDagProcess = new QProcess(this);
    digDagProcess->start(digDagPath, arguments);

    QMessageBox::information(this,
                             "DigDag",
                             tr("Mining in progress ..."));

  }

}


/**
 * Slot
 * Opens a file chooser dialog to chose a DigDag file to open. File can be gzipped
 * or not. After a valid file is selected it is read and parsed to build the data.
 * When done it displays the first DAG pattern.
 */
void MainWindow::openFile() {

  // Clear scene
  _graphicScene->clear();

  if (_digDagData != NULL) {
    delete _digDagData;
  }

  _digDagData = new DigDagData();

  /* Progress dialog :
   * There are only a few progress steps, and each step % is equal. Step % don't
   * represent the length of the task. The objective is only to give the user
   * a feedback about progress. Too many progress steps would slow down the
   * reading process.
   * 0 : start
   * 1 : decompress (if opening a gzip file)
   * 2 : opening file
   * 3 : reading file content
   * 4 : filling GUI contents with file data
   * 5 : end
   */
  int nbProgressSteps = 5;
  QProgressDialog progressDialog(
      QObject::tr("Loading..."),
      QObject::tr("Cancel"),
      0,
      nbProgressSteps,
      this);
  progressDialog.setWindowModality(Qt::WindowModal);

  /// TODO create recent locations list , open last location
  QString startFolder = "../Output_1.5/";

  // File type filter
  QString FILTER_ALL = "All DigDag files (*.ddf *.ddf.gz)";
  QString FILTER_RAW = "Raw DigDag files (*.ddf)";
  QString FILTER_COMPRESSED = "Compressed DigDag files (*.ddf.gz)";
  QString fileTypeFilter = FILTER_ALL
                           .append(";;")
                           .append(FILTER_RAW)
                           .append(";;")
                           .append(FILTER_COMPRESSED);

  // Show file chooser dialog
  QString fileName = QFileDialog::getOpenFileName(
      this,
      QObject::tr("Open DigDag file"),
      startFolder,
      QObject::tr(fileTypeFilter.toStdString().c_str()));

  // If a valid file is selected ...
  if (!fileName.isEmpty()) {

    progressDialog.show();

    QFileInfo fileInfo(fileName);

    // If file is compressed...
    if (fileInfo.suffix() == "gz") {
      progressDialog.setLabelText(tr("Decompressing file..."));

      /* TODO copy gzip file to tmp directory :
        - decompress the gzip copy file
        - set file path to the copy path
        - decompress copied file
        - open copied file
        - delete copied file when closing file
        This way the original file stays gzipped
        */

      QStringList args;
      args << "-d" << fileName;
      int result = QProcess::execute("gzip", args);
      if (result != 0) {

        QMessageBox::critical(this, tr("Critical error"), tr("Unable to decompress gzip file."));
        // TODO Check variables before exiting function

        // Exit function
        return;

      } else {

        // File is now unzipped, with only ".ddf" suffix
        fileName = fileName.mid(0, fileName.indexOf(".gz"));

        progressDialog.setLabelText(tr("File decompressed. Opening raw file ..."));

      }

      progressDialog.setValue(1);
      // TODO test progress cancel

    }

    // Open file
    progressDialog.setLabelText(tr("Opening file..."));
    if (_digDagData->openFile(fileName) != DDV_OPEN_SUCESS) {
      QMessageBox::warning(this, tr("Warning"), tr("Failed to open file ").append(fileName).append("."));
      return;
    }
    progressDialog.setValue(2);
    // TODO test progress cancel

    // Read file
    progressDialog.setLabelText(tr("Reading file..."));
    int readResult = _digDagData->readFile();
    if (readResult != DDV_PARSE_SUCCESS) {
      switch(readResult) {
      case DDV_OPEN_CANCELED:
        // Opening canceled by user : exit function
        return;

      case DDV_WRONG_FILE_VERSION:
        // Wrong file format detected
        QMessageBox::warning(this, tr("Reading error"), tr("Wrong file version."));
        break;

      default :
          // TODO handle other errors
          QMessageBox::warning(this, tr("Reading error"), tr("Parsing failed."));
    } // end switch
    } // end if
    progressDialog.setValue(3);
    // TODO test progress cancel

    // Update explorer dock data
    progressDialog.setLabelText(tr("Filling explorer contents..."));
    _explorerDock->setDigDagData(_digDagData);
    progressDialog.setValue(4);
    // TODO test progress cancel

    // Give data pointer to Current DAG dock
    _currentDagDock->setDigDagData(_digDagData);

    // Initialize TID list dock with the input DAGs list
    _tidListDock->setInputDagsMap(_digDagData->getInputDagsMap());

    // Close progress dialog
    progressDialog.setValue(nbProgressSteps);
    // TODO test progress cancel

    // Select first pattern and display it
    _explorerDock->selectFirstDagPattern();

  } // end fileName.isEmpty

}


/**
 * Slot
 *
 */
void MainWindow::closeFile() {
  // TODO close file
}


/**
 * Slot
 *
 */
void MainWindow::closeAndCompressFile() {
  // TODO close & compress
}


/**
 * Slot
 *
 */
void MainWindow::exportDag() {
  // TODO exportDag
}


/**
 * Slot
 *
 */
void MainWindow::quitApp() {
  // TODO save settings & stuff before closing ...
  close();
}


//////////////////////////////////////////////////////////////////////////////
// Navigation menu slots



/**
 * Slot
 *
 */
void MainWindow::showAssociatedDag() {
  // TODO show related dag
}


/**
 * Slot
 * Prompts the user with a dialog to jump to a given index DAG pattern.
 * Index values belong to the range [first_index, last_index].
 */
void MainWindow::jumpToDagPattern() {

  // TODO gérer le cas où on jump vers un dag qui ne fait pas partie de la liste (dans le cas d'un filtrage)
  // TODO tester s'il y a des données!

  bool accepted;
  int minIndexValue = 0;
  int maxIndexValue = _digDagData->getNumberOfDagPatterns() - 1;
  int step = 1;

  // Show input dialog
  int selectedIndex = QInputDialog::getInt(
      this,
      tr("Jump to DAG pattern"),
      tr("Pattern index :"),
      _explorerDock->getCurrentDagPatternIndex(),
      minIndexValue,
      maxIndexValue,
      step,
      &accepted);

  // If dialog is accepted, select the pattern
  if (accepted) {
    _explorerDock->selectDagPattern(selectedIndex);
  }

}


//////////////////////////////////////////////////////////////////////////////
// Display menu slots

/**
 * Slot
 * Shows/hides all the toolbars.
 */
void MainWindow::showToolbar(bool isVisible) {
  _ui->fileToolBar->setVisible(isVisible);
  _ui->navigationToolBar->setVisible(isVisible);
}


/**
 * Slot
 * Shows/hides the explorer dock.
 */
void MainWindow::showExplorerDock(bool isVisible) {
  _explorerDock->setVisible(isVisible);
}


/**
 * Slot
 * Shows/hides the DAG properties dock.
 */
void MainWindow::showDagPropertiesDock(bool isVisible) {
  _currentDagDock->setVisible(isVisible);
}


/**
 * Slot
 * Shows/hides the TID list dock.
 */
void MainWindow::showTidListDock(bool isVisible) {
  _tidListDock->setVisible(isVisible);
}


/**
 * Slot
 * Enables/disables antialiasing on the graphic view.
 */
void MainWindow::enableAntialiasing(bool isEnabled) {
  if (isEnabled) {
    _graphicView->setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
  } else {
    _graphicView->setRenderHints(QPainter::TextAntialiasing);
  }
}


//////////////////////////////////////////////////////////////////////////////
// Tools menu slots
// TODO


//////////////////////////////////////////////////////////////////////////////
// Help menu slots
// TODO
