#include "tracker_frame.h"
#include <fmt/core.h>
#include <wx/aboutdlg.h>
#include <wx/choicebk.h>
#include <wx/filedlg.h>
#include <wx/notebook.h>
#include <wx/stdpaths.h>
#include <wx/webrequest.h>
#include <nlohmann/json.hpp>
#include <sstream>
#include "achievements_pane.h"
#include "ap_state.h"
#include "connection_dialog.h"
#include "ipc_dialog.h"
#include "ipc_state.h"
#include "settings_dialog.h"
#include "subway_map.h"
#include "tracker_config.h"
#include "tracker_panel.h"
#include "version.h"
namespace {
std::string GetStatusMessage() {
std::string msg = AP_GetStatusMessage();
std::optional<std::string> ipc_msg = IPC_GetStatusMessage();
if (ipc_msg) {
msg += " ";
msg += *ipc_msg;
}
return msg;
}
} // namespace
enum TrackerFrameIds {
ID_AP_CONNECT = 1,
ID_CHECK_FOR_UPDATES = 2,
ID_SETTINGS = 3,
ID_ZOOM_IN = 4,
ID_ZOOM_OUT = 5,
ID_OPEN_SAVE_FILE = 6,
ID_IPC_CONNECT = 7,
};
wxDEFINE_EVENT(STATE_RESET, wxCommandEvent);
wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(REDRAW_POSITION, wxCommandEvent);
wxDEFINE_EVENT(CONNECT_TO_AP, ApConnectEvent);
TrackerFrame::TrackerFrame()
: wxFrame(nullptr, wxID_ANY, "Lingo Archipelago Tracker", wxDefaultPosition,
wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxFULL_REPAINT_ON_RESIZE) {
::wxInitAllImageHandlers();
AP_SetTrackerFrame(this);
IPC_SetTrackerFrame(this);
wxMenu *menuFile = new wxMenu();
menuFile->Append(ID_AP_CONNECT, "&Connect to Archipelago");
menuFile->Append(ID_IPC_CONNECT, "&Connect to Lingo");
menuFile->Append(ID_OPEN_SAVE_FILE, "&Open Save Data\tCtrl-O");
menuFile->Append(ID_SETTINGS, "&Settings");
menuFile->Append(wxID_EXIT);
wxMenu *menuView = new wxMenu();
zoom_in_menu_item_ = menuView->Append(ID_ZOOM_IN, "Zoom In\tCtrl-+");
zoom_out_menu_item_ = menuView->Append(ID_ZOOM_OUT, "Zoom Out\tCtrl--");
zoom_in_menu_item_->Enable(false);
zoom_out_menu_item_->Enable(false);
wxMenu *menuHelp = new wxMenu();
menuHelp->Append(wxID_ABOUT);
menuHelp->Append(ID_CHECK_FOR_UPDATES, "Check for Updates");
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(menuFile, "&File");
menuBar->Append(menuView, "&View");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
CreateStatusBar();
Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT);
Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT);
Bind(wxEVT_MENU, &TrackerFrame::OnApConnect, this, ID_AP_CONNECT);
Bind(wxEVT_MENU, &TrackerFrame::OnIpcConnect, this, ID_IPC_CONNECT);
Bind(wxEVT_MENU, &TrackerFrame::OnSettings, this, ID_SETTINGS);
Bind(wxEVT_MENU, &TrackerFrame::OnCheckForUpdates, this,
ID_CHECK_FOR_UPDATES);
Bind(wxEVT_MENU, &TrackerFrame::OnZoomIn, this, ID_ZOOM_IN);
Bind(wxEVT_MENU, &TrackerFrame::OnZoomOut, this, ID_ZOOM_OUT);
Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &TrackerFrame::OnChangePage, this);
Bind(wxEVT_MENU, &TrackerFrame::OnOpenFile, this, ID_OPEN_SAVE_FILE);
Bind(STATE_RESET, &TrackerFrame::OnStateReset, this);
Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this);
Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this);
Bind(REDRAW_POSITION, &TrackerFrame::OnRedrawPosition, this);
Bind(CONNECT_TO_AP, &TrackerFrame::OnConnectToAp, this);
wxChoicebook *choicebook = new wxChoicebook(this, wxID_ANY);
achievements_pane_ = new AchievementsPane(choicebook);
choicebook->AddPage(achievements_pane_, "Achievements");
notebook_ = new wxNotebook(this, wxID_ANY);
tracker_panel_ = new TrackerPanel(notebook_);
subway_map_ = new SubwayMap(notebook_);
notebook_->AddPage(tracker_panel_, "Map");
notebook_->AddPage(subway_map_, "Subway");
wxBoxSizer *top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(choicebook, wxSizerFlags().Expand().Proportion(1));
top_sizer->Add(notebook_, wxSizerFlags().Expand().Proportion(3));
SetSizerAndFit(top_sizer);
SetSize(1280, 728);
if (!GetTrackerConfig().asked_to_check_for_updates) {
GetTrackerConfig().asked_to_check_for_updates = true;
if (wxMessageBox(
"Check for updates automatically when the tracker is opened?",
"Lingo AP Tracker", wxYES_NO) == wxYES) {
GetTrackerConfig().should_check_for_updates = true;
} else {
GetTrackerConfig().should_check_for_updates = false;
}
GetTrackerConfig().Save();
}
if (GetTrackerConfig().should_check_for_updates) {
CheckForUpdates(/*manual=*/false);
}
SetStatusText(GetStatusMessage());
}
void TrackerFrame::ConnectToAp(std::string server, std::string user,
std::string pass) {
QueueEvent(new ApConnectEvent(CONNECT_TO_AP, GetId(), std::move(server),
std::move(user), std::move(pass)));
}
void TrackerFrame::UpdateStatusMessage() {
QueueEvent(new wxCommandEvent(STATUS_CHANGED));
}
void TrackerFrame::ResetIndicators() {
QueueEvent(new wxCommandEvent(STATE_RESET));
}
void TrackerFrame::UpdateIndicators(UpdateIndicatorsMode mode) {
auto evt = new wxCommandEvent(STATE_CHANGED);
evt->SetInt(static_cast<int>(mode));
QueueEvent(evt);
}
void TrackerFrame::RedrawPosition() {
QueueEvent(new wxCommandEvent(REDRAW_POSITION));
}
void TrackerFrame::OnAbout(wxCommandEvent &event) {
wxAboutDialogInfo about_info;
about_info.SetName("Lingo Archipelago Tracker");
about_info.SetVersion(kTrackerVersion.ToString());
about_info.AddDeveloper("hatkirby");
about_info.AddDeveloper("art0007i");
about_info.AddArtist("Brenton Wildes");
about_info.AddArtist("kinrah");
wxAboutBox(about_info);
}
void TrackerFrame::OnExit(wxCommandEvent &event) { Close(true); }
void TrackerFrame::OnApConnect(wxCommandEvent &event) {
ConnectionDialog dlg;
if (dlg.ShowModal() == wxID_OK) {
GetTrackerConfig().connection_details.ap_server = dlg.GetServerValue();
GetTrackerConfig().connection_details.ap_player = dlg.GetPlayerValue();
GetTrackerConfig().connection_details.ap_password = dlg.GetPasswordValue();
std::deque<ConnectionDetails> new_history;
new_history.push_back(GetTrackerConfig().connection_details);
for (const ConnectionDetails &details :
GetTrackerConfig().connection_history) {
if (details != GetTrackerConfig().connection_details) {
new_history.push_back(details);
}
}
while (new_history.size() > 10) {
new_history.pop_back();
}
GetTrackerConfig().connection_history = std::move(new_history);
GetTrackerConfig().Save();
AP_Connect(dlg.GetServerValue(), dlg.GetPlayerValue(),
dlg.GetPasswordValue());
}
}
void TrackerFrame::OnIpcConnect(wxCommandEvent &event) {
IpcDialog dlg;
if (dlg.ShowModal() == wxID_OK) {
GetTrackerConfig().ipc_address = dlg.GetIpcAddress();
GetTrackerConfig().Save();
IPC_Connect(dlg.GetIpcAddress());
}
}
void TrackerFrame::OnSettings(wxCommandEvent &event) {
SettingsDialog dlg;
if (dlg.ShowModal() == wxID_OK) {
GetTrackerConfig().should_check_for_updates =
dlg.GetShouldCheckForUpdates();
GetTrackerConfig().hybrid_areas = dlg.GetHybridAreas();
GetTrackerConfig().show_hunt_panels = dlg.GetShowHuntPanels();
GetTrackerConfig().Save();
UpdateIndicators();
}
}
void TrackerFrame::OnCheckForUpdates(wxCommandEvent &event) {
CheckForUpdates(/*manual=*/true);
}
void TrackerFrame::OnZoomIn(wxCommandEvent &event) {
if (notebook_->GetSelection() == 1) {
subway_map_->Zoom(true);
}
}
void TrackerFrame::OnZoomOut(wxCommandEvent &event) {
if (notebook_->GetSelection() == 1) {
subway_map_->Zoom(false);
}
}
void TrackerFrame::OnChangePage(wxBookCtrlEvent &event) {
zoom_in_menu_item_->Enable(event.GetSelection() == 1);
zoom_out_menu_item_->Enable(event.GetSelection() == 1);
}
void TrackerFrame::OnOpenFile(wxCommandEvent &event) {
wxFileDialog open_file_dialog(
this, "Open Lingo Save File",
fmt::format("{}\\Godot\\app_userdata\\Lingo\\level1_stable",
wxStandardPaths::Get().GetUserConfigDir().ToStdString()),
AP_GetSaveName(), "Lingo save file (*.save)|*.save",
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (open_file_dialog.ShowModal() == wxID_CANCEL) {
return;
}
std::string savedata_path = open_file_dialog.GetPath().ToStdString();
if (panels_panel_ == nullptr) {
panels_panel_ = new TrackerPanel(notebook_);
notebook_->AddPage(panels_panel_, "Panels");
}
notebook_->SetSelection(notebook_->FindPage(panels_panel_));
panels_panel_->SetSavedataPath(savedata_path);
}
void TrackerFrame::OnStateReset(wxCommandEvent &event) {
tracker_panel_->UpdateIndicators();
achievements_pane_->UpdateIndicators();
subway_map_->OnConnect();
if (panels_panel_ != nullptr) {
notebook_->DeletePage(notebook_->FindPage(panels_panel_));
panels_panel_ = nullptr;
}
Refresh();
}
void TrackerFrame::OnStateChanged(wxCommandEvent &event) {
UpdateIndicatorsMode mode = static_cast<UpdateIndicatorsMode>(event.GetInt());
if (mode == kUPDATE_ALL_INDICATORS) {
tracker_panel_->UpdateIndicators();
achievements_pane_->UpdateIndicators();
subway_map_->UpdateIndicators();
if (panels_panel_ != nullptr) {
panels_panel_->UpdateIndicators();
}
Refresh();
} else if (mode == kUPDATE_ONLY_PANELS) {
if (panels_panel_ == nullptr) {
panels_panel_ = new TrackerPanel(notebook_);
panels_panel_->SetPanelsMode();
notebook_->AddPage(panels_panel_, "Panels");
}
panels_panel_->UpdateIndicators();
if (notebook_->GetSelection() == 2) {
Refresh();
}
}
}
void TrackerFrame::OnStatusChanged(wxCommandEvent &event) {
SetStatusText(GetStatusMessage());
}
void TrackerFrame::OnRedrawPosition(wxCommandEvent &event) {
if (notebook_->GetSelection() == 0) {
tracker_panel_->Refresh();
} else if (notebook_->GetSelection() == 2) {
panels_panel_->Refresh();
}
}
void TrackerFrame::OnConnectToAp(ApConnectEvent &event) {
AP_Connect(event.GetServer(), event.GetUser(), event.GetPass());
}
void TrackerFrame::CheckForUpdates(bool manual) {
wxWebRequest request = wxWebSession::GetDefault().CreateRequest(
this, "https://code.fourisland.com/lingo-ap-tracker/plain/VERSION");
if (!request.IsOk()) {
if (manual) {
wxMessageBox("Could not check for updates.", "Error",
wxOK | wxICON_ERROR);
} else {
SetStatusText("Could not check for updates.");
}
return;
}
Bind(wxEVT_WEBREQUEST_STATE, [this, manual](wxWebRequestEvent &evt) {
if (evt.GetState() == wxWebRequest::State_Completed) {
std::string response = evt.GetResponse().AsString().ToStdString();
Version latest_version(response);
if (kTrackerVersion < latest_version) {
std::ostringstream message_text;
message_text << "There is a newer version of Lingo AP Tracker "
"available. You have "
<< kTrackerVersion.ToString()
<< ", and the latest version is "
<< latest_version.ToString()
<< ". Would you like to update?";
if (wxMessageBox(message_text.str(), "Update available", wxYES_NO) ==
wxYES) {
wxLaunchDefaultBrowser(
"https://code.fourisland.com/lingo-ap-tracker/about/"
"CHANGELOG.md");
}
} else if (manual) {
wxMessageBox("Lingo AP Tracker is up to date!", "Lingo AP Tracker",
wxOK);
}
} else if (evt.GetState() == wxWebRequest::State_Failed) {
if (manual) {
wxMessageBox("Could not check for updates.", "Error",
wxOK | wxICON_ERROR);
} else {
SetStatusText("Could not check for updates.");
}
}
});
request.Start();
}