#include <graph/server/gui/chart/chart.h>
#include <graph/server/gui/mainwindow.h>
#include <graph/server/gui/specdialog/specdialog.h>
#include <graph/server/plugin.h>
#include <graph/server/spec.h>
#include <graph/server/ui_mainwindow.h>
#include <qlistwidget.h>
#include <QFileDialog>
#include <QMessageBox>
#include <iostream>
#include <stdexcept>

namespace Graph::GUI {

MainWindow* MainWindow::instance = nullptr;

MainWindow* MainWindow::getInstance(int argc, char** argv) {
  if (!instance)
    instance = new MainWindow(nullptr, argc, argv);

  return instance;
}

MainWindow* MainWindow::getInstance() {
  if (!instance)
    instance = new MainWindow();

  return instance;
}

void MainWindow::close() {
  SpecDialog::close();
  if (instance)
    delete instance;
}

void addData(unsigned int chartIndex, const char* seriesName, const point_t* points, unsigned long count) {
  MainWindow::getInstance()->addData(chartIndex, seriesName, points, count);
}

void MainWindow::addData(unsigned int chartIndex, const char* seriesName, const point_t* points, unsigned long count) {
  if (chartIndex >= charts.size())
    return;

  charts[chartIndex]->addData(seriesName, points, count);
}

MainWindow::MainWindow(QWidget* parent, int argc, char** argv) : QMainWindow(parent), ui(new Ui::MainWindow), argc(argc), argv(argv) {
  ui->setupUi(this);

  // plugins
  pluginModel = new PluginModel(this);
  ui->pluginsListView->setModel(pluginModel);

  // unload plugin
  connect(ui->pluginsListView, &QListView::doubleClicked, [&](const QModelIndex& index) {
    QMessageBox::StandardButton result = QMessageBox::question(this, "Unload plugin?", "Are you sure?", QMessageBox::Yes | QMessageBox::Cancel);

    if (result == QMessageBox::Yes) {
      pluginModel->removeRow(index.row());
      pluginModel->remove(index.row());
      ui->pluginsListView->doItemsLayout();
    }
  });

  fileDialog = new QFileDialog(this, "Load plugin", "./plugins", "Plugin (*.so *.dll *.dlsym)");
}

MainWindow::~MainWindow() {
  for (ChartWidget* chart : charts) {
    delete chart;
  }

  delete pluginModel;
  delete fileDialog;
}

void MainWindow::on_action_Add_triggered() {
  SpecDialog* dialog = SpecDialog::getInstance();
  if (dialog->exec() == SpecDialog::Rejected)
    return;

  ChartWidget* chart = new ChartWidget(ui->graphsListWidget, dialog->getSpec());

  charts.push_back(chart);

  QListWidgetItem* item = new QListWidgetItem();

  item->setSizeHint(chart->minimumSize());

  ui->graphsListWidget->addItem(item);
  ui->graphsListWidget->setItemWidget(item, chart);
}

void MainWindow::on_action_Load_triggered() {
  if (!fileDialog->exec())
    return;

  for (QString file : fileDialog->selectedFiles()) {
    bool loaded = false;
    for (Plugin* plugin : pluginModel->getPlugins()) {
      if (plugin->getPath() == file.toStdString()) {
        loaded = true;
      }
    }
    if (loaded) {
      QMessageBox::critical(this, "Error", "Plugin already loaded:\n" + file, QMessageBox::StandardButton::Ignore);
      continue;
    }

    try {
      Plugin* plugin = new Plugin(file.toStdString(), &Graph::GUI::addData, argc, argv);

      pluginModel->add(plugin);
      int row = pluginModel->rowCount();

      QThread* thread = new QThread();
      plugin->moveToThread(thread);
      connect(plugin, &Plugin::exited, this, [](int code) { std::cout << "plugin exited: " << code << std::endl; });
      connect(thread, &QThread::started, plugin, &Plugin::run);
      connect(plugin, &Plugin::exited, thread, &QThread::quit);
      connect(plugin, &Plugin::exited, this, [this, file, row](int code) {
        if (code != 0) {
          QMessageBox::critical(this, "Error", "Plugin exited with an error!\n" + file, QMessageBox::StandardButton::Ignore);
          pluginModel->remove(row - 1);
        }
      });
      connect(thread, &QThread::finished, thread, &QThread::deleteLater);
      thread->start();
    } catch (std::runtime_error& e) {
      std::cout << e.what() << std::endl;
      QMessageBox::critical(this, "Error", "Failed to load plugin!\n" + file, QMessageBox::StandardButton::Ignore);
    }

    int row = pluginModel->rowCount();
    pluginModel->insertRow(row);
    QModelIndex index = pluginModel->index(row);
    ui->pluginsListView->doItemsLayout();
    ui->pluginsListView->setCurrentIndex(index);
  }
}

void MainWindow::removeChart(ChartWidget* chartWidget) {
  for (int i = 0; i < ui->graphsListWidget->count(); i++) {
    ChartWidget* widget = (ChartWidget*)ui->graphsListWidget->itemWidget(ui->graphsListWidget->item(i));
    if (widget == chartWidget) {
      ui->graphsListWidget->takeItem(i);
      break;
    }
  }

  for (auto it = charts.begin(); it != charts.end(); it++) {
    if ((*it) == chartWidget) {
      charts.erase(it);
      delete chartWidget;
      break;
    }
  }
}

} // namespace Graph::GUI
