// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WPAINTER_H_
#define WPAINTER_H_

#include <WBrush>
#include <WFont>
#include <WTransform>
#include <WPainterPath>
#include <WPen>

namespace Wt {

class WLineF;
class WPaintDevice;
class WPainterPath;
class WPointF;
class WRectF;

/*! \class WPainter WPainter WPainter
 *  \brief Helper class for painting on a WPaintDevice
 *
 * The painter class provides a rich interface for painting on a
 * WPaintDevice. To start painting on a device, either pass the device
 * through the constructor WPainter(WPaintDevice *), or use
 * begin(WPaintDevice *). Typically, you will instantiate a %WPainter
 * from within the WPaintedWidget::paintEvent() method.
 *
 * The painter maintains state such as the current \link setPen()
 * pen\endlink, \link setBrush() brush\endlink, \link setFont()
 * font\endlink, \link worldTransform() transformation\endlink and
 * clipping settings (see setClipping() and setClipPath()). A
 * particular state can be saved using save() and later restored using
 * restore().
 *
 * The painting system distinguishes between device coordinates,
 * logical coordinates, and drawing coordinates. The device coordinate
 * system ranges from (0, 0) in the top left corner of the device, to
 * (\link WPaintDevice::width device()->width()\endlink, \link
 * WPaintDevice::height device()->height()\endlink) for the bottom
 * right corner. The logical coordinate system defines a coordinate
 * system that may be chosen independent of the geometry of the
 * device, which is convenient to make abstraction of the actual
 * device size. Finally, the current drawing coordinate system may be
 * different from the logical coordinate system because of a world
 * transformation. Initially, the drawing coordinate system coincides
 * with the logical coordinate system, which coincides with the device
 * coordinate system.
 *
 * By setting a viewPort() and a window(), a viewPort transformation
 * is defined which maps logical coordinates onto device
 * coordinates. By changing the world transformation (using
 * setWorldTransform(), or translate(), rotate(), scale() operations),
 * it is defined how current drawing coordinates map onto logical
 * coordinates.
 *
 * Although the painter has support for clipping using an arbitrary
 * \link WPainterPath path\endlink, not all devices support clipping.
 *
 * \sa WPaintedWidget::paintEvent(WPaintDevice *)
 */
class WT_API WPainter
{
public:
  /*! \brief Enumeration for render hints
   */
  enum RenderHint {
    Antialiasing = 0x01 //!< Antialiasing
  };

  /*! \brief Default constructor.
   *
   * Before painting, you must invoke begin(WPaintDevice *) on a paint device.
   *
   * \sa WPainter(WPaintDevice *)
   */
  WPainter();

  /*! \brief Create a paint and initialize it for a paint device.
   */
  WPainter(WPaintDevice *device);

  /*! \brief Destructor.
   */
  ~WPainter();

  /*! \brief Begin painting on a paint device.
   *
   * Starts painting on a paint device. The paint device is
   * automatically cleared to become entirely transparent.
   *
   * \sa end(), isActive()
   */
  bool begin(WPaintDevice *device);

  /*! \brief Return whether this painter is active on a paint device.
   *
   * \sa begin(WPaintDevice *), end()
   */
  bool isActive() const;

  /*! \brief End painting.
   *
   * This method is called automatically from the destructor.
   */
  bool end();

  /*! \brief The device on which this painter is active (or 0 if not active).
   *
   * \sa begin(WPaintDevice *), WPainter(WPaintDevice *), isActive()
   */
  WPaintDevice *device() const { return device_; }

  /*! \brief Set a render hint.
   *
   * Renderers may ignore particular hints for which they have no
   * support.
   */
  void setRenderHint(RenderHint hint, bool on = true);

  /*! \brief Get the current render hints.
   *
   * Returns the logical OR of render hints currently set.
   *
   * \sa setRenderHint(RenderHint, bool).
   */
  int renderHints() const { return s().renderHints_; }

