/* +------------------------------------------------------------------------+
   |                     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          |
   +------------------------------------------------------------------------+ */
#include "CFormEdit.h"

#include <wx/msgdlg.h>

#include "xRawLogViewerMain.h"

//(*InternalHeaders(CFormEdit)
#include <wx/intl.h>
#include <wx/string.h>
//*)
#include <wx/app.h>
#include <wx/busyinfo.h>
#include <wx/filedlg.h>
#include <wx/imaglist.h>
#include <wx/progdlg.h>

// General global variables:
#include <mrpt/containers/stl_containers_utils.h>
#include <mrpt/io/CFileGZInputStream.h>
#include <mrpt/io/CFileGZOutputStream.h>
#include <mrpt/obs/CObservation2DRangeScan.h>
#include <mrpt/obs/CObservationImage.h>
#include <mrpt/obs/CObservationStereoImages.h>
#include <mrpt/serialization/CArchive.h>
#include <mrpt/system/filesystem.h>

using namespace mrpt;
using namespace mrpt::obs;
using namespace mrpt::opengl;
using namespace mrpt::system;
using namespace mrpt::img;
using namespace mrpt::math;
using namespace mrpt::containers;
using namespace mrpt::serialization;
using namespace mrpt::math;
using namespace mrpt::img;
using namespace mrpt::io;
using namespace std;

//(*IdInit(CFormEdit)
const long CFormEdit::ID_RADIOBUTTON1 = wxNewId();
const long CFormEdit::ID_RADIOBUTTON2 = wxNewId();
const long CFormEdit::ID_STATICTEXT22 = wxNewId();
const long CFormEdit::ID_TEXTCTRL11 = wxNewId();
const long CFormEdit::ID_BUTTON9 = wxNewId();
const long CFormEdit::ID_STATICTEXT23 = wxNewId();
const long CFormEdit::ID_TEXTCTRL12 = wxNewId();
const long CFormEdit::ID_BUTTON11 = wxNewId();
const long CFormEdit::ID_STATICTEXT1 = wxNewId();
const long CFormEdit::ID_SLIDER1 = wxNewId();
const long CFormEdit::ID_SPINCTRL1 = wxNewId();
const long CFormEdit::ID_STATICTEXT3 = wxNewId();
const long CFormEdit::ID_SLIDER2 = wxNewId();
const long CFormEdit::ID_SPINCTRL2 = wxNewId();
const long CFormEdit::ID_BUTTON1 = wxNewId();
const long CFormEdit::ID_BUTTON2 = wxNewId();
const long CFormEdit::ID_CHECKBOX1 = wxNewId();
const long CFormEdit::ID_CHECKBOX2 = wxNewId();
const long CFormEdit::ID_CHECKBOX3 = wxNewId();
const long CFormEdit::ID_CHECKBOX4 = wxNewId();
const long CFormEdit::ID_CHECKBOX5 = wxNewId();
const long CFormEdit::ID_BUTTON4 = wxNewId();
const long CFormEdit::ID_CHECKBOX6 = wxNewId();
const long CFormEdit::ID_CHECKBOX7 = wxNewId();
const long CFormEdit::ID_CHECKBOX8 = wxNewId();
const long CFormEdit::ID_CHECKBOX9 = wxNewId();
const long CFormEdit::ID_CHECKBOX10 = wxNewId();
const long CFormEdit::ID_BUTTON5 = wxNewId();
const long CFormEdit::ID_CHECKLISTBOX2 = wxNewId();
const long CFormEdit::ID_BUTTON7 = wxNewId();
const long CFormEdit::ID_BUTTON8 = wxNewId();
const long CFormEdit::ID_CHECKLISTBOX1 = wxNewId();
const long CFormEdit::ID_BUTTON10 = wxNewId();
const long CFormEdit::ID_BUTTON12 = wxNewId();
const long CFormEdit::ID_BUTTON13 = wxNewId();
const long CFormEdit::ID_STATICTEXT2 = wxNewId();
const long CFormEdit::ID_TEXTCTRL2 = wxNewId();
const long CFormEdit::ID_BUTTON3 = wxNewId();
const long CFormEdit::ID_BUTTON6 = wxNewId();
//*)

BEGIN_EVENT_TABLE(CFormEdit, wxDialog)
//(*EventTable(CFormEdit)
//*)
END_EVENT_TABLE()

