/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2023, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

/*
  App      : kinect-calibrate
  Web page : https://www.mrpt.org/Kinect_and_MRPT

  Usage    : Run and follow on-screen instructions
*/

#ifndef KINECT_CALIBRATE_GUIMAIN_H
#define KINECT_CALIBRATE_GUIMAIN_H

#include <atomic>

#include "wx/defs.h"

//(*Headers(kinect_calibrate_guiDialog)
#include <mrpt/gui/WxUtils.h>
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/dialog.h>
#include <wx/grid.h>
#include <wx/listbox.h>
#include <wx/notebook.h>
#include <wx/panel.h>
#include <wx/radiobox.h>
#include <wx/sizer.h>
#include <wx/spinctrl.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/timer.h>

#include "MyGLCanvas.h"
//*)

#include <mrpt/gui/CMyRedirector.h>
#include <mrpt/obs/CObservation3DRangeScan.h>
#include <mrpt/opengl.h>
#include <mrpt/vision/chessboard_stereo_camera_calib.h>
#include <wx/config.h>	// wxConfig

#include <thread>

// Thread for grabbing: Do this is another thread so we divide rendering and
// grabbing
//   and exploit multicore CPUs.
struct TThreadParam
{
	TThreadParam() = default;

	volatile bool quit{false};
	volatile bool terminated{false};
	volatile double tilt_ang_deg{0};
	volatile double Hz{0};
	volatile bool select_IR_channel{false};

	/** Default: false = only grab intensity channel(s) */
	bool flag_grab_depth{false};

	mrpt::obs::CObservation3DRangeScan::Ptr new_obs;  // RGB+D (+3D points)
};

// Thread for (async) live detecting corners: Do in another thread so the GUI
// doesn't freeze.
struct TThreadDetectCornerParam
{
	std::atomic<bool> quit{false};
	std::atomic<bool> terminated{false};

	std::atomic<bool> ready_for_new_images{true};
	mrpt::img::CImage image;
	mrpt::system::TTimeStamp image_timestamp{INVALID_TIMESTAMP};
	std::vector<mrpt::img::TPixelCoordf> detected_corners;
	std::atomic<bool> detected_corners_done{false};
};

enum TGrabState
{
	gsIdle = 0,
	gsSwitchingRGB,
	gsCapturingRGB,
	gsSwitchingIR,
	gsCapturingIR
};

class kinect_calibrate_guiDialog : public wxDialog
{
	friend void myCalibCallback(
		const mrpt::vision::TImageStereoCallbackData& d, void* user_data);

   public:
	kinect_calibrate_guiDialog(wxWindow* parent, wxWindowID id = -1);
	~kinect_calibrate_guiDialog() override;

   private:
	wxConfig m_config;

	std::unique_ptr<CMyRedirector> m_my_redirector;

	std::thread m_cap_thread;
	TThreadParam m_cap_thread_data;

	std::thread m_findcorners_thread;
	TThreadDetectCornerParam m_findcorners_thread_data;

	mrpt::obs::CObservation3DRangeScan::Ptr m_last_obs;

	TGrabState m_grabstate;

	mrpt::vision::TCalibrationStereoImageList m_calib_images;
	mrpt::vision::TStereoCalibResults m_calib_result;
	mrpt::vision::TStereoCalibParams m_calib_params;

	mrpt::opengl::CPointCloudColoured::Ptr
		m_gl_3d_points;	 // For live Kinect tests
	mrpt::opengl::CSetOfObjects::Ptr m_gl_corner_left,
		m_gl_corner_right;	// For live Kinect tests

	void thread_grabbing();
	void thread_find_corners();
	void ProcessNewGrabbedObs();

	void StopLiveGrabThreads();

	void UpdateListOfImages();
	void ProcessNewSelectedImageListBox();

	void CalibUpdate3DViewCameras();

	void LiveCalibGridInitialize();

	void LiveCalibUpdateToGrid();
	void LiveCalibUpdateFromGrid();

	void fillGridLine(
		int r, const char* label_prefix, const char* label,
		const std::string& val);
	double parseGridLine(int r);

