/* +------------------------------------------------------------------------+
   |                     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          |
   +------------------------------------------------------------------------+ */
#pragma once

#include <mrpt/gui/CBaseGUIWindow.h>
#include <mrpt/gui/gui_frwds.h>
#include <mrpt/img/CImage.h>
#include <mrpt/math/CMatrixDynamic.h>
#include <mrpt/math/CVectorDynamic.h>

namespace mrpt::gui
{
/** Create a GUI window and display plots with MATLAB-like interfaces and
 * commands.
 *
 *  For a list of supported events with the observer/observable pattern, see the
 * discussion in mrpt::gui::CBaseGUIWindow.
 *
 *   See CDisplayWindowPlots::plot
 *
 *
 *
 * ![mrpt::gui::CDisplayWindowPlots screenshot](preview_CDisplayWindowPlots.png)
 *
 * \ingroup mrpt_gui_grp
 */
class CDisplayWindowPlots : public mrpt::gui::CBaseGUIWindow
{
   public:
	using Ptr = std::shared_ptr<CDisplayWindowPlots>;

	/** Type for the callback function used in setMenuCallback */
	using TCallbackMenu =
		void (*)(int menuID, float cursor_x, float cursor_y, void* userParam);

   protected:
	friend class CWindowDialogPlots;

	/** Whether hold_on is enabled */
	bool m_holdon{false};
	bool m_holdon_just_disabled{false};
	/** Counter for hold_on */
	uint32_t m_holdon_cnt{0};
	TCallbackMenu m_callback{nullptr};
	void* m_callback_param{nullptr};

	void internal_plot(
		mrpt::math::CVectorFloat& x, mrpt::math::CVectorFloat& y,
		const std::string& lineFormat, const std::string& plotName);
	template <typename VECTOR1, typename VECTOR2>
	void internal_plot_interface(
		const VECTOR1& x, const VECTOR2& y, const std::string& lineFormat,
		const std::string& plotName)
	{
		mrpt::math::CVectorFloat x1(x.size()), y1(y.size());
		const auto N1 = size_t(x.size());
		for (size_t i = 0; i < N1; i++)
			x1[i] = mrpt::d2f(x[i]);
		const auto N2 = size_t(y.size());
		for (size_t i = 0; i < N2; i++)
			y1[i] = mrpt::d2f(y[i]);
		this->internal_plot(x1, y1, lineFormat, plotName);
	}
	template <typename VECTOR1>
	void internal_plot_interface(
		const VECTOR1& y, const std::string& lineFormat,
		const std::string& plotName)
	{
		const auto N = size_t(y.size());
		mrpt::math::CVectorFloat x1(N), y1(N);
		for (size_t i = 0; i < N; i++)
		{
			x1[i] = i;
			y1[i] = y[i];
		}
		this->internal_plot(x1, y1, lineFormat, plotName);
	}

   public:
	/** Constructor
	 */
	CDisplayWindowPlots(
		const std::string& windowCaption = std::string(),
		unsigned int initialWidth = 350, unsigned int initialHeight = 300);

	/** Class factory returning a smart pointer, equivalent to
	 * `std::make_shared<>(...)` */
	static CDisplayWindowPlots::Ptr Create(
		const std::string& windowCaption, unsigned int initialWindowWidth = 400,
		unsigned int initialWindowHeight = 300);

	/** Destructor
	 */
	~CDisplayWindowPlots() override;

	/** Gets the last x,y pixel coordinates of the mouse. \return False if the
	 * window is closed. */
	bool getLastMousePosition(int& x, int& y) const override;

	/** Set cursor style to default (cursorIsCross=false) or to a cross
	 * (cursorIsCross=true) */
	void setCursorCross(bool cursorIsCross) override;

	/** Resizes the window, stretching the image to fit into the display area.
	 */
	void resize(unsigned int width, unsigned int height) override;

	/** Changes the position of the window on the screen.
	 */
	void setPos(int x, int y) override;

	/** Changes the window title text.
	 */
	void setWindowTitle(const std::string& str) override;

	/** Enable/disable the feature of pan/zoom with the mouse (default=enabled)
	 */
	void enableMousePanZoom(bool enabled);