CFormEdit::CFormEdit(wxWindow* parent, wxWindowID id)
{
	//(*Initialize(CFormEdit)
	wxFlexGridSizer* FlexGridSizer16;
	wxFlexGridSizer* FlexGridSizer10;
	wxFlexGridSizer* FlexGridSizer5;
	wxFlexGridSizer* FlexGridSizer15;
	wxFlexGridSizer* FlexGridSizer14;
	wxFlexGridSizer* FlexGridSizer13;
	wxFlexGridSizer* FlexGridSizer12;

	Create(
		parent, id, _("Edit the rawlog"), wxDefaultPosition, wxDefaultSize,
		wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER, _T("id"));
	FlexGridSizer1 = new wxFlexGridSizer(5, 1, 0, 0);
	FlexGridSizer1->AddGrowableCol(0);
	FlexGridSizer1->AddGrowableRow(0);
	FlexGridSizer1->AddGrowableRow(1);
	FlexGridSizer1->AddGrowableRow(2);
	FlexGridSizer1->AddGrowableRow(3);
	BoxSizer1 = new wxBoxSizer(wxHORIZONTAL);
	FlexGridSizer12 = new wxFlexGridSizer(1, 2, 0, 0);
	FlexGridSizer12->AddGrowableCol(0);
	FlexGridSizer12->AddGrowableCol(1);
	StaticBoxSizer6 = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Apply to:"));
	BoxSizer4 = new wxBoxSizer(wxVERTICAL);
	BoxSizer5 = new wxBoxSizer(wxHORIZONTAL);
	FlexGridSizer7 = new wxFlexGridSizer(3, 4, 0, 0);
	FlexGridSizer7->AddGrowableCol(2);
	rbLoaded = new wxRadioButton(
		this, ID_RADIOBUTTON1, _("Loaded rawlog"), wxDefaultPosition,
		wxDefaultSize, 0, wxDefaultValidator, _T("ID_RADIOBUTTON1"));
	FlexGridSizer7->Add(
		rbLoaded, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
	FlexGridSizer7->Add(
		-1, -1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	FlexGridSizer7->Add(
		-1, -1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	FlexGridSizer7->Add(
		-1, -1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	rbFile = new wxRadioButton(
		this, ID_RADIOBUTTON2, _("Rawlog in file:"), wxDefaultPosition,
		wxDefaultSize, 0, wxDefaultValidator, _T("ID_RADIOBUTTON2"));
	FlexGridSizer7->Add(
		rbFile, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
	StaticText22 = new wxStaticText(
		this, ID_STATICTEXT22, _("Input file:"), wxDefaultPosition,
		wxDefaultSize, 0, _T("ID_STATICTEXT22"));
	FlexGridSizer7->Add(
		StaticText22, 1, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 5);
	txtInputFile = new wxTextCtrl(
		this, ID_TEXTCTRL11, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_TEXTCTRL11"));
	FlexGridSizer7->Add(txtInputFile, 1, wxEXPAND, 5);
	btnPickInput = new wxButton(
		this, ID_BUTTON9, _("Select..."), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_BUTTON9"));
	FlexGridSizer7->Add(
		btnPickInput, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 0);
	FlexGridSizer7->Add(
		-1, -1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	StaticText23 = new wxStaticText(
		this, ID_STATICTEXT23, _("Output file:"), wxDefaultPosition,
		wxDefaultSize, 0, _T("ID_STATICTEXT23"));
	FlexGridSizer7->Add(
		StaticText23, 1, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 5);
	txtOutputFile = new wxTextCtrl(
		this, ID_TEXTCTRL12, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_TEXTCTRL12"));
	FlexGridSizer7->Add(txtOutputFile, 1, wxEXPAND, 5);
	btnPickOut = new wxButton(
		this, ID_BUTTON11, _("Select..."), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_BUTTON11"));
	FlexGridSizer7->Add(
		btnPickOut, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 0);
	BoxSizer5->Add(FlexGridSizer7, 1, wxEXPAND, 0);
	BoxSizer4->Add(BoxSizer5, 1, wxEXPAND, 0);
	StaticBoxSizer6->Add(BoxSizer4, 1, wxEXPAND, 0);
	FlexGridSizer12->Add(StaticBoxSizer6, 1, wxEXPAND, 5);
	StaticBoxSizer1 = new wxStaticBoxSizer(
		wxHORIZONTAL, this, _("Apply changes to this range:"));
	FlexGridSizer2 = new wxFlexGridSizer(2, 3, 0, 0);
	FlexGridSizer2->AddGrowableCol(1);
	FlexGridSizer2->AddGrowableRow(0);
	FlexGridSizer2->AddGrowableRow(1);
	StaticText1 = new wxStaticText(
		this, ID_STATICTEXT1, _("First:"), wxDefaultPosition, wxDefaultSize, 0,
		_T("ID_STATICTEXT1"));
	FlexGridSizer2->Add(
		StaticText1, 1, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 5);
	slFrom = new wxSlider(
		this, ID_SLIDER1, 0, 0, 100, wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_SLIDER1"));
	FlexGridSizer2->Add(slFrom, 1, wxEXPAND, 5);
	spinFirst = new wxSpinCtrl(
		this, ID_SPINCTRL1, _T("0"), wxDefaultPosition, wxDefaultSize, 0, 0,
		100, 0, _T("ID_SPINCTRL1"));
	spinFirst->SetValue(_T("0"));
	FlexGridSizer2->Add(
		spinFirst, 1, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
	StaticText3 = new wxStaticText(
		this, ID_STATICTEXT3, _("Last:"), wxDefaultPosition, wxDefaultSize, 0,
		_T("ID_STATICTEXT3"));
	FlexGridSizer2->Add(
		StaticText3, 1, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 5);
	slTo = new wxSlider(
		this, ID_SLIDER2, 0, 0, 100, wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_SLIDER2"));
	FlexGridSizer2->Add(slTo, 1, wxEXPAND, 5);
	spinLast = new wxSpinCtrl(
		this, ID_SPINCTRL2, _T("0"), wxDefaultPosition, wxDefaultSize, 0, 0,
		100, 0, _T("ID_SPINCTRL2"));
	spinLast->SetValue(_T("0"));
	FlexGridSizer2->Add(
		spinLast, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	StaticBoxSizer1->Add(FlexGridSizer2, 1, wxEXPAND, 0);
	FlexGridSizer12->Add(StaticBoxSizer1, 1, wxEXPAND, 5);
	BoxSizer1->Add(FlexGridSizer12, 1, wxEXPAND, 0);
	FlexGridSizer1->Add(BoxSizer1, 1, wxEXPAND, 0);
	BoxSizer2 = new wxBoxSizer(wxHORIZONTAL);
	btnKeep = new wxButton(
		this, ID_BUTTON1, _("Keep the selected range only..."),
		wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
		_T("ID_BUTTON1"));
	BoxSizer2->Add(
		btnKeep, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	btnDelete = new wxButton(
		this, ID_BUTTON2, _("Remove the selected range..."), wxDefaultPosition,
		wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON2"));
	BoxSizer2->Add(
		btnDelete, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	FlexGridSizer1->Add(BoxSizer2, 1, wxEXPAND, 0);
	FlexGridSizer8 = new wxFlexGridSizer(1, 2, 0, 0);
	FlexGridSizer8->AddGrowableCol(0);
	FlexGridSizer8->AddGrowableCol(1);
	StaticBoxSizer3 = new wxStaticBoxSizer(
		wxHORIZONTAL, this,
		_("Remove observations by index in sensorial frame:"));
	FlexGridSizer3 = new wxFlexGridSizer(1, 6, 0, 0);
	cbO0 = new wxCheckBox(
		this, ID_CHECKBOX1, _("0"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX1"));
	cbO0->SetValue(false);
	FlexGridSizer3->Add(
		cbO0, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	cbO1 = new wxCheckBox(
		this, ID_CHECKBOX2, _("1"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX2"));
	cbO1->SetValue(false);
	FlexGridSizer3->Add(
		cbO1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	cbO2 = new wxCheckBox(
		this, ID_CHECKBOX3, _("2"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX3"));
	cbO2->SetValue(false);
	FlexGridSizer3->Add(
		cbO2, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	cbO3 = new wxCheckBox(
		this, ID_CHECKBOX4, _("3"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX4"));
	cbO3->SetValue(false);
	FlexGridSizer3->Add(
		cbO3, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	cbO4 = new wxCheckBox(
		this, ID_CHECKBOX5, _("4"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX5"));
	cbO4->SetValue(false);
	FlexGridSizer3->Add(
		cbO4, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	btnDelObsIndx = new wxButton(
		this, ID_BUTTON4, _("Do it"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_BUTTON4"));
	FlexGridSizer3->Add(
		btnDelObsIndx, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	StaticBoxSizer3->Add(FlexGridSizer3, 1, wxEXPAND, 0);
	FlexGridSizer8->Add(StaticBoxSizer3, 1, wxEXPAND, 5);
	StaticBoxSizer4 = new wxStaticBoxSizer(
		wxHORIZONTAL, this,
		_("Remove actions by index in collection of actions:"));
	FlexGridSizer4 = new wxFlexGridSizer(1, 6, 0, 0);
	FlexGridSizer4->AddGrowableCol(5);
	FlexGridSizer4->AddGrowableRow(0);
	cbA0 = new wxCheckBox(
		this, ID_CHECKBOX6, _("0"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX6"));
	cbA0->SetValue(false);
	FlexGridSizer4->Add(
		cbA0, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	cbA1 = new wxCheckBox(
		this, ID_CHECKBOX7, _("1"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX7"));
	cbA1->SetValue(false);
	FlexGridSizer4->Add(
		cbA1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	cbA2 = new wxCheckBox(
		this, ID_CHECKBOX8, _("2"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX8"));
	cbA2->SetValue(false);
	FlexGridSizer4->Add(
		cbA2, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	cbA3 = new wxCheckBox(
		this, ID_CHECKBOX9, _("3"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX9"));
	cbA3->SetValue(false);
	FlexGridSizer4->Add(
		cbA3, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	cbA4 = new wxCheckBox(
		this, ID_CHECKBOX10, _("4"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_CHECKBOX10"));
	cbA4->SetValue(false);
	FlexGridSizer4->Add(
		cbA4, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
		5);
	btnRemActsIndx = new wxButton(
		this, ID_BUTTON5, _("Do it"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_BUTTON5"));
	FlexGridSizer4->Add(
		btnRemActsIndx, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	StaticBoxSizer4->Add(FlexGridSizer4, 1, wxEXPAND, 0);
	FlexGridSizer8->Add(StaticBoxSizer4, 1, wxEXPAND, 5);
	FlexGridSizer1->Add(FlexGridSizer8, 1, wxEXPAND, 0);
	FlexGridSizer9 = new wxFlexGridSizer(1, 2, 0, 0);
	FlexGridSizer9->AddGrowableCol(0);
	FlexGridSizer9->AddGrowableCol(1);
	StaticBoxSizer5 = new wxStaticBoxSizer(
		wxHORIZONTAL, this, _("Remove observations by CLASS:"));
	FlexGridSizer6 = new wxFlexGridSizer(2, 1, 0, 0);
	FlexGridSizer6->AddGrowableCol(0);
	FlexGridSizer5 = new wxFlexGridSizer(1, 1, 0, 0);
	FlexGridSizer5->AddGrowableCol(0);
	FlexGridSizer5->AddGrowableRow(0);
	cbObsClass = new wxCheckListBox(
		this, ID_CHECKLISTBOX2, wxDefaultPosition, wxSize(-1, 200), 0, nullptr,
		0, wxDefaultValidator, _T("ID_CHECKLISTBOX2"));
	cbObsClass->SetMaxSize(wxSize(-1, 200));
	FlexGridSizer5->Add(cbObsClass, 1, wxEXPAND, 5);
	FlexGridSizer6->Add(FlexGridSizer5, 1, wxEXPAND, 0);
	FlexGridSizer10 = new wxFlexGridSizer(1, 2, 0, 0);
	FlexGridSizer10->AddGrowableCol(0);
	FlexGridSizer10->AddGrowableCol(1);
	btnRemoveObsClass = new wxButton(
		this, ID_BUTTON7, _("Remove selected..."), wxDefaultPosition,
		wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON7"));
	FlexGridSizer10->Add(
		btnRemoveObsClass, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	btnRemoveAllButByClass = new wxButton(
		this, ID_BUTTON8, _("Remove all but selected..."), wxDefaultPosition,
		wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON8"));
	FlexGridSizer10->Add(
		btnRemoveAllButByClass, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	FlexGridSizer6->Add(FlexGridSizer10, 1, wxEXPAND, 0);
	StaticBoxSizer5->Add(FlexGridSizer6, 1, wxEXPAND, 0);
	FlexGridSizer9->Add(StaticBoxSizer5, 1, wxEXPAND, 5);
	StaticBoxSizer7 = new wxStaticBoxSizer(
		wxHORIZONTAL, this, _("Remove observations by LABEL:"));
	FlexGridSizer11 = new wxFlexGridSizer(2, 1, 0, 0);
	FlexGridSizer11->AddGrowableCol(0);
	FlexGridSizer13 = new wxFlexGridSizer(1, 2, 0, 0);
	FlexGridSizer13->AddGrowableCol(0);
	FlexGridSizer13->AddGrowableRow(0);
	cbObsLabel = new wxCheckListBox(
		this, ID_CHECKLISTBOX1, wxDefaultPosition, wxSize(-1, 200), 0, nullptr,
		0, wxDefaultValidator, _T("ID_CHECKLISTBOX1"));
	cbObsLabel->SetMaxSize(wxSize(-1, 200));
	FlexGridSizer13->Add(cbObsLabel, 1, wxEXPAND, 5);
	FlexGridSizer11->Add(FlexGridSizer13, 1, wxEXPAND, 0);
	FlexGridSizer14 = new wxFlexGridSizer(1, 2, 0, 0);
	FlexGridSizer14->AddGrowableCol(0);
	FlexGridSizer14->AddGrowableCol(1);
	btnRemByLabel = new wxButton(
		this, ID_BUTTON10, _("Remove matches..."), wxDefaultPosition,
		wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON10"));
	FlexGridSizer14->Add(
		btnRemByLabel, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	btnRemByLabelNon = new wxButton(
		this, ID_BUTTON12, _("Remove non-matches..."), wxDefaultPosition,
		wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON12"));
	FlexGridSizer14->Add(
		btnRemByLabelNon, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	FlexGridSizer11->Add(FlexGridSizer14, 1, wxEXPAND, 0);
	StaticBoxSizer7->Add(FlexGridSizer11, 1, wxEXPAND, 0);
	FlexGridSizer9->Add(StaticBoxSizer7, 1, wxEXPAND, 5);
	FlexGridSizer1->Add(FlexGridSizer9, 1, wxEXPAND, 0);
	FlexGridSizer16 = new wxFlexGridSizer(1, 2, 0, 0);
	FlexGridSizer16->AddGrowableCol(1);
	StaticBoxSizer2 =
		new wxStaticBoxSizer(wxHORIZONTAL, this, _("Miscellaneous"));
	FlexGridSizer15 = new wxFlexGridSizer(2, 3, 0, 0);
	btnLeaveHorizScans = new wxButton(
		this, ID_BUTTON13, _("Leave horizontal laser scans only..."),
		wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
		_T("ID_BUTTON13"));
	FlexGridSizer15->Add(btnLeaveHorizScans, 1, wxEXPAND, 5);
	StaticText2 = new wxStaticText(
		this, ID_STATICTEXT2, _("Max. pitch (deg):"), wxDefaultPosition,
		wxDefaultSize, 0, _T("ID_STATICTEXT2"));
	FlexGridSizer15->Add(
		StaticText2, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	edMaxPitch = new wxTextCtrl(
		this, ID_TEXTCTRL2, _("1.5"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_TEXTCTRL2"));
	FlexGridSizer15->Add(
		edMaxPitch, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	btnImgSwap = new wxButton(
		this, ID_BUTTON3, _("Swap Red-Blue channels of all images..."),
		wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator,
		_T("ID_BUTTON3"));
	FlexGridSizer15->Add(
		btnImgSwap, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	StaticBoxSizer2->Add(FlexGridSizer15, 1, wxEXPAND, 0);
	FlexGridSizer16->Add(
		StaticBoxSizer2, 1,
		wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
	btnClose = new wxButton(
		this, ID_BUTTON6, _("Close"), wxDefaultPosition, wxDefaultSize, 0,
		wxDefaultValidator, _T("ID_BUTTON6"));
	FlexGridSizer16->Add(
		btnClose, 1, wxALL | wxALIGN_RIGHT | wxALIGN_BOTTOM, 5);
	FlexGridSizer1->Add(FlexGridSizer16, 1, wxEXPAND, 0);
	SetSizer(FlexGridSizer1);
	FlexGridSizer1->Fit(this);
	FlexGridSizer1->SetSizeHints(this);
	Center();

	Bind(
		wxEVT_RADIOBUTTON, &CFormEdit::OnrbLoadedSelect, this, ID_RADIOBUTTON1);
	Bind(wxEVT_RADIOBUTTON, &CFormEdit::OnrbFileSelect, this, ID_RADIOBUTTON2);
	Bind(wxEVT_BUTTON, &CFormEdit::OnbtnPickInputClick, this, ID_BUTTON9);
	Bind(wxEVT_BUTTON, &CFormEdit::OnbtnPickOutClick, this, ID_BUTTON11);
	Bind(
		wxEVT_SCROLL_THUMBTRACK, &CFormEdit::OnslFirstCmdScrollChanged, this,
		ID_SLIDER1);
	Bind(
		wxEVT_SCROLL_CHANGED, &CFormEdit::OnslFirstCmdScrollChanged, this,
		ID_SLIDER1);
	Bind(
		wxEVT_SCROLL_THUMBTRACK, &CFormEdit::OnslToCmdScrollChanged, this,
		ID_SLIDER2);
	Bind(
		wxEVT_SCROLL_CHANGED, &CFormEdit::OnslToCmdScrollChanged, this,
		ID_SLIDER2);
	Bind(wxEVT_BUTTON, &CFormEdit::OnbtnKeepClick, this, ID_BUTTON1);
	Bind(wxEVT_BUTTON, &CFormEdit::OnbtnDeleteClick, this, ID_BUTTON2);
	Bind(wxEVT_BUTTON, &CFormEdit::OnbtnDelObsIndxClick, this, ID_BUTTON4);
	Bind(wxEVT_BUTTON, &CFormEdit::OnbtnRemActsIndxClick, this, ID_BUTTON5);
	Bind(wxEVT_BUTTON, &CFormEdit::OnbtnRemoveObsClassClick, this, ID_BUTTON7);
	Bind(
		wxEVT_BUTTON, &CFormEdit::OnbtnRemoveAllButByClassClick1, this,
		ID_BUTTON8);
	Bind(wxEVT_BUTTON, &CFormEdit::OnRemoveByLabel, this, ID_BUTTON10);
	Bind(wxEVT_BUTTON, &CFormEdit::OnRemoveButLabel, this, ID_BUTTON12);
	Bind(
		wxEVT_BUTTON, &CFormEdit::OnbtnLeaveHorizScansClick, this, ID_BUTTON13);
	Bind(wxEVT_BUTTON, &CFormEdit::OnbtnImgSwapClick, this, ID_BUTTON3);
	Bind(wxEVT_BUTTON, &CFormEdit::OnbtnCloseClick, this, ID_BUTTON6);
	Bind(wxEVT_INIT_DIALOG, &CFormEdit::OnInit, this, wxID_ANY);
	//*)
}

CFormEdit::~CFormEdit()
{
	//(*Destroy(CFormEdit)
	//*)
}

// Load the selected items from list box:
void loadSelectionsFromListBox(std::vector<std::string>& v, wxCheckListBox* c)
{
	v.clear();
	for (unsigned i = 0; i < c->GetCount(); i++)
		if (c->IsChecked(i)) v.emplace_back(c->GetString(i).mb_str());
}

void CFormEdit::OnbtnCloseClick(wxCommandEvent& event) { Close(); }
void CFormEdit::OnslFirstCmdScrollChanged(wxScrollEvent& event)
{
	int toVal = slTo->GetValue();
	int curVal = slFrom->GetValue();

	if (curVal > toVal) slFrom->SetValue(toVal);
	spinFirst->SetValue(slFrom->GetValue());
	// lbFirst->SetLabel( wxString::Format(_("%d"),slFrom->GetValue()) );
}

void CFormEdit::OnslToCmdScrollChanged(wxScrollEvent& event)
{
	int fromVal = spinFirst->GetValue();
	int curVal = slTo->GetValue();

	if (curVal < fromVal) slTo->SetValue(fromVal);
	//    lbLast->SetLabel( wxString::Format(_("%d"),slTo->GetValue()) );
	spinLast->SetValue(slTo->GetValue());
}

void CFormEdit::OnbtnKeepClick(wxCommandEvent& event)
{
	WX_START_TRY

	size_t n = rawlog.size();
	size_t first = spinFirst->GetValue();
	size_t last = spinLast->GetValue();

	ASSERT_(last < n);
	ASSERT_(last >= first);

	wxBusyCursor wait;

	if (last < n - 1) rawlog.remove(last + 1, n - 1);

	if (first > 0) rawlog.remove(0, first - 1);

	Close();

	WX_END_TRY
}

void CFormEdit::OnbtnDeleteClick(wxCommandEvent& event)
{
	WX_START_TRY

	size_t n = rawlog.size();
	size_t first = spinFirst->GetValue();
	size_t last = spinLast->GetValue();

	ASSERT_(last < n);
	ASSERT_(last >= first);

	wxBusyCursor wait;

	rawlog.remove(first, last);

	Close();

	WX_END_TRY
}

std::vector<bool> auxMask;

// Delete observations by index in their sensory frame.
void filter_delObsByIndex(
	mrpt::obs::CActionCollection* acts, mrpt::obs::CSensoryFrame* SF,
	int& changesCount)
{
	if (SF)
	{
		int j = 0;
		for (auto it = SF->begin(); it != SF->end(); ++j)
		{
			if (auxMask[j])
			{
				it = SF->erase(it);
				changesCount++;
			}
			else
				it++;
		}
	}
}

// Delete observations by index in their sensory frame.
void CFormEdit::OnbtnDelObsIndxClick(wxCommandEvent& event)
{
	WX_START_TRY

	auxMask.clear();
	auxMask.resize(5, false);
	auxMask[0] = cbO0->IsChecked();
	auxMask[1] = cbO1->IsChecked();
	auxMask[2] = cbO2->IsChecked();
	auxMask[3] = cbO3->IsChecked();
	auxMask[4] = cbO4->IsChecked();

	executeOperationOnRawlog(filter_delObsByIndex, "Observations deleted:");

	WX_END_TRY
}

// Remove observations by class name:
std::vector<std::string> classNameOfObsToRemove;

void filter_delObsByClass(
	mrpt::obs::CActionCollection* acts, mrpt::obs::CSensoryFrame* SF,
	int& changesCount)
{
	if (SF)
	{
		for (auto it = SF->begin(); it != SF->end();)
		{
			CObservation::Ptr obs = *it;
			if (string::npos !=
				find_in_vector(
					string(obs->GetRuntimeClass()->className),
					classNameOfObsToRemove))
			{
				// A match: Delete it:
				it = SF->erase(it);
				changesCount++;
			}
			else
				it++;
		}
	}
}

void filter_remObsByClass(
	mrpt::obs::CActionCollection* acts, mrpt::obs::CSensoryFrame* SF,
	int& changesCount)
{
	if (SF)
	{
		for (auto it = SF->begin(); it != SF->end();)
		{
			CObservation::Ptr obs = *it;
			if (string::npos ==
				find_in_vector(
					string(obs->GetRuntimeClass()->className),
					classNameOfObsToRemove))
			{
				// A NO match: Delete it:
				it = SF->erase(it);
			}
			else
				it++;
		}
	}
}

// Remove observations by class name:
void CFormEdit::OnbtnRemoveObsClassClick(wxCommandEvent& event)
{
	WX_START_TRY

	loadSelectionsFromListBox(classNameOfObsToRemove, cbObsClass);
	if (classNameOfObsToRemove.empty())
		THROW_EXCEPTION("You must select one class name!");

	executeOperationOnRawlog(filter_delObsByClass, "Observations deleted:");
	Close();

	WX_END_TRY
}

// Delete actions by index.
void filter_delActsByIndex(
	mrpt::obs::CActionCollection* acts, mrpt::obs::CSensoryFrame* SF,
	int& changesCount)
{
	if (acts)
	{
		int j = 0;
		for (auto it = acts->begin(); it != acts->end(); ++j)
		{
			if (auxMask[j])
			{
				it = acts->erase(it);
				changesCount++;
			}
			else
				it++;
		}
	}
}

// Remove actions by index:
void CFormEdit::OnbtnRemActsIndxClick(wxCommandEvent& event)
{
	WX_START_TRY

	auxMask.clear();
	auxMask.resize(5, false);
	auxMask[0] = cbA0->IsChecked();
	auxMask[1] = cbA1->IsChecked();
	auxMask[2] = cbA2->IsChecked();
	auxMask[3] = cbA3->IsChecked();
	auxMask[4] = cbA4->IsChecked();

	executeOperationOnRawlog(filter_delActsByIndex, "Actions deleted:");

	WX_END_TRY
}

void swapColors(CImage& img)
{
	int lx = img.getWidth();
	int ly = img.getHeight();
	for (int y = 0; y < ly; y++)
	{
		for (int x = 0; x < lx; x++)
		{
			unsigned char* pixels = img(x, y);
			unsigned char temp = pixels[0];
			pixels[0] = pixels[2];
			pixels[2] = temp;
		}
	}
}

// Swap colors:
void filter_swapColors(
	mrpt::obs::CActionCollection* acts, mrpt::obs::CSensoryFrame* SF,
	int& changesCount)
{
	if (SF)
	{
		for (auto obs : *SF)
		{
			if (IS_CLASS(*obs, CObservationImage))
			{
				auto* o = (CObservationImage*)obs.get();
				if (o->image.isColor())
				{
					swapColors(o->image);
					changesCount++;
				}
			}
			else if (IS_CLASS(*obs, CObservationStereoImages))
			{
				auto* o = (CObservationStereoImages*)obs.get();
				if (o->imageLeft.isColor())
				{
					swapColors(o->imageLeft);
					changesCount++;
				}

				if (o->imageRight.isColor())
				{
					swapColors(o->imageRight);
					changesCount++;
				}
			}
		}
	}
}

// Swap colors:
void CFormEdit::OnbtnImgSwapClick(wxCommandEvent& event)
{
	WX_START_TRY
	executeOperationOnRawlog(filter_swapColors, "Individual images modified:");
	WX_END_TRY
}

void CFormEdit::OnInit(wxInitDialogEvent& event)
{
	Center();
	wxCommandEvent dumm;

	// Is there any rawlog loaded??
	if (!rawlog.size())
	{
		// NO: Disable "modify current":
		OnrbFileSelect(dumm);
		rbFile->SetValue(true);
		rbLoaded->Disable();
	}
	else
	{
		// Select the loaded rawlog by default:
		OnrbLoadedSelect(dumm);
		rbLoaded->SetValue(true);
	}  // end there is loaded rawlog

	FlexGridSizer1->Layout();
}

void CFormEdit::OnrbLoadedSelect(wxCommandEvent& event)
{
	btnPickInput->Disable();
	btnPickOut->Disable();
	txtOutputFile->Disable();
	txtInputFile->Disable();

	slFrom->Enable();
	slTo->Enable();
	spinFirst->Enable();
	spinLast->Enable();

	btnKeep->Enable();
	btnDelete->Enable();
}

void CFormEdit::OnrbFileSelect(wxCommandEvent& event)
{
	btnPickInput->Enable();
	btnPickOut->Enable();
	txtOutputFile->Enable();
	txtInputFile->Enable();

	slFrom->Disable();
	slTo->Disable();
	spinFirst->Disable();
	spinLast->Disable();

	btnKeep->Disable();
	btnDelete->Disable();
}

void CFormEdit::OnbtnRemoveAllButByClassClick1(wxCommandEvent& event)
{
	WX_START_TRY

	loadSelectionsFromListBox(classNameOfObsToRemove, cbObsClass);
	if (classNameOfObsToRemove.empty())
		THROW_EXCEPTION("You must select one class name!");

	executeOperationOnRawlog(filter_remObsByClass, "Observations deleted:");
	Close();

	WX_END_TRY
}

void CFormEdit::OnbtnPickInputClick(wxCommandEvent& event)
{
	WX_START_TRY

	wxFileDialog dialog(
		this, _("Select input rawlog file") /* caption */,
		(iniFile->read_string(iniFileSect, "LastDir", ".").c_str()),
		_("*.rawlog") /* defaultFilename */,
		_("Rawlog files (*.rawlog,*.rawlog.gz)|*.rawlog;*.rawlog.gz|All files "
		  "(*.*)|*.*") /* wildcard */,
		wxFD_OPEN | wxFD_FILE_MUST_EXIST);

	if (dialog.ShowModal() != wxID_OK) return;

	// Save the path
	WX_START_TRY
	iniFile->write(
		iniFileSect, "LastDir", std::string(dialog.GetDirectory().mb_str()));
	WX_END_TRY

	txtInputFile->SetValue(dialog.GetPath());

	WX_END_TRY
}

void CFormEdit::OnbtnPickOutClick(wxCommandEvent& event)
{
	WX_START_TRY

	wxFileDialog dialog(
		this, _("Select output rawlog file") /* caption */,
		(iniFile->read_string(iniFileSect, "LastDir", ".").c_str()),
		_("*.rawlog") /* defaultFilename */,
		_("Rawlog files (*.rawlog,*.rawlog.gz)|*.rawlog;*.rawlog.gz|All files "
		  "(*.*)|*.*") /* wildcard */,
		wxFD_SAVE | wxFD_OVERWRITE_PROMPT);

	if (dialog.ShowModal() != wxID_OK) return;

	// Save the path
	WX_START_TRY
	iniFile->write(
		iniFileSect, "LastDir", std::string(dialog.GetDirectory().mb_str()));
	WX_END_TRY

	txtOutputFile->SetValue(dialog.GetPath());

	WX_END_TRY
}

/** This is the common function for all operations over a rawlog file ("filter"
 * a rawlog file into a new one) or over the loaded rawlog (depending on the
 * user selection in the GUI).
 */
void CFormEdit::executeOperationOnRawlog(
	TRawlogFilter operation, const char* endMsg)
{
	WX_START_TRY

	unsigned int processMax;
	bool isInMemory;
	CFileGZInputStream* in_fil = nullptr;
	CFileGZOutputStream* out_fil = nullptr;
	size_t first = 0, last = 0;

	if (rbLoaded->GetValue())
	{
		// APPLY TO rawlog in memory:
		isInMemory = true;

		processMax = (unsigned int)rawlog.size();
		first = spinFirst->GetValue();
		last = spinLast->GetValue();

		ASSERT_(last < processMax);
		ASSERT_(last >= first);
	}
	else
	{
		// APPLY TO rawlog files:
		isInMemory = false;

		if (!txtInputFile->GetValue().size())
			THROW_EXCEPTION("An input rawlog file must be selected");
		if (!txtOutputFile->GetValue().size())
			THROW_EXCEPTION("An output rawlog file must be selected");

		string fileName_IN(txtInputFile->GetValue().mbc_str());
		if (!fileExists(fileName_IN))
			THROW_EXCEPTION("Input file does not exist!");

		string fileName_OUT(txtOutputFile->GetValue().mbc_str());

		if (!fileName_OUT.compare(fileName_IN))
			THROW_EXCEPTION("Input and output files must be different!");

		in_fil = new CFileGZInputStream(fileName_IN);
		out_fil = new CFileGZOutputStream(fileName_OUT);

		processMax = (unsigned int)in_fil->getTotalBytesCount();
	}

	wxProgressDialog progDia(
		wxT("Modifying rawlog"), wxT("Processing..."),
		processMax,	 // range
		this,  // parent
		wxPD_CAN_ABORT | wxPD_APP_MODAL | wxPD_SMOOTH | wxPD_AUTO_HIDE |
			wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME);

	CRawlogTreeView::RAWLOG_UNDERGOING_CHANGES = true;

	wxTheApp->Yield();	// Let the app. process messages

	unsigned int countLoop = 0;
	bool keepLoading = true;
	string errorMsg;
	wxString auxStr;

	CSensoryFrame::Ptr dummy_sf = std::make_shared<CSensoryFrame>();

	// Apply changes:
	int changes = 0;
	wxBusyCursor cursor;

	while ((!isInMemory && keepLoading) ||
		   (isInMemory && countLoop < rawlog.size()))
	{
		CSerializable::Ptr newObj;
		try
		{
			if (isInMemory)
			{
				try
				{
					newObj = rawlog.getAsGeneric(countLoop);
				}
				catch (std::exception&)
				{
					break;
				}
			}
			else
			{
				archiveFrom(*in_fil) >> newObj;
			}

			// Check type:
			if (newObj->GetRuntimeClass() == CLASS_ID(CRawlog))
			{
				THROW_EXCEPTION(
					"File is a 'CRawlog' object. Please save it as a sequence "
					"of actions/observations.");
			}
			else if (newObj->GetRuntimeClass() == CLASS_ID(CSensoryFrame))
			{
				// A sensory frame:
				CSensoryFrame::Ptr sf =
					std::dynamic_pointer_cast<CSensoryFrame>(newObj);

				// Process & save:
				if (!isInMemory || (countLoop >= first && countLoop <= last))
					operation(nullptr, sf.get(), changes);

				if (!isInMemory) archiveFrom(*out_fil) << *sf;
			}
			else if (newObj->GetRuntimeClass()->derivedFrom(
						 CLASS_ID(CObservation)))
			{
				// A single observation:
				dummy_sf->clear();
				dummy_sf->insert(
					std::dynamic_pointer_cast<CObservation>(newObj));

				// Process & save:
				if (!isInMemory || (countLoop >= first && countLoop <= last))
					operation(nullptr, dummy_sf.get(), changes);

				// Still there?
				if (dummy_sf->size() == 1)
				{
					if (!isInMemory) archiveFrom(*out_fil) << *newObj;
				}
				else
				{
					if (isInMemory)
					{
						rawlog.remove(countLoop);
						countLoop--;
					}
				}
			}
			else if (newObj->GetRuntimeClass() == CLASS_ID(CActionCollection))
			{
				// This is an action:
				CActionCollection::Ptr acts =
					std::dynamic_pointer_cast<CActionCollection>(newObj);

				// Process & save:
				if (!isInMemory || (countLoop >= first && countLoop <= last))
					operation((CActionCollection*)acts.get(), nullptr, changes);

				if (!isInMemory) archiveFrom(*out_fil) << *acts;
			}
			else
			{  // Unknown class:
				THROW_EXCEPTION(format(
					"Unexpected class found in the file: '%s'",
					newObj->GetRuntimeClass()->className));
			}
		}
		catch (exception& e)
		{
			errorMsg = mrpt::exception_to_str(e);
			keepLoading = false;
		}
		catch (...)
		{
			keepLoading = false;
		}

		// Step counter & update progress dialog
		if (countLoop++ % 20 == 0)
		{
			auxStr.sprintf(
				wxT("Processing... (%u objects processed)"), countLoop);
			int curProgr = isInMemory ? countLoop : in_fil->getPosition();
			if (!progDia.Update(curProgr, auxStr)) keepLoading = false;
			wxTheApp->Yield();	// Let the app. process messages
		}

		// Delete only if processing file
		if (newObj && !isInMemory) { newObj.reset(); }

	}  // end while keep loading

	progDia.Update(processMax);	 // Close dialog.

	wxMessageBox(
		(format("%s %i\n\nEnd message:\n%s", endMsg, changes, errorMsg.c_str())
			 .c_str()),
		_("Result:"), wxOK, this);

	if (in_fil) delete in_fil;
	if (out_fil) delete out_fil;

	WX_END_TRY

	CRawlogTreeView::RAWLOG_UNDERGOING_CHANGES = false;
}

// Remove observations by class name:
std::vector<std::string> labelOfObsToRemove;

void filter_delObsByLabel(
	mrpt::obs::CActionCollection* acts, mrpt::obs::CSensoryFrame* SF,
	int& changesCount)
{
	if (SF)
	{
		for (auto it = SF->begin(); it != SF->end();)
		{
			CObservation::Ptr obs = *it;
			if (string::npos !=
				find_in_vector(obs->sensorLabel, labelOfObsToRemove))
			{
				// A match: Delete it:
				it = SF->erase(it);
				changesCount++;
			}
			else
				it++;
		}
	}
}

void filter_NotDelObsByLabel(
	mrpt::obs::CActionCollection* acts, mrpt::obs::CSensoryFrame* SF,
	int& changesCount)
{
	if (SF)
	{
		for (auto it = SF->begin(); it != SF->end();)
		{
			CObservation::Ptr obs = *it;
			if (string::npos ==
				find_in_vector(obs->sensorLabel, labelOfObsToRemove))
			{
				// A match: Delete it:
				it = SF->erase(it);
				changesCount++;
			}
			else
				it++;
		}
	}
}

void CFormEdit::OnRemoveByLabel(wxCommandEvent& event)
{
	WX_START_TRY

	loadSelectionsFromListBox(labelOfObsToRemove, cbObsLabel);
	if (labelOfObsToRemove.empty())
		THROW_EXCEPTION("You must select at least one label!");

	executeOperationOnRawlog(filter_delObsByLabel, "Observations deleted:");
	Close();

	WX_END_TRY
}

void CFormEdit::OnRemoveButLabel(wxCommandEvent& event)
{
	WX_START_TRY

	loadSelectionsFromListBox(labelOfObsToRemove, cbObsLabel);
	if (labelOfObsToRemove.empty())
		THROW_EXCEPTION("You must select at least one label!");

	executeOperationOnRawlog(filter_NotDelObsByLabel, "Observations deleted:");
	Close();

	WX_END_TRY
}

void CFormEdit::OnslFromCmdScroll(wxScrollEvent& event) {}
void CFormEdit::OnslFromCmdScroll1(wxScrollEvent& event) {}
double minPitchToDeleteLaserScan = 1.2_deg;

void leave_horizontalScans(
	mrpt::obs::CActionCollection* acts, mrpt::obs::CSensoryFrame* SF,
	int& changesCount)
{
	if (SF)
	{
		for (auto it = SF->begin(); it != SF->end();)
		{
			CObservation::Ptr obs = *it;

			if (IS_CLASS(*obs, CObservation2DRangeScan))
			{
				auto* o = static_cast<CObservation2DRangeScan*>(obs.get());

				if (fabs(o->sensorPose.pitch()) > minPitchToDeleteLaserScan)
				{
					it = SF->erase(it);
				}
				else
				{
					changesCount++;	 // Count success
					it++;
				}
			}
			else
				it++;
		}
	}
}

// Leave horizontal scans only:
void CFormEdit::OnbtnLeaveHorizScansClick(wxCommandEvent& event)
{
	WX_START_TRY

	minPitchToDeleteLaserScan =
		atof(string(edMaxPitch->GetValue().mb_str()).c_str());
	executeOperationOnRawlog(
		leave_horizontalScans, "Laser scans that passed the filter: ");

	WX_END_TRY
}