  /*! \brief Draw an arc.
   *
   * Draws an arc using the current pen, and fills using the current brush.
   *
   * The arc is defined as a segment from an ellipse, which fits in
   * the <i>rectangle</i>. The segment starts at <i>startAngle</i>, and
   * spans an angle given by <i>spanAngle</i>. These angles have as
   * unit 1/16th of a degree, and are measured counter-clockwise
   * starting from the 3 o'clock position.
   *
   * \sa drawEllipse(const WRectF&), drawChord(const WRectF&, int, int)
   * \sa drawArc(double, double, double, double, int, int)
   */
  void drawArc(const WRectF& rectangle, int startAngle, int spanAngle);

  /*! \brief Draw an arc.
   *
   * This is an overloaded method for convenience.
   *
   * \sa drawArc(const WRectF&, int, int)
   */
  void drawArc(double x, double y, double width, double height,
	       int startAngle, int spanAngle);

  /*! \brief Draw a chord.
   *
   * Draws an arc using the current pen, and connects start and end
   * point with a line. The area is filled using the current brush.
   *
   * The arc is defined as a segment from an ellipse, which fits in
   * the <i>rectangle</i>. The segment starts at <i>startAngle</i>, and
   * spans an angle given by <i>spanAngle</i>. These angles have as
   * unit 1/16th of a degree, and are measured counter-clockwise
   * starting at 3 o'clock.
   *
   * \sa drawEllipse(const WRectF&), drawArc(const WRectF&, int, int)
   * \sa drawChord(double, double, double, double, int, int)
   */
  void drawChord(const WRectF& rectangle, int startAngle, int spanAngle);

  /*! \brief Draw a chord.
   *
   * This is an overloaded method for convenience.
   *
   * \sa drawChord(const WRectF&, int, int)
   */
  void drawChord(double x, double y, double width, double height,
		 int startAngle, int spanAngle);

  /*! \brief Draw an ellipse.
   *
   * Draws an ellipse using the current pen and fills it using the
   * current brush.
   *
   * The ellipse is defined as being bounded by the <i>rectangle</i>.
   *
   * \sa drawArc(const WRectF&, int, int)
   * \sa drawEllipse(double, double, double, double)
   */
  void drawEllipse(const WRectF& rectangle);

  /*! \brief Draw an ellipse.
   *
   * This is an overloaded method for convenience.
   *
   * \sa drawEllipse(const WRectF&)
   */
  void drawEllipse(double x, double y, double width, double height);

  /*! \brief An image that can be rendered on a WPainter.
   *
   * The image is specified in terms of a URL, and the width and
   * height.
   *
   * \sa drawImage()
   */
  class Image {
  public:
    /*! \brief Create an image.
     *
     * Create an image which is located at the <i>uri</i>, and which has
     * dimensions <i>width</i> x <i>height</i>.
     */
    Image(const std::string& uri, int width, int height);

    /*! \brief Returns the uri.
     */
    std::string uri() const { return uri_; }

    /*! \brief Returns the image width.
     */
    int width() const { return width_; }

    /*! \brief Returns the image height.
     */
    int height() const { return height_; }

  private:
    std::string uri_;
    int width_, height_;
  };

  /*! \brief Draw an image.
   *
   * Draws the <i>image</i> so that the top left corner corresponds to
   * <i>point</i>.
   *
   * This is an overloaded method provided for convenience.
   */
  void drawImage(const WPointF& point, const Image& image);

  /*! \brief Draw part of an image.
   *
   * Draws the <i>sourceRect</i> rectangle from an image to the
   * location <i>point</i>.
   *
   * This is an overloaded method provided for convenience.
   */
  void drawImage(const WPointF& point, const Image& image,
		 const WRectF& sourceRect);

  /*! \brief Draw an image inside a rectangle.
   *
   * Draw the <i>image</i> inside <i>rect</i> (If necessary, the image
   * is scaled to fit into the rectangle).
   *
   * This is an overloaded method provided for convenience.
   */
  void drawImage(const WRectF& rect, const Image& image);