	/** Adds a new layer with a 2D plot based on two vectors of X and Y points,
	 *using a MATLAB-like syntax.
	 *  Each call to this function creates a new plot, unless the plot name
	 *coincides with an already existing plot: in this case the X & Y points
	 *are used to update this existing layer (this also applies to using the
	 *default plot name).
	 *  If "hold_on" is enabled, then every call will always create a new plot,
	 *even if no "plotName" is provided.
	 *
	 *  The lineFormat string is a combination of the following characters:
	 * - Line styles:
	 *		- '.': One point for each data point
	 *		- '-': A continuous line
	 *		- ':': A dashed line
	 * - Colors:
	 *		- k: black
	 *		- r: red
	 *		- g: green
	 *		- b: blue
	 *		- m: magenta
	 *		- c: cyan
	 * - Line width:
	 *		- '1' to '9': The line width (default=1)
	 *
	 *  Examples:
	 *   - 'r.' -> red points.
	 *   - 'k3' or 'k-3' -> A black line with a line width of 3 pixels.
	 * \note The vectors x & y can be of types: float or double.
	 * \sa axis, axis_equal, axis_fit, clear, hold_on, hold_off
	 * \tparam VECTOR Can be std::vector<float/double> or
	 *mrpt::dynamicsize_vector<float/double> or a column/row Eigen::Matrix<>
	 */
	template <typename VEC1, typename VEC2, typename = typename VEC2::Scalar>
	inline void plot(
		const VEC1& x, const VEC2& y,
		const std::string& lineFormat = std::string("b-"),
		const std::string& plotName = std::string("plotXY"))
	{
		this->internal_plot_interface(x, y, lineFormat, plotName);
	}
	//! \overload (for std::vector)
	template <typename T>
	inline void plot(
		const std::vector<T>& x, const std::vector<T>& y,
		const std::string& lineFormat = std::string("b-"),
		const std::string& plotName = std::string("plotXY"))
	{
		this->internal_plot_interface(x, y, lineFormat, plotName);
	}

	//! \overload
	template <typename VEC>
	void plot(
		const VEC& y, const std::string& lineFormat = std::string("b-"),
		const std::string& plotName = std::string("plotXY"))
	{
		this->internal_plot_interface(y, lineFormat, plotName);
	}

	/** Set the view area according to the passed coordinated. */
	void axis(
		float x_min, float x_max, float y_min, float y_max,
		bool aspectRatioFix = false);

	/** Enable/disable the fixed X/Y aspect ratio fix feature
	 * (default=disabled). */
	void axis_equal(bool enable = true);

	/** Fix automatically the view area according to existing graphs. */
	void axis_fit(bool aspectRatioFix = false);

	/** Plots a 2D ellipse given its mean, covariance matrix, and
	 *  Each call to this function creates a new plot, unless the plot name
	 * coincides with an already existing plot: in this case the new values are
	 * used to update this existing layer (this also applies to using the
	 * default plot name).
	 *  If "hold_on" is enabled, then every call will always create a new plot,
	 * even if no "plotName" is provided.
	 *
	 *  For a description of lineFormat see CDisplayWindowPlots::plot.
	 *  The "quantiles" value determines the confidence interval for the
	 * ellipse:
	 *     - 1 : 68.27% confidence interval
	 *     - 2 : 95.45%
	 *     - 3 : 99.73%
	 *     - 4 : 99.994%
	 * \note This method can be called with 2x2 fixed-sized or dynamic-size
	 * matrices of types: float or double.
	 * \sa axis, axis_equal, axis_fit, hold_on, hold_off
	 */
	template <typename T>
	void plotEllipse(
		const T mean_x, const T mean_y,
		const mrpt::math::CMatrixDynamic<T>& cov22, const float quantiles,
		const std::string& lineFormat = std::string("b-"),
		const std::string& plotName = std::string("plotEllipse"),
		bool showName = false);

	//! \overload
	template <typename T>
	void plotEllipse(
		const T mean_x, const T mean_y,
		const mrpt::math::CMatrixFixed<T, 2, 2>& cov22, const float quantiles,
		const std::string& lineFormat = std::string("b-"),
		const std::string& plotName = std::string("plotEllipse"),
		bool showName = false);

	/** Adds a bitmap image layer.
	 *  Each call to this function creates a new layer, unless the plot name
	 * coincides with an already existing plot: in this case the new values are
	 * used to update this existing layer (this also applies to using the
	 * default plot name).
	 *
	 * \sa axis, axis_equal, axis_fit, hold_on, hold_off
	 */
	void image(
		const mrpt::img::CImage& img, float x_left, float y_bottom,
		float x_width, float y_height,
		const std::string& plotName = std::string("image"));

	/** Remove all plot objects in the display.
	 * \sa plot
	 */
	void clear();

	/** Remove all plot objects in the display (clear and clf do exactly the
	 * same).
	 * \sa plot, hold_on, hold_off
	 */
	inline void clf() { clear(); }
	/** Enables keeping all the graphs, instead of overwriting them.
	 * \sa hold_off, plot
	 */
	void hold_on();

	/** Disables keeping all the graphs (this is the default behavior).
	 * \sa hold_on, plot
	 */
	void hold_off();

	/** Disables keeping all the graphs (this is the default behavior).
	 * \param label The text that appears in the new popup menu item.
	 * \param menuID Any positive number (0,1,..). Used to tell which menu was
	 * selected in the user callback.
	 * \sa setMenuCallback
	 */
	void addPopupMenuEntry(const std::string& label, int menuID);

	/** Must be called to have a callback when the user selects one of the
	 * user-defined entries in the popup menu.
	 * \sa addPopupMenuEntry
	 */
	void setMenuCallback(TCallbackMenu userFunction, void* userParam = nullptr);

};	// End of class def.
}  // namespace mrpt::gui
