#include "mainwindow.h"
#include "./ui_mainwindow.h"

#include "bucket_finder.h"
#include "finder.h"
#include "grouped_finder.h"
#include "linear_finder.h"
#include "parallel_finder.h"
#include "sorted_linear_finder.h"
#include "timer.h"
#include "tree_finder.h"

#include <sstream>

MainWindow::MainWindow(QWidget *parent, bool enableIncrementalSearch,
                       bool enableDevMode)
    : QMainWindow(parent), ui(new Ui::MainWindow),
      incremental_search_enabled_(enableIncrementalSearch) {
  ui->setupUi(this);

  ui->resultView->setModel(&result_model_);
  ui->wordListSourceSelector->setModel(&word_list_sources_);
  ui->searchAlgorithmSelector->setModel(&search_algorithms_);

  setDevMode(enableDevMode);

  setupAlgorithmSelector();
  setupWordListSourceSelector();
  setupWordList();
  createSelectedFinder();
}

MainWindow::~MainWindow() { delete ui; }

void MainWindow::setDevMode(bool enable) {
  ui->searchAlgorithmLabel->setVisible(enable);
  ui->searchAlgorithmSelector->setVisible(enable);

  ui->selectWordListLabel->setVisible(enable);
  ui->wordListSourceSelector->setVisible(enable);

  ui->selectWordListSizeLabel->setVisible(enable);
  ui->wordListSizeSelector->setVisible(enable);
}

void MainWindow::setupAlgorithmSelector() {
  search_algorithms_.appendRow(new QStandardItem("Bucket search"));
  search_algorithms_.appendRow(new QStandardItem("Linear search"));
  search_algorithms_.appendRow(new QStandardItem("Sorted linear search"));
  search_algorithms_.appendRow(new QStandardItem("Parallel search"));
  search_algorithms_.appendRow(new QStandardItem("Tree search"));
  search_algorithms_.appendRow(new QStandardItem("Grouped search"));
}

void MainWindow::setupWordListSourceSelector() {
  word_list_sources_.appendRow(new QStandardItem("All 4-caps words"));
  word_list_sources_.appendRow(new QStandardItem("English words"));
}

void MainWindow::setupWordList() {
  switch (ui->wordListSourceSelector->currentIndex()) {
  case 0:
  default:
    generateWordList();
    break;
  case 1:
    loadWordList("words.txt");
    break;
  }
}

void MainWindow::generateWordList() {
  Timer timer;
  word_list_ = WordList::fourCaps()
                   .multiply(ui->wordListSizeSelector->value())
                   .shuffle();
  timer.stop();

  std::stringstream status_message;
  status_message << "generated " << word_list_.size() << " words in " << timer;
  ui->mainStatusBar->showMessage(QString::fromStdString(status_message.str()));
}

void MainWindow::loadWordList(std::filesystem::path path) {
  Timer timer;
  word_list_ = WordList::fromFile("words.txt")
                   .multiply(ui->wordListSizeSelector->value())
                   .shuffle();
  timer.stop();

  std::stringstream status_message;
  status_message << "loaded " << word_list_.size() << " words in " << timer;
  ui->mainStatusBar->showMessage(QString::fromStdString(status_message.str()));
}

void MainWindow::search(const QString &search_term) {
  if (search_term.isEmpty()) {
    clearResults();
    return;
  }

  Timer timer;
  const auto results = finder_->find_prefix(search_term.toStdString());
  timer.stop();

  std::stringstream status_message;
  status_message << "search took " << timer << ", found " << results.size()
                 << " result(s) in " << word_list_.size() << " words.";
  ui->mainStatusBar->showMessage(QString::fromStdString(status_message.str()));

  showResults(results);
}

void MainWindow::createSelectedFinder() {
  auto selectedFinder = ui->searchAlgorithmSelector->currentIndex();

  Timer timer;
  switch (selectedFinder) {
  case 0:
  default:
    finder_ = std::make_unique<BucketFinder>(word_list_);
    break;
  case 1:
    finder_ = std::make_unique<LinearFinder>(word_list_);
    break;
  case 2:
    finder_ = std::make_unique<SortedLinearFinder>(word_list_);
    break;
  case 3:
    finder_ = std::make_unique<ParallelFinder>(word_list_);
    break;
  case 4:
    finder_ = std::make_unique<TreeFinder>(word_list_);
    break;
  case 5:
    finder_ = std::make_unique<GroupedFinder>(word_list_);
    break;
  }
  timer.stop();

  std::stringstream status_message;
  status_message << "finder initialized in " << timer << ", "
                 << word_list_.size() << " words loaded";
  ui->mainStatusBar->showMessage(QString::fromStdString(status_message.str()));
}

void MainWindow::showResults(const WordRefList &results) {
  QStringList ui_results;
  for (auto word : results) {
    ui_results.append(QString::fromStdString(*word));
  }

  result_model_.setStringList(ui_results);
}

void MainWindow::clearResults() { result_model_.setStringList({}); }

void MainWindow::on_searchInput_textChanged(const QString &search_term) {
  if (!incremental_search_enabled_) {
    return;
  }

  QGuiApplication::setOverrideCursor(Qt::WaitCursor);
  search(search_term);
  QGuiApplication::restoreOverrideCursor();
}

void MainWindow::on_searchAlgorithmSelector_currentIndexChanged(int) {
  QGuiApplication::setOverrideCursor(Qt::WaitCursor);
  clearResults();
  createSelectedFinder();
  QGuiApplication::restoreOverrideCursor();
}

void MainWindow::on_wordListSourceSelector_currentIndexChanged(int) {
  QGuiApplication::setOverrideCursor(Qt::WaitCursor);
  clearResults();
  setupWordList();
  createSelectedFinder();
  QGuiApplication::restoreOverrideCursor();
}

void MainWindow::on_wordListSizeSelector_valueChanged(int) {
  QGuiApplication::setOverrideCursor(Qt::WaitCursor);
  clearResults();
  setupWordList();
  createSelectedFinder();
  QGuiApplication::restoreOverrideCursor();
}

void MainWindow::on_searchInput_returnPressed() {
  QGuiApplication::setOverrideCursor(Qt::WaitCursor);
  search(ui->searchInput->displayText());
  QGuiApplication::restoreOverrideCursor();
}