	//(*Handlers(kinect_calibrate_guiDialog)
	void OnQuit(wxCommandEvent& event);
	void OnAbout(wxCommandEvent& event);
	void OnNotebook1PageChanging(wxNotebookEvent& event);
	void OnbtnNext1Click(wxCommandEvent& event);
	void OnbtnQuitClick(wxCommandEvent& event);
	void OnbtnConnectClick(wxCommandEvent& event);
	void OntimConsoleDumpTrigger(wxTimerEvent& event);
	void OntimMiscTrigger(wxTimerEvent& event);
	void OnClose(wxCloseEvent& event);
	void OnrbChannelSwitchSelect(wxCommandEvent& event);
	void OnbtnCaptureClick(wxCommandEvent& event);
	void OnbtnNextCalibClick(wxCommandEvent& event);
	void OnedTiltChange(wxSpinEvent& event);
	void OnlbImagePairsSelect(wxCommandEvent& event);
	void OnbtnRunCalibClick(wxCommandEvent& event);
	void OnbtnSaveCalibClick(wxCommandEvent& event);
	void OnbtnOpCalibKinectClick(wxCommandEvent& event);
	void OnbtnOpCalibStereoGenericClick(wxCommandEvent& event);
	void OnbtnOpTestKinectClick(wxCommandEvent& event);
	void OnbtnDisconnectClick(wxCommandEvent& event);
	void OnbtnListLoadClick(wxCommandEvent& event);
	void OnbtnListSaveClick(wxCommandEvent& event);
	void OnbtnListRemoveSelectedClick(wxCommandEvent& event);
	void OnbtnLoadImageListClick(wxCommandEvent& event);
	void OnrbShowImagesSelect(wxCommandEvent& event);
	void OncbCalibNormalizeClick(wxCommandEvent& event);
	void OnResize(wxSizeEvent& event);
	void OnbtnConnectLive3DClick(wxCommandEvent& event);
	void OnbtnDisconnectLiveClick(wxCommandEvent& event);
	void OnbtnApplyCalibLiveClick(wxCommandEvent& event);
	void OnbtnSaveCalibLiveClick(wxCommandEvent& event);
	void OnbtnLoadCalibClick(wxCommandEvent& event);
	void OnPanel5SetFocus(wxFocusEvent& event);
	void Onm_grid_live_calibCellChange(wxGridEvent& event);
	void OnbtnHelpLiveCalibClick(wxCommandEvent& event);
	void OnNeedsToUpdate6DCamPlot(wxSpinEvent& event);
	void OnedCalibSizeXText(wxCommandEvent& event);
	//*)

	//(*Identifiers(kinect_calibrate_guiDialog)
	static const long ID_STATICTEXT27;
	static const long ID_BUTTON15;
	static const long ID_BUTTON16;
	static const long ID_BUTTON17;
	static const long ID_PANEL8;
	static const long ID_STATICTEXT28;
	static const long ID_STATICTEXT29;
	static const long ID_CUSTOM2;
	static const long ID_STATICTEXT30;
	static const long ID_TEXTCTRL10;
	static const long ID_STATICTEXT31;
	static const long ID_BUTTON3;
	static const long ID_PANEL9;
	static const long ID_CUSTOM3;
	static const long ID_STATICTEXT11;
	static const long ID_BUTTON5;
	static const long ID_STATICTEXT9;
	static const long ID_BUTTON4;
	static const long ID_STATICTEXT5;
	static const long ID_BUTTON8;
	static const long ID_PANEL3;
	static const long ID_CUSTOM1;
	static const long ID_TEXTCTRL5;
	static const long ID_STATICTEXT1;
	static const long ID_SPINCTRL1;
	static const long ID_STATICTEXT2;
	static const long ID_SPINCTRL2;
	static const long ID_RADIOBOX1;
	static const long ID_CHECKBOX1;
	static const long ID_STATICTEXT22;
	static const long ID_SPINCTRL7;
	static const long ID_BUTTON6;
	static const long ID_STATICTEXT12;
	static const long ID_BUTTON7;
	static const long ID_PANEL7;
	static const long ID_PANEL4;
	static const long ID_STATICTEXT23;
	static const long ID_LISTBOX1;
	static const long ID_BUTTON9;
	static const long ID_BUTTON10;
	static const long ID_BUTTON11;
	static const long ID_BUTTON12;
	static const long ID_RADIOBOX2;
	static const long ID_STATICTEXT19;
	static const long ID_CUSTOM4;
	static const long ID_STATICTEXT20;
	static const long ID_CUSTOM5;
	static const long ID_STATICTEXT25;
	static const long ID_STATICTEXT13;
	static const long ID_SPINCTRL3;
	static const long ID_STATICTEXT14;
	static const long ID_SPINCTRL4;
	static const long ID_STATICTEXT15;
	static const long ID_TEXTCTRL6;
	static const long ID_STATICTEXT16;
	static const long ID_TEXTCTRL7;
	static const long ID_PANEL11;
	static const long ID_CHECKBOX4;
	static const long ID_CHECKBOX5;
	static const long ID_CHECKBOX6;
	static const long ID_CHECKBOX7;
	static const long ID_CHECKBOX8;
	static const long ID_PANEL12;
	static const long ID_STATICTEXT26;
	static const long ID_SPINCTRL8;
	static const long ID_CHECKBOX3;
	static const long ID_CHECKBOX2;
	static const long ID_PANEL13;
	static const long ID_NOTEBOOK3;
	static const long ID_BUTTON14;
	static const long ID_STATICTEXT24;
	static const long ID_BUTTON13;
	static const long ID_TEXTCTRL8;
	static const long ID_PANEL1;
	static const long ID_CUSTOM6;
	static const long ID_PANEL10;
	static const long ID_NOTEBOOK2;
	static const long ID_PANEL6;
	static const long ID_XY_GLCANVAS;
	static const long ID_STATICTEXT3;
	static const long ID_BUTTON18;
	static const long ID_BUTTON20;
	static const long ID_STATICTEXT4;
	static const long ID_BUTTON19;
	static const long ID_BUTTON21;
	static const long ID_BITMAPBUTTON1;
	static const long ID_GRID1;
	static const long ID_PANEL5;
	static const long ID_NOTEBOOK1;
	static const long ID_STATICTEXT10;
	static const long ID_TEXTCTRL4;
	static const long ID_BUTTON1;
	static const long ID_BUTTON2;
	static const long ID_PANEL2;
	static const long ID_TIMER1;
	static const long ID_TIMER2;
	//*)

