#include <fstream>

#include <config.hpp>
#include <args.hpp>
#include <paths.hpp>
#include <modellist.hpp>
#include <configfile.hpp>

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/spinctrl.h>

class ConfigurationApp : public wxApp {
	public:
		virtual bool OnInit();
};

class ConfigurationFrame : public wxFrame {
	public:
		ConfigurationFrame();
	private:
		std::vector<std::string> modelVec;
		wxCheckBox* useHaarCheckBox;
		wxCheckBox* showCameraCheckBox;
		wxCheckBox* noEyesCheckBox;
		wxChoice* modelNameChoice;
		wxSpinCtrl* cameraSpinCtrl;
		void loadExistingConfig();	// loads existing config file and populates controls
		void OnApply(wxCommandEvent& event);
		void OnExit(wxCommandEvent& event);
};

wxIMPLEMENT_APP(ConfigurationApp);

bool ConfigurationApp::OnInit() {
	initPrefixes();
	makePaths();

	ConfigurationFrame* frame = new ConfigurationFrame();
	frame->Show(true);
	return true;
}

ConfigurationFrame::ConfigurationFrame() : wxFrame(NULL, wxID_ANY, "Configure " PROJECT_NAME) {
	// find models to populate model dropdown
	modelVec = listModels();
	wxString modelArray[modelVec.size()];
	for (int i = 0; i < modelVec.size(); i++) {
		modelArray[i] = wxString(modelVec[i]);
	}

	wxPanel* panel = new wxPanel(this);

	// define all controls and labels
	useHaarCheckBox = new wxCheckBox(panel, wxID_ANY, "Disable DNN face detection");
	showCameraCheckBox = new wxCheckBox(panel, wxID_ANY, "Show camera feed");
	noEyesCheckBox = new wxCheckBox(panel, wxID_ANY, "Disable eye tracking");
	wxStaticText* modelNameText = new wxStaticText(panel, wxID_ANY, "Model name:");
	modelNameChoice = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, modelVec.size(), modelArray);
	wxStaticText* cameraText = new wxStaticText(panel, wxID_ANY, "Camera ID:");
	cameraSpinCtrl = new wxSpinCtrl(panel);

	wxBoxSizer* modelSizer = new wxBoxSizer(wxHORIZONTAL); // need to put label next to dropdown
	modelSizer->Add(modelNameText, 0, wxALIGN_CENTER | wxALL, 5);
	modelSizer->Add(modelNameChoice, 0, wxLEFT, 20);

	wxBoxSizer* cameraSizer = new wxBoxSizer(wxHORIZONTAL);
	cameraSizer->Add(cameraText, 0, wxALIGN_CENTER | wxALL, 5);
	cameraSizer->Add(cameraSpinCtrl, 0, wxLEFT, 20);


	// define tooltips
	useHaarCheckBox->SetToolTip("Use Haar Cascades for faster (but less accurate) face detection.");
	showCameraCheckBox->SetToolTip("Show the camera feed in another window.");
	noEyesCheckBox->SetToolTip("Disable eye tracking for better performance.");
	modelNameText->SetToolTip("Name of model to use.");
	cameraText->SetToolTip("ID number of camera to use. (e.g. /dev/videoXX where XX is the ID)");


	// General section
	wxStaticBoxSizer* generalBoxSizer = new wxStaticBoxSizer(new wxStaticBox(panel, wxID_ANY, "General"), wxVERTICAL);
	generalBoxSizer->Add(modelSizer, 0, wxALL, 5);
	generalBoxSizer->Add(cameraSizer, 0, wxALL, 5);
	generalBoxSizer->Add(showCameraCheckBox, 0, wxALL, 5);

	// Performance section
	wxStaticBoxSizer* performanceBoxSizer = new wxStaticBoxSizer(new wxStaticBox(panel, wxID_ANY, "Performance"), wxVERTICAL);
	performanceBoxSizer->Add(useHaarCheckBox, 0, wxALL, 5);
	performanceBoxSizer->Add(noEyesCheckBox, 0, wxALL, 5);

	// define bottom buttons (Cancel and Apply) and their sizer
	wxButton* cancelButton = new wxButton(panel, wxID_CANCEL, "Cancel");
	wxButton* applyButton = new wxButton(panel, wxID_APPLY, "Apply settings");
	wxBoxSizer* bottomButtonSizer = new wxBoxSizer(wxHORIZONTAL);
	bottomButtonSizer->Add(cancelButton, 0, wxALL, 5);
	bottomButtonSizer->Add(applyButton, 0, wxALL, 5);

	// define main sizer
	wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
	sizer->Add(generalBoxSizer, 0, wxALL | wxALIGN_LEFT | wxEXPAND, 10);
	sizer->Add(performanceBoxSizer, 0, wxALL | wxALIGN_LEFT | wxEXPAND, 10);
	sizer->AddStretchSpacer(1);
	sizer->Add(bottomButtonSizer, 0, wxALIGN_RIGHT | wxALL, 10);

	panel->SetSizer(sizer);

	// define top sizer which only contains the panel
	// a panel is needed for proper background on Microsoft Windows
	wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
	topSizer->Add(panel, 1, wxEXPAND);

	SetSizerAndFit(topSizer);

	Bind(wxEVT_BUTTON, &ConfigurationFrame::OnApply, this, wxID_APPLY);
	Bind(wxEVT_BUTTON, &ConfigurationFrame::OnExit, this, wxID_CANCEL);

	loadExistingConfig();
}

void ConfigurationFrame::loadExistingConfig() {
	struct optData configData = {
		false,
		false,
		false,
		"",
		0
	};
	configFileOpen(&configData);

	useHaarCheckBox->SetValue(configData.useHaar);
	showCameraCheckBox->SetValue(configData.showCamera);
	noEyesCheckBox->SetValue(configData.noEyes);
	for (int i = 0; i < modelVec.size(); i++) {
		if (modelVec[i] == configData.model) {
			modelNameChoice->SetSelection(i);
			break;
		}
	}
	cameraSpinCtrl->SetValue(configData.minCameraID);
}

void ConfigurationFrame::OnApply(wxCommandEvent& event) {
	// write options to config file
	std::ofstream configFile;
	configFile.open(prefixConfig + "config.toml");
	configFile << "use_haar = " << (useHaarCheckBox->GetValue() ? "true" : "false") << std::endl;
	configFile << "show_camera = " << (showCameraCheckBox->GetValue() ? "true" : "false") << std::endl;
	configFile << "no_eyes = " << (noEyesCheckBox->GetValue() ? "true" : "false") << std::endl;
	// janky edge case lmao
	// if user did not select any model, don't write the line in the config file!
	if (modelNameChoice->GetSelection() != wxNOT_FOUND) {
		configFile << "model = \"" << modelVec[modelNameChoice->GetSelection()] << "\""<< std::endl;
	}
	configFile << "min_camera_id = " << cameraSpinCtrl->GetValue() << std::endl;
	configFile.close();

	Close(true);
}

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