#include "MainWindow.hpp"
#include "ChessArbiter.hpp"
#include "UCI.hpp"
#include "engine_tab/EngineTab.hpp"
#include "pgnp.hpp"
#include "preferences/preferences.hpp"

wxDEFINE_EVENT(REFRESH_TAB_TITLE, wxCommandEvent);
wxDEFINE_EVENT(NEW_GAME_EVENT, wxCommandEvent);
wxDEFINE_EVENT(CLOSE_TAB_EVENT, wxCommandEvent);

/// ---------- MainWindow ----------

MainWindow::MainWindow()
    : wxFrame(NULL, wxID_ANY, "OChess: The Open Chess software",
              wxDefaultPosition, wxSize(1500, 1000)),
      prefsEditor(NULL) {
  CreateStatusBar();
  SetStatusText("OChess");

  /// File menu
  wxMenu *menuFile = new wxMenu;
  menuFile->Append(1, "Open", "Open file");
  Bind(wxEVT_MENU, &MainWindow::OnOpen, this, 1);
  menuFile->AppendSeparator();
  menuFile->Append(10, "Save", "Save current game");
  menuFile->Append(11, "Save As", "Save current game as");
  menuFile->AppendSeparator();
  menuFile->Append(4, "Settings", "Configure OChess");
  Bind(wxEVT_MENU, &MainWindow::OnSettings, this, 4);
  menuFile->AppendSeparator();
  menuFile->Append(wxID_EXIT);
  Bind(wxEVT_MENU, &MainWindow::OnExit, this, wxID_EXIT);

  // Game menu
  menuGame = new wxMenu;
  menuGame->Append(2, "New", "Create new game");
  Bind(wxEVT_MENU, &MainWindow::OnMenuNewGame, this, 2);
  menuGame->Append(3, "New from FEN", "Create new game using FEN");
  Bind(wxEVT_MENU, &MainWindow::OnMenuNewGame, this, 3);

  // Game base menu
  wxMenu *menuBase = new wxMenu;
  menuBase->Append(5, "New", "Create new database");

  // Engine menu
  wxMenu *engineMenu = new wxMenu;
  engineMenu->Append(6, "New", "Create a new engine configuration");
  Bind(wxEVT_MENU, &MainWindow::OnNewEngine, this, 6);

  /// Menu bar
  menuBar = new wxMenuBar;
  menuBar->Append(menuFile, "&File");
  menuBar->Append(menuGame, "&Game");
  menuBar->Append(menuBase, "&Database");
  menuBar->Append(engineMenu, "&Engines");
  SetMenuBar(menuBar);

  // Create the wxNotebook widget
  notebook = new wxAuiNotebook(this, wxID_ANY);
  NewGame(new Game());

  Bind(wxEVT_AUINOTEBOOK_PAGE_CHANGED, &MainWindow::OnPageChange, this,
       wxID_ANY);
  Bind(REFRESH_TAB_TITLE, &MainWindow::OnRefreshTabTitle, this, wxID_ANY);
  Bind(NEW_GAME_EVENT, &MainWindow::OnNewGame, this, wxID_ANY);
  Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnClose, this);
  Bind(CLOSE_TAB_EVENT, &MainWindow::OnCloseTabEvent, this, wxID_ANY);

  /*BaseTab *bt = new BaseTab((wxFrame *)notebook,
  "/home/loic/hartwig_tests.pgn"); notebook->AddPage(bt, bt->GetLabel());
  notebook->SetSelection(notebook->GetPageIndex(bt));*/
/*
  EngineTab *bt =
      new EngineTab((wxWindow *)notebook,
                    new uciadapter::UCI("/home/loic/.local/bin/stockfish"),
                    "/home/loic/.local/bin/stockfish");
  notebook->AddPage(bt, bt->GetLabel());
  notebook->SetSelection(notebook->GetPageIndex(bt));*/
}

void MainWindow::OnCloseTabEvent(wxCommandEvent &event) {
  notebook->DeletePage(notebook->GetSelection());
}