	//(*Declarations(kinect_calibrate_guiDialog)
	wxTextCtrl* edCalibSizeX;
	wxStaticText* StaticText24;
	wxStaticText* StaticText22;
	wxCheckBox* cbOptT1;
	wxButton* btnQuit;
	mrpt::gui::wxMRPTImageControl* m_realtimeview_cap;
	wxTextCtrl* TextCtrl3;
	wxPanel* Panel6;
	wxPanel* Panel1;
	wxPanel* Panel11;
	wxStaticText* StaticText21;
	wxStaticText* StaticText13;
	wxStaticText* StaticText14;
	wxStaticText* StaticText15;
	wxPanel* Panel7;
	wxButton* btnOpCalibKinect;
	wxStaticText* lbNumCaptured;
	wxStaticText* StaticText28;
	wxCheckBox* cbNormalize;
	wxListBox* lbImagePairs;
	mrpt::gui::wxMRPTImageControl* m_imgStaticKinect;
	wxButton* btnDisconnectLive;
	wxButton* btnDisconnect;
	wxButton* btnConnect;
	wxButton* bntAbout;
	wxPanel* Panel12;
	wxButton* btnSaveCalib;
	wxButton* btnListLoad;
	wxBitmapButton* btnHelpLiveCalib;
	wxButton* btnRunCalib;
	wxTimer timMisc;
	wxButton* btnOpTestKinect;
	wxCheckBox* cbCalibNormalize;
	wxSpinCtrl* edSizeY;
	wxPanel* Panel9;
	wxPanel* Panel8;
	wxCheckBox* cbOptK2;
	wxStaticText* StaticText18;
	wxSpinCtrl* edTilt;
	wxStaticText* StaticText1;
	wxStaticText* StaticText10;
	wxPanel* Panel10;
	wxRadioBox* rbShowImages;
	wxTimer timConsoleDump;
	wxButton* btnConnectLive3D;
	wxButton* btnLoadImageList;
	wxPanel* Panel2;
	wxStaticText* StaticText3;
	CMyGLCanvas* m_plot3D;
	wxCheckBox* cbCalibUseRobust;
	wxPanel* Panel4;
	wxTextCtrl* edCalibSizeY;
	wxStaticText* StaticText23;
	wxCheckBox* cbOptK3;
	wxPanel* Panel5;
	wxCheckBox* cbOptK1;
	wxStaticText* StaticText12;
	wxSpinCtrl* edSizeX;
	wxSpinCtrl* edCalibCheckY;
	CMyGLCanvas* m_plot3D_cameras;
	wxRadioBox* rbChannelSwitch;
	wxButton* btnSaveCalibLive;
	wxPanel* Panel3;
	wxButton* btnLoadCalib;
	wxGrid* m_grid_live_calib;
	wxButton* btnNext1;
	wxButton* btnListSave;
	wxNotebook* Notebook2;
	wxPanel* Panel13;
	wxSpinCtrl* edCalibCheckX;
	wxSpinCtrl* edMaxIters;
	wxStaticText* StaticText4;
	wxStaticText* StaticText5;
	wxButton* btnNextCalib;
	wxStaticText* StaticText2;
	wxStaticText* StaticText30;
	wxStaticText* StaticText27;
	wxNotebook* Notebook1;
	mrpt::gui::wxMRPTImageControl* m_view_right;
	wxButton* btnOpCalibStereoGeneric;
	wxStaticText* StaticText26;
	wxCheckBox* cbOptT2;
	wxButton* btnCapture;
	wxButton* btnNext2;
	wxNotebook* NotebookCalibResults;
	wxStaticText* StaticText19;
	wxStaticText* StaticText29;
	mrpt::gui::wxMRPTImageControl* m_realtimeview_test;
	wxTextCtrl* edLogTest;
	wxStaticText* StaticText9;
	wxTextCtrl* edLogCalibResult;
	wxTextCtrl* TextCtrl2;
	wxStaticText* StaticText11;
	wxStaticText* StaticText25;
	mrpt::gui::wxMRPTImageControl* m_view_left;
	wxButton* btnListRemoveSelected;
	//*)

	DECLARE_EVENT_TABLE()
};

#endif	// KINECT_CALIBRATE_GUIMAIN_H
