#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);
wxDEFINE_EVENT(REFRESH_ENGINE_LIST, wxCommandEvent);
wxDEFINE_EVENT(CLOSE_LINKED_TAB, wxCommandEvent);


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

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

  /// File menu
  menu_file->Append(1, "Open", "Open file");
  menu_file->AppendSeparator();
  menu_file->Append(10, "Save", "Save current game");
  menu_file->Append(11, "Save As", "Save current game as");
  menu_file->AppendSeparator();
  menu_file->Append(4, "Settings", "Configure OChess");
  menu_file->AppendSeparator();
  menu_file->Append(wxID_EXIT);

  // Game menu
  menu_game->Append(2, "New", "Create new game");
  menu_game->Append(3, "New from FEN", "Create new game using FEN");

  // Game base menu
  menu_db->Append(7, "New", "Create database");
  menu_db->Append(5, "Open", "Open database");

  // Engine menu
  menu_engine->Append(6, "New", "Create a new engine configuration");
  manageMenu = new wxMenu;
  menu_engine->AppendSubMenu(manageMenu, "Manage");
  wxCommandEvent dummy(REFRESH_ENGINE_LIST, GetId());
  OnRefreshEngineList(dummy);

  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);
  Bind(wxEVT_MENU, &MainWindow::OnMenuItemClick, this, wxID_ANY);
  Bind(REFRESH_ENGINE_LIST, &MainWindow::OnRefreshEngineList, this, wxID_ANY);
  Bind(CLOSE_LINKED_TAB, &MainWindow::OnCloseTabLinkedTo, this, wxID_ANY);
  Bind(wxEVT_AUINOTEBOOK_PAGE_CLOSED, &MainWindow::OnAuiNotebookPageClosed, this, wxID_ANY);

  // Add new game tab by default
  NewGame(std::shared_ptr<Game>(new Game()));

  // Temporary TO REMOVE JUST FOR TESTS:
  //BaseTab *bt = new BaseTab((wxFrame *)notebook, "/home/loic/jean.pgn");
  //this->AddPage(bt,bt);
/*
  bt = new BaseTab((wxFrame *)notebook, "/home/loic/pgn/Milov.pgn");
  this->AddPage(bt,bt);*/
}

void MainWindow::AddPage(wxWindow* window, TabInfos* infos){
  window->SetClientData(infos);
  notebook->AddPage(window, window->GetLabel());
  notebook->SetSelection(notebook->GetPageIndex(window));
  // Refresh tab that require knowledge on other tabs
  for(auto i: wxGetApp().ListTabInfos()){i->Refresh();}
}

void MainWindow::OnAuiNotebookPageClosed(wxAuiNotebookEvent& event){
  // Refresh tab that require knowledge on other tabs
  for(auto i: wxGetApp().ListTabInfos()){i->Refresh();}
}

void MainWindow::OnCloseTabEvent(wxCommandEvent &event) {
  notebook->DeletePage(notebook->GetSelection());
  // Refresh tab that require knowledge on other tabs
  for(auto i: wxGetApp().ListTabInfos()){i->Refresh();}
}

void MainWindow::OnCloseTabLinkedTo(wxCommandEvent &event){
  TabInfos *infosEvent=(TabInfos*)event.GetClientData();
  // Now close all tabs in the notebook related to the one in the event
  int i=0;
  while(i<notebook->GetPageCount()){
    wxWindow *page=notebook->GetPage(i);
    TabInfos* infos=(TabInfos*)page->GetClientData();
    if(infos->is_linked && infos->linked_id==infosEvent->id){
      notebook->DeletePage(i); // Remove page
      i=0; // Restart to page 0 since notebook updated (we just remove one page)
    }
    else
      i++;
  }
}

void MainWindow::OnMenuItemClick(wxCommandEvent &event) {
  std::uint32_t id = event.GetId();
  if (id == wxID_EXIT) {
    Close(true);
  } else if (id >= 100) {
    wxLogDebug("Engine selected!");
    wxMenuItemList items = manageMenu->GetMenuItems();
    for (wxMenuItem *item : items) {
      if (item->GetId() == id) {
        wxLogDebug("Selected %s", item->GetItemLabel());
        EngineTab *bt = new EngineTab((wxWindow *)notebook,
                                      item->GetItemLabel().ToStdString());
        notebook->AddPage(bt, bt->GetLabel());
        notebook->SetSelection(notebook->GetPageIndex(bt));
      }
    }
  } else if (id == 1) {
    OpenFile();
  } else if (id == 2) {
    NewGame(false);
  } else if (id == 3) {
    NewGame(true);
  } else if (id == 4) {
    OpenSettings();
  } else if (id == 5) {
    OpenFile();
  } else if (id == 6) {
    NewEngine();
  } else if (id == 7) {
    wxFileDialog 
        newFileDialog(this, _("Create database file"), "", "",
                       "PGN files (*.pgn)|*.pgn", wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
    if (newFileDialog.ShowModal() == wxID_CANCEL)
        return;
    // Create and open new db
    std::string path = newFileDialog.GetPath().ToStdString();
    BaseTab *bt = new BaseTab((wxFrame *)notebook, path);
    AddPage(bt,bt);
  }
}

void MainWindow::OnRefreshEngineList(wxCommandEvent &event) {
  // Delete all items
  wxMenuItemList items = manageMenu->GetMenuItems();
  for (wxMenuItem *item : items) {
    manageMenu->Delete(item->GetId());
  }
  // Refresh items
  CONFIG_OPEN(conf);
  conf->SetPath("engines/");
  wxString engine_name;
  long index;
  if (conf->GetFirstGroup(engine_name, index)) {
    std::uint32_t id = 0;
    do {
      manageMenu->Append(100 + id, engine_name, "Configure " + engine_name);
      id++;
    } while (conf->GetNextGroup(engine_name, index));
  }
  CONFIG_CLOSE(conf);
  ApplyPreferences(); // Propagate motifications
}

void MainWindow::NewEngine() {
  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);
      AddPage(bt,bt);
    } catch (...) {
      SHOW_DIALOG_ERROR("Could not communicate with the engine");
    }
  }
}

void MainWindow::OpenSettings() {
  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::OnClose(wxCloseEvent &e) {
  if (prefsEditor != NULL) {
    prefsEditor->Dismiss();
  }
  e.Skip();
}


void MainWindow::OpenFile() {
  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);
    AddPage(bt,bt);
  }
}

void MainWindow::NewGame(bool useFen) {
  if (useFen) {
    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(std::shared_ptr<Game>(new Game()));
  }
}

void MainWindow::OnNewGame(wxCommandEvent &event) {
  TabInfos *tab = (TabInfos*)event.GetClientData();
  TabInfos *i=NewGame(tab->GetGame());
  i->Link(tab);
}

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 (menu_game->FindChildItem(i) != NULL) {
        menu_game->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());
  }
}

TabInfos* MainWindow::NewGame(std::shared_ptr<Game> game) {
  GameTab *gt = new GameTab((wxFrame *)notebook, game);
  this->AddPage(gt,gt);
  return(gt);
}