  /*! \brief Draw part of an image inside a rectangle.
   *
   * Draws the <i>sourceRect</i> rectangle from an image inside
   * <i>rect</i> (If necessary, the image is scaled to fit into the
   * rectangle).
   */
  void drawImage(const WRectF& rect, const Image& image,
		 const WRectF& sourceRect);

  /*! \brief Draw part of an image.
   *
   * Draws the <i>sourceRect</i> rectangle with top left corner
   * (<i>sx</i>, <i>sy</i>) and size <i>sw</i> x <i>sh</i> from an
   * image to the location (<i>x</i>, <i>y</i>).
   */
  void drawImage(double x, double y, const Image& image,
		 double sx = 0, double sy = 0, double sw = 0, double sh = 0);

  /*! \brief Draw a line.
   *
   * Draws a line using the current pen.
   *
   * \sa drawLine(const WPointF&, const WPointF&),
   *     drawLine(double, double, double, double)
   */  
  void drawLine(const WLineF& line);

  /*! \brief Draw a line.
   *
   * Draws a line defined by two points.
   *
   * \sa drawLine(const WLine&),
   *     drawLine(double, double, double, double)
   */  
  void drawLine(const WPointF& p1, const WPointF& p2);

  /*! \brief Draw a line.
   *
   * Draws a line defined by two points.
   *
   * \sa drawLine(const WLine&),
   *     drawLine(const WPointF&, const WPointF&)
   */  
  void drawLine(double x1, double y1, double x2, double y2);

  /*! \brief Draws an array of lines.
   *
   * Draws the <i>lineCount</i> first lines from the given array of lines.
   */  
  void drawLines(const WLineF *lines, int lineCount);

  /*! \brief Draws an array of lines.
   *
   * Draws <i>lineCount</i> lines, where each line is specified using
   * a begin and end point that are read from an array. Thus, the
   * <i>pointPairs</i> array must have at least 2*<i>lineCount</i>
   * points.
   */
  void drawLines(const WPointF *pointPairs, int lineCount);

  /*! \brief Draws an array of lines.
   *
   * Draws the lines given in the vector.
   */  
  void drawLines(const std::vector<WLineF>& lines);

  /*! \brief Draws an array of lines.
   *
   * Draws a number of lines that are specified by pairs of begin- and
   * endpoints. The vector should hold a number of points that is a
   * multiple of two.
   */  
  void drawLines(const std::vector<WPointF>& pointPairs);

  /*! \brief Draw a (complex) path.
   *
   * Draws and fills the given path using the current pen and brush.
   *
   * \sa strokePath(const WPainterPath&, const WPen&),
   *     fillPath(const WPainterPath&, const WBrush&)
   */  
  void drawPath(const WPainterPath& path);

  /*! \brief Draw a pie.
   *
   * Draws an arc using the current pen, and connects start and end
   * point with the center of the corresponding ellipse. The area is
   * filled using the current brush.
   *
   * The arc is defined as a segment from an ellipse, which fits in
   * the <i>rectangle</i>. The segment starts at <i>startAngle</i>, and
   * spans an angle given by <i>spanAngle</i>. These angles have as
   * unit 1/16th of a degree, and are measured counter-clockwise
   * starting at 3 o'clock.
   *
   * \sa drawEllipse(const WRectF&), drawArc(const WRectF&, int, int)
   * \sa drawPie(double, double, double, double, int, int)
   */
  void drawPie(const WRectF& rectangle, int startAngle, int spanAngle);

  /*! \brief Draw a pie.
   *
   * This is an overloaded method for convenience.
   *
   * \sa drawPie(const WRectF&, int, int)
   */
  void drawPie(double x, double y, double width, double height,
	       int startAngle, int spanAngle);

  /*! \brief Draw a point.
   *
   * Draw a single point using the current pen.
   *
   * \sa drawPoint(double, double)
   */
  void drawPoint(const WPointF& position);

  /*! \brief Draw a point.
   *
   * This is an overloaded method for convenience.
   *
   * \sa drawPoint(const WPointF&)
   */
  void drawPoint(double x, double y);