void MainWindow::OnNewEngine(wxCommandEvent &event) {
  wxFileDialog openFileDialog(this, _("Use engine"), "", "", "Executable|*",
                              wxFD_OPEN | wxFD_FILE_MUST_EXIST);
  if (openFileDialog.ShowModal() != wxID_CANCEL) {
    std::string path = openFileDialog.GetPath().ToStdString();
    uciadapter::UCI *engine;
    try {
      engine = new uciadapter::UCI(path);
      EngineTab *bt = new EngineTab((wxWindow *)notebook, engine, path);
      notebook->AddPage(bt, bt->GetLabel());
      notebook->SetSelection(notebook->GetPageIndex(bt));
    } catch (...) {
      SHOW_DIALOG_ERROR("Could not communicate with the engine");
    }
  }
}

void MainWindow::OnSettings(wxCommandEvent &event) {
  if (prefsEditor != NULL) {
    delete prefsEditor;
  }
  prefsEditor = new wxPreferencesEditor("Preferences");
  prefsEditor->AddPage(new BoardPrefs());
  prefsEditor->AddPage(new EditorPrefs());
  prefsEditor->Show(this);
}

void MainWindow::ApplyPreferences() {
  for (int i = 0; i < notebook->GetPageCount(); i++) {
    TabInfos *infos = dynamic_cast<TabInfos *>(notebook->GetPage(i));
    infos->ApplyPreferences();
  }
}

void MainWindow::OnExit(wxCommandEvent &event) { Close(true); }

std::vector<TabInfos *> MainWindow::ListTabInfos() {
  std::vector<TabInfos *> tinfos;
  for (int i = 0; i < notebook->GetPageCount(); i++) {
    tinfos.push_back(dynamic_cast<TabInfos *>(notebook->GetPage(i)));
  }
  return (tinfos);
}

void MainWindow::OnClose(wxCloseEvent &e) {
  if (prefsEditor != NULL) {
    prefsEditor->Dismiss();
  }
  e.Skip();
}

void MainWindow::OnOpen(wxCommandEvent &event) {
  wxFileDialog openFileDialog(this, _("Open file"), "", "",
                              "PGN files (*.pgn)|*.pgn",
                              wxFD_OPEN | wxFD_FILE_MUST_EXIST);
  if (openFileDialog.ShowModal() != wxID_CANCEL) {
    std::string path = openFileDialog.GetPath().ToStdString();
    // Test base tab
    BaseTab *bt = new BaseTab((wxFrame *)notebook, path);
    notebook->AddPage(bt, bt->GetLabel());
    notebook->SetSelection(notebook->GetPageIndex(bt));
  }
}

void MainWindow::OnMenuNewGame(wxCommandEvent &event) {
  if (event.GetId() == 3) {
    wxTextEntryDialog *dial =
        new wxTextEntryDialog(NULL, wxT("Enter FEN:"), wxT("Error"));
    if (dial->ShowModal() == wxID_OK) {
      try {
        NewGame(new Game(dial->GetValue().ToStdString()));
      } catch (...) {
        SHOW_DIALOG_ERROR("Invalid FEN");
      }
    }
  } else {
    NewGame(new Game());
  }
}

void MainWindow::OnNewGame(wxCommandEvent &event) {
  Game *g = (Game *)event.GetClientData();
  NewGame(g);
}

void MainWindow::OnPageChange(wxAuiNotebookEvent &event) {
  TabInfos *infos = dynamic_cast<TabInfos *>(notebook->GetCurrentPage());
  if (infos->type != TabInfos::GAME) {
    for (short i = 10; i < 20; i++) {
      if (menuGame->FindChildItem(i) != NULL) {
        menuGame->Enable(i, false);
      }
    }
  }
}

void MainWindow::OnRefreshTabTitle(wxCommandEvent &event) {
  GameTab *win = dynamic_cast<GameTab *>(event.GetEventObject());
  int page = notebook->GetPageIndex(win);
  if (page != wxNOT_FOUND) {
    notebook->SetPageText(page, win->GetLabel());
  }
}

void MainWindow::NewGame(Game *game) {
  GameTab *gt = new GameTab((wxFrame *)notebook, game);
  notebook->AddPage(gt, gt->GetLabel());
  notebook->SetSelection(notebook->GetPageIndex(gt));
}