/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2024, 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/core/safe_pointers.h>
#include <mrpt/img/CCanvas.h>
#include <mrpt/math/math_frwds.h>

namespace mrpt::img
{
/** This class represents a Windows Enhanced Meta File (EMF) for generating and
 * saving graphics.
 *  If used under Linux, a ".png", non-vectorial, file will be generated
 * instead.
 * \ingroup mrpt_img_grp
 */
class CEnhancedMetaFile : public CCanvas
{
 private:
  void_ptr_noncopy m_hdc;
  int m_scale;
  void_ptr_noncopy m_hFont{nullptr};
  std::string m_targetFile;

 public:
  /** In Linux, the size of the bitmap image that emulates the EMF
   * (Default:800) */
  static void LINUX_IMG_WIDTH(int value);
  static int LINUX_IMG_WIDTH();
  /** In Linux, the size of the bitmap image that emulates the EMF
   * (Default:600) */
  static void LINUX_IMG_HEIGHT(int value);
  static int LINUX_IMG_HEIGHT();

  /** Constructor
   *  \param targetFileName This file will be created and the EMF saved
   * there.
   *  \param scaleFactor All coordinates in draw commands will be internally
   * multiplied by this scale, to provide a way of obtaining "subpixel"
   * drawing.
   */
  CEnhancedMetaFile(const std::string& targetFileName, int scaleFactor = 1);

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

  /** Changes the value of the pixel (x,y).
   *  Pixel coordinates starts at the left-top corner of the image, and start
   * in (0,0).
   *  The meaning of the parameter "color" depends on the implementation: it
   * will usually
   *   be a 24bit RGB value (0x00RRGGBB), but it can also be just a 8bit gray
   * level.
   *  This method must support (x,y) values OUT of the actual image size
   * without neither
   *   raising exceptions, nor leading to memory access errors.
   */
  void setPixel(int x, int y, size_t color) override;

  /** Returns the width of the image in pixels (this currently has no
   * applicability for a EMF file...) */
  size_t getWidth() const override { return 640; }
  /** Returns the height of the image in pixels (this currently has no
   * applicability for a EMF file...) */
  size_t getHeight() const override { return 480; }
  /** Draws an image as a bitmap at a given position.
   * \param x0 The top-left corner x coordinates on this canvas where the
   * image is to be drawn
   * \param y0 The top-left corner y coordinates on this canvas where the
   * image is to be drawn
   * \param img The image to be drawn in this canvas
   *  This method may be redefined in some classes implementing this
   * interface in a more appropriate manner.
   */
  void drawImage(int x, int y, const mrpt::img::CImage& img) override;

  /** Draws a line.
   * \param x0 The starting point x coordinate
   * \param y0 The starting point y coordinate
   * \param x1 The end point x coordinate
   * \param y1 The end point y coordinate
   * \param color The color of the line
   * \param width The desired width of the line (this is IGNORED in this
   * virtual class)
   *  This method may be redefined in some classes implementing this
   * interface in a more appropriate manner.
   */
  void line(
      int x0,
      int y0,
      int x1,
      int y1,
      const mrpt::img::TColor color,
      unsigned int width = 1,
      TPenStyle penStyle = psSolid) override;

  /** Places a text label.
   * \param x0 The x coordinates
   * \param y0 The y coordinates
   * \param str The string to put
   * \param color The text color
   * \param fontSize The font size, in "points"
   *  This method may be redefined in some classes implementing this
   * interface in a more appropriate manner.
   * \sa rectangle
   */
  void textOut(int x0, int y0, const std::string& str, const mrpt::img::TColor color) override;

  /** Select the current font used when drawing text.
   * \param fontName The face name of a font (e.g. "Arial","System",...)
   * \param fontSize The size of the font in pts.
   * \param bold Whether the font is bold
   * \param italic Whether the font is italic
   * \sa textOut, CCanvas::selectTextFont
   */
  virtual void selectVectorTextFont(
      const std::string& fontName, int fontSize, bool bold = false, bool italic = false);

  /** Draws an image as a bitmap at a given position, with some custom scale
   * and rotation changes.
   * \param x0 The top-left corner x coordinates on this canvas where the
   * image is to be drawn
   * \param y0 The top-left corner y coordinates on this canvas where the
   * image is to be drawn
   * \param rotation The rotation in radians, positive values being
   * anti-clockwise direction, 0 is the normal position.
   * \param scale The scale factor, e.g. 2 means twice the original size.
   * \param img The image to be drawn in this canvas
   *  This method may be redefined in some classes implementing this
   * interface in a more appropriate manner.
   */
  void drawImage(int x, int y, const mrpt::img::CImage& img, float rotation, float scale) override
  {
    CCanvas::drawImage(x, y, img, rotation, scale);
  }

  /** Draws a rectangle (an empty rectangle, without filling)
   * \param x0 The top-left x coordinate
   * \param y0 The top-left y coordinate
   * \param x1 The right-bottom x coordinate
   * \param y1 The right-bottom y coordinate
   * \param color The color of the line
   * \param width The desired width of the line.
   * \sa filledRectangle
   */
  virtual void rectangle(
      int x0, int y0, int x1, int y1, const mrpt::img::TColor color, unsigned int width = 1);

  /** Draws an ellipse representing a given confidence interval of a 2D
   * Gaussian distribution.
   * \param mean_x The x coordinate of the center point of the ellipse.
   * \param mean_y The y coordinate of the center point of the ellipse.
   * \param cov2D A 2x2 covariance matrix.
   * \param confIntervalStds How many "sigmas" for the confidence level (i.e.
   * 2->95%, 3=99.97%,...)
   * \param color The color of the ellipse
   * \param width The desired width of the line (this is IGNORED in this
   * virtual class)
   * \param nEllipsePoints The number of points to generate to approximate
   * the ellipse shape.
   * \exception std::exception On an invalid matrix.
   */
  template <class T>
  void ellipseGaussian(
      mrpt::math::CMatrixDynamic<T>* cov2D,
      T mean_x,
      T mean_y,
      float confIntervalStds = 2,
      const mrpt::img::TColor& color = mrpt::img::TColor(255, 255, 255),
      unsigned int width = 1,
      int nEllipsePoints = 20)
  {
    MRPT_START
    int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
    double ang;
    math::CMatrixDynamic<T> eigVal, eigVec;
    int i;

    // Compute the eigen-vectors & values:
    cov2D->eigenVectors(eigVec, eigVal);

    eigVal.Sqrt();
    math::CMatrixDynamic<T> M(eigVal * (~eigVec));

    // Compute the points of the 2D ellipse:
    for (i = 0, ang = 0; i < nEllipsePoints; i++, ang += (M_2PI / (nEllipsePoints - 1)))
    {
      float ccos = cos(ang);
      float ssin = sin(ang);

      x2 = round(mean_x + confIntervalStds * (ccos * M(0, 0) + ssin * M(1, 0)));
      y2 = round(mean_y + confIntervalStds * (ccos * M(0, 1) + ssin * M(1, 1)));

      if (i > 0) line(x1, y1, x2, y2, color, width);

      x1 = x2;
      y1 = y2;
    }  // end for points on ellipse

    MRPT_END_WITH_CLEAN_UP(std::cout << "Covariance matrix leading to error is:" << std::endl
                                     << *cov2D << std::endl;);
  }
};  // End of class def.
}  // namespace mrpt::img