  /*! \brief Draw a number of points.
   *
   * Draws the <i>pointCount</i> first points from the given array of points.
   *
   * \sa drawPoint(const WPointF&)
   */
  void drawPoints(const WPointF *points, int pointCount);

  /*! \brief Draw a polygon.
   *
   * Draws a polygon that is specified by a list of points, using the
   * current pen. The polygon is closed by connecting the last point
   * with the first point, and filled using the current brush.
   *
   * \sa drawPath(const WPainterPath&), drawPolyline(const WPointF *, int)
   */
  void drawPolygon(const WPointF *points, int pointCount
		   /*, FillRule fillRule */);

  /*! \brief Draw a polyline.
   *
   * Draws a polyline that is specified by a list of points, using the
   * current pen.
   *
   * \sa drawPath(const WPainterPath&), drawPolygon(const WPointF *, int)
   */
  void drawPolyline(const WPointF *points, int pointCount);

  /*! \brief Draw a rectangle.
   *
   * Draws and fills a rectangle using the current pen and brush.
   *
   * \sa drawRect(double, double, double, double)
   */
  void drawRect(const WRectF& rectangle);

  /*! \brief Draw a rectangle.
   *
   * This is an overloaded method for convenience.
   *
   * \sa drawRect(const WRectF&)
   */
  void drawRect(double x, double y, double width, double height);

  /*! \brief Draw a number of rectangles.
   *
   * Draws and fills the <i>rectCount</i> first rectangles from the
   * given array, using the current pen and brush.
   *
   * \sa drawRect(const WRectF&)
   */
  void drawRects(const WRectF *rectangles, int rectCount);

  /*! \brief Draw a number of rectangles.
   *
   * Draws and fills a list of rectangles using the current pen and
   * brush.
   *
   * \sa drawRect(const WRectF&)
   */
  void drawRects(const std::vector<WRectF>& rectangles);
  
  /*! \brief Draw text.
   *
   * Draw text using inside the rectangle, using the current font. The
   * text is aligned inside the rectangle following alignment
   * indications given in <i>flags</i>. The text is drawn using the
   * current pen color (pen()) and font settings (font()).
   *
   * Flags is the logical OR of a horizontal and vertical
   * alignment. Horizontal alignment may be one of AlignLeft,
   * AlignCenter, or AlignRight. Vertical alignment is one of
   * AlignTop, AlignMiddle or AlignBottom.
   *
   * <i>Note: The SVG renderer ignores the vertical alignment options,
   * and aligns the text baseline to the top of the rectangle. To
   * minimize differences (if you wish to support Svg), you should use
   * AlignBottom or AlignMiddle. On the other hand, the SVG renderer
   * is the only renderer that currently supports all aspects of the
   * transformation for the text. The other renderers will always
   * render the text horizontally (unaffected by rotation), and
   * unaffected by the scaling component of the transformation
   * matrix.
   *
   * For the HtmlCanvas renderer, text is overlayed on top of painted
   * shapes, and is not covered by shapes that are painted after the
   * text (which is the expected behaviour, and the case for SVG and
   * VML).</i>
   */
  void drawText(const WRectF& rectangle, int flags, const WString& text);

  /*! \brief Draw text.
   *
   * This is an overloaded method for convenience.
   *
   * \sa drawText(const WRectF&, int, const WString&)
   */
  void drawText(double x, double y, double width, double height,
		int flags, const WString& text);

  /*! \brief Fill a (complex) path.
   *
   * Like drawPath(const WPainterPath&), but does not stroke the path,
   * and fills the path with the given <i>brush</i>.
   *
   * \sa drawPath(const WPainterPath&), strokePath(const WPainterPath&)
   */
  void fillPath(const WPainterPath& path, const WBrush& brush);

  /*! \brief Fill a rectangle.
   *
   * Like drawRect(const WRectF&), but does not stroke the rect, and
   * fills the rect with the given <i>brush</i>.
   *
   * \sa drawRect(const WPainterPath&)
   */
  void fillRect(const WRectF& rectangle, const WBrush& brush);

  /*! \brief Fill a rectangle.
   *
   * This is an overloaded method for convenience.
   *
   * \sa fillRect(const WRectF&, const WBrush&)
   */
  void fillRect(double x, double y, double width, double height,
		const WBrush& brush);

  /*! \brief Stroke a path.
   *
   * Like drawPath(const WPainterPath&), but does not fill the path,
   * and strokes the path with the given <i>pen</i>.
   *
   * \sa drawPath(const WRectF&), fillPath(const WRectF&, const WBrush&)
   */
  void strokePath(const WPainterPath& path, const WPen& pen);

  /*! \brief Change the fill style.
   *
   * Changes the fills style for subsequent draw operations.
   *
   * \sa brush(), setPen(const WPen&)
   */
  void setBrush(const WBrush& brush);

  /*! \brief Change the font.
   *
   * Changes the font for subsequent text rendering.
   *
   * \sa font(), drawText()
   */
  void setFont(const WFont& font);

  /*! \brief Change the pen.
   *
   * Changes the pen used for stroking subsequent draw operations.
   *
   * \sa pen(), setBrush(const WBrush&)
   */
  void setPen(const WPen& pen);

  /*! \brief Returns the current brush.
   *
   * Returns the brush style that is currently used for filling.
   *
   * \sa setBrush(const WBrush&)
   */
  const WBrush& brush() const { return s().currentBrush_; }

  /*! \brief Returns the current font.
   *
   * Returns the font that is currently used for rendering text.
   *
   * \sa setFont(const WFont&)
   */
  const WFont& font() const { return s().currentFont_; }

  /*! \brief Returns the current pen.
   *
   * Returns the pen that is currently used for stroking.
   *
   * \sa setPen(const WPen&)
   */
  const WPen& pen() const { return s().currentPen_; }

  /*! \brief Enable or disable clipping.
   *
   * Enables are disables clipping for subsequent operations using the
   * current clip path set using setClipPath().
   *
   * <i>Note: Clipping is not supported for the VML renderer.</i>
   *
   * \sa hasClipping(), setClipPath(const WPainterPath&)
   */
  void setClipping(bool enable);

  /*! \brief Return whether clipping is enabled.
   *
   * <i>Note: Clipping is not supported for the VML renderer.</i>
   *
   * \sa setClipping(bool), setClipPath(const WPainterPath&)
   */
  bool hasClipping() const { return s().clipping_; }

  /*! \brief Set the clip path.
   *
   * Sets the path that is used for clipping subsequent drawing
   * operations. The clip path is only used when clipping is enabled
   * using setClipping(bool).
   *
   * <i>Note: Clipping is not supported for the VML renderer.</i>
   *
   * \sa clipPath(), setClipping(bool)
   */
  void setClipPath(const WPainterPath& clipPath);

  /*! \brief Get the clip path.
   *
   * <i>Note: Clipping is not supported for the VML renderer.</i>
   *
   * \sa setClipPath(const WPainterPath&)
   */
  WPainterPath clipPath() const { return s().clipPath_; }

  /*! \brief Reset the current transformation.
   *
   * Resets the current transformation to the identity transformation
   * matrix, so that the logical coordinate system coincides with the
   * device coordinate system.
   */
  void resetTransform();

  /*! \brief Rotate the logical coordinate system.
   *
   * Rotates the logical coordinate system around its origin. The
   * <i>angle</i> is specified in degrees, and positive values are
   * clock-wise.
   *
   * \sa scale(double, double), translate(double, double), resetTransform()
   */
  void rotate(double angle);

  /*! \brief Scale the logical coordinate system.
   *
   * Scales the logical coordinate system around its origin, by a factor
   * in the X and Y directions.
   *
   * \sa rotate(double), translate(double, double), resetTransform()
   */
  void scale(double sx, double sy);

  /*! \brief Translate the origin of the logical coordinate system.
   *
   * Translates the origin of the logical coordinate system to a new
   * location relative to the current logical coordinate system.
   *
   * \sa translate(double, double), rotate(double),
   *     scale(double, double), resetTransform()
   */
  void translate(const WPointF& offset);

  /*! \brief Translate the origin of the logical coordinate system.
   *
   * Translates the origin of the logical coordinate system to a new
   * location relative to the logical coordinate system.
   *
   * \sa translate(const WPointF& offset), rotate(double),
   *     scale(double, double), resetTransform()
   */
  void translate(double dx, double dy);

  /*! \brief Set a transformation for the logical coordinate system.
   *
   * Sets a new transformation which transforms logical coordinates to
   * device coordinates. When <i>combine</i> is true, the
   * transformation is combined with the current world transformation
   * matrix.
   *
   * \sa worldTransform()
   * \sa rotate(double), scale(double, double), translate(double, double)
   * \sa resetTransform()
   */
  void setWorldTransform(const WTransform& matrix, bool combine = false);

  /*! \brief Get the current world transformation matrix.
   *
   * \sa setWorldTransform()
   */
  const WTransform& worldTransform() const { return s().worldTransform_; }

  /*! \brief Save the current state.
   *
   * A copy of the current state is saved on a stack. This state will
   * may later be restored by popping this state from the stack using
   * restore().
   *
   * The state that is saved is the current \link setPen()
   * pen\endlink, \link setBrush() brush\endlink, \link setFont()
   * font\endlink, \link worldTransform() transformation\endlink and
   * clipping settings (see setClipping() and setClipPath()).
   *
   * \sa restore()
   */
  void save();

  /*! \brief Get the last save state.
   *
   * Pops the last saved state from the state stack.
   *
   * \sa save()
   */
  void restore();

  /*! \brief Set the viewport.
   *
   * Selects the part of the device that will correspond to the logical
   * coordinate system.
   *
   * By default, the viewport spans the entire device: it is the
   * rectangle (0, 0) to (device->width(), device->height()). The
   * window defines how the viewport is mapped to logical coordinates.
   *
   * \sa viewPort(), setWindow(const WRectF&)
   */
  void setViewPort(const WRectF& viewPort);

  /*! \brief Set the viewport.
   *
   * This is an overloaded method for convenience.
   *
   * \sa setViewPort(const WRectF&)
   */
  void setViewPort(double x, double y, double width, double height);

  /*! \brief Returns the viewport.
   *
   * \sa setViewPort(const WRectF&)
   */
  WRectF viewPort() const { return viewPort_; }

  /*! \brief Set the window.
   *
   * Defines the viewport rectangle in logical coordinates, and thus how
   * logical coordinates map onto the viewPort.
   *
   * By default, is (0, 0) to (device->width(), device->height()). Thus,
   * the default window and viewport leave logical coordinates identical
   * to device coordinates.
   *
   * \sa window(), setViewPort(const WRectF&)
   */
  void setWindow(const WRectF& window);

  /*! \brief Set the window.
   *
   * This is an overloaded method for convenience.
   *
   * \sa setWindow(const WRectF&)
   */
  void setWindow(double x, double y, double width, double height);

  /*! \brief Returns the current window.
   *
   * \sa setViewPort(const WRectF&)
   */
  WRectF window() const { return window_; }

  /*! \brief Returns the combined transformation matrix.
   *
   * Returns the transformation matrix that maps coordinates to device
   * coordinates. It is the combination of the current world
   * transformation (which defines the transformation within the
   * logical coordinate system) and the window/viewport transformation
   * (which transforms logical coordinates to device coordinates).
   *
   * \sa setWorldTransform(), setViewPort(), setWindow()
   */
  WTransform combinedTransform() const;

private:
  WPaintDevice *device_;
  WRectF        viewPort_, window_;
  WTransform    viewTransform_;

  struct State {
    WTransform    worldTransform_;
    WBrush        currentBrush_;
    WFont         currentFont_;
    WPen          currentPen_;
    int           renderHints_;
    WPainterPath  clipPath_;
    bool          clipping_;

    State();
  };

  std::vector<State> stateStack_;

  State& s() { return stateStack_.back(); }
  const State& s() const { return stateStack_.back(); }

  void recalculateViewTransform();
};

}

#endif // WPAINTER_H_
