// 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 CHART_WCARTESIAN_CHART_H_
#define CHART_WCARTESIAN_CHART_H_

#include <Wt/Chart/WAbstractChart>
#include <Wt/Chart/WAxis>
#include <Wt/Chart/WDataSeries>
#include <Wt/Chart/WLegend>
#include <Wt/WPaintDevice>
#include <Wt/WContainerWidget>

namespace Wt {

class WAbstractItemModel;
class WPainter;
class WPainterPath;

  namespace Chart {

/*! \class SeriesIterator Wt/Chart/WCartesianChart Wt/Chart/WCartesianChart
 *  \brief Abstract base class for iterating over series data in a chart.
 *
 * This class is specialized for rendering series data.
 *
 * \sa WCartesianChart::iterateSeries()
 *
 * \ingroup charts
 */
class WT_API SeriesIterator {
public:
  /*! \brief Destructor.
   */
  virtual ~SeriesIterator();

  /*! \brief Start handling a new segment.
   *
   * Because of a 'break' specified in an axis, axes may be divided in
   * one or two segments (in fact only the API limits this now to
   * two). The iterator will iterate all segments seperately, but each time
   * with a different clipping region specified in the painter, corresponding
   * to that segment.
   *
   * The <i>currentSegmentArea</i> specifies the clipping area.
   */
  virtual void startSegment(int currentXSegment, int currentYSegment,
			    const WRectF& currentSegmentArea);

  /*! \brief End handling a particular segment.
   *
   * \sa startSegment()
   */
  virtual void endSegment();

  /*! \brief Start iterating a particular series.
   *
   * Returns whether the series values should be iterated.
   * The <i>groupWidth</i> is the width (in pixels) of a single bar
   * group. The chart contains <i>numBarGroups</i>, and the current
   * series is in the <i>currentBarGroup</i>'th group.
   */
  virtual bool startSeries(const WDataSeries& series, double groupWidth,
			   int numBarGroups, int currentBarGroup);

  /*! \brief End iterating a particular series.
   */
  virtual void endSeries();

  /*! \brief Process a value.
   *
   * Processes a value with model coordinates (<i>x</i>,
   * <i>y</i>). The y value may differ from the model's y value,
   * because of stacked series. The y value here corresponds to the
   * location on the chart, after stacking.
   *
   * The <i>stackY</i> argument is the y value from the previous
   * series (also after stacking). It will be 0, unless this series is
   * stacked.
   */
  virtual void newValue(const WDataSeries& series, double x, double y,
			double stackY, const WModelIndex& xIndex,
			const WModelIndex& yIndex);

  /*! \brief Returns the current X segment.
   */
  int currentXSegment() const { return currentXSegment_; }

  /*! \brief Returns the current Y segment.
   */
  int currentYSegment() const { return currentYSegment_; }

  static void setPenColor(WPen& pen,
			  const WModelIndex& xIndex,
			  const WModelIndex& yIndex,
			  int colorRole);

  static void setBrushColor(WBrush& brush,
			    const WModelIndex& xIndex,
			    const WModelIndex& yIndex,
			    int colorRole);

private:
  int currentXSegment_, currentYSegment_;
};

class WChart2DImplementation;
class WChartPalette;

/*! \class WCartesianChart Wt/Chart/WCartesianChart Wt/Chart/WCartesianChart
 *  \brief A cartesian chart.
 *
 * A cartesian chart is a chart that uses X and Y axes. It can display
 * one or multiple data series, which each may be rendered using bars,
 * lines, areas, or points.
 *
 * To use a cartesian chart, the minimum you need to do is set a model
 * using setModel(), set the model column that holds the X data using
 * setXSeriesColumn(int modelColumn), and add one or more series using
 * addSeries(const WDataSeries&). Each series corresponds to one data
 * column that holds Y data.
 *
 * A cartesian chart is either a \link Chart::CategoryChart
 * CategoryChart\endlink or a \link Chart::ScatterPlot
 * ScatterPlot\endlink.
 *
 * In a <b>CategoryChart</b>, the X series represent different
 * categories, which are listed consecutively in model row order. The
 * X axis scale is set to \link Chart::CategoryScale
 * CategoryScale\endlink.
 *
 * \image html ChartWCartesianChart-1.png "A category chart with bar series"
 *
 * Each series may be rendered differently, and this is configured in
 * the data series (see WDataSeries for more information).
 *
 * In a <b>ScatterPlot</b>, the X series data are interpreted as
 * numbers on a numerical scale. The scale for the X axis defaults to
 * a \link Chart::LinearScale LinearScale\endlink, but this may be
 * changed to a \link Chart::DateScale DateScale\endlink when the X
 * series contains dates (of type WDate) to create a time series
 * chart, or to a \link Chart::LogScale LogScale\endlink. A
 * ScatterPlot supports the same types of data series as a
 * CategoryChart, but does not support stacking. In a scatter plot,
 * the X series do not need to be ordered in increasing values, and
 * may be set differently for each dataseries using
 * WDataSeries::setXSeriesColumn(int modelColumn).
 *
 * \image html ChartWCartesianChart-2.png "A time series scatter plot with line series"
 *
 * Missing data in a model series Y values is interpreted as a
 * <i>break</i>. For curve-like series, this breaks the curve (or
 * line).
 *
 * The cartesian chart has support for dual Y axes. Each data series may
 * be bound to one of the two Y axes. By default, only the first Y axis
 * is displayed. To show the second Y axis you will need to call:
 * 
 * \if cpp
 * \code
 * chart->axis(Y2Axis).setVisible(true);
 * \endcode
 * \endif
 *
 * By default a chart has a horizontal X axis and a vertical Y axis,
 * which corresponds to a \link Wt::Vertical Vertical\endlink
 * orientation. The orientation may be changed to \link Wt::Horizontal
 * Horizontal\endlink using setOrientation().
 *
 * The styling of the series data are dictated by a palette which may
 * be set using setPalette(WChartPalette *), but may be overridden by
 * settings in each data series.
 *
 * <h3>CSS</h3>
 *
 * Styling through CSS is not applicable.
 *
 * \sa WDataSeries, WAxis
 * \sa WPieChart
 *
 * \ingroup charts modelview
 */
class WT_API WCartesianChart : public WAbstractChart
{

public:
  /*! \brief Creates a new cartesian chart.
   *
   * Creates a cartesian chart of type \link Wt::Chart::CategoryChart
   * CategoryChart\endlink.
   */
  WCartesianChart(WContainerWidget *parent = 0);

  /*! \brief Creates a new cartesian chart.
   *
   * Creates a cartesian chart of the indicated \p type.
   */
  WCartesianChart(ChartType type, WContainerWidget *parent = 0);

  ~WCartesianChart();

  /*! \brief Sets the chart type.
   *
   * The chart type determines how (x,y) data are interpreted. In a
   * \link Wt::Chart::CategoryChart CategoryChart\endlink, the X
   * values are categories, and these are plotted consecutively,
   * evenly spaced, and in row order. In a \link
   * Wt::Chart::ScatterPlot ScatterPlot\endlink, the X values are
   * interpreted numerically (as for Y values).
   *
   * The default chart type is a \link Wt::Chart::CategoryChart
   * CategoryChart\endlink.
   *
   * \sa type()
   * \sa WAxis::setScale(), axis(Axis)
   */
  void setType(ChartType type);

  /*! \brief Returns the chart type.
   *
   * \sa setType()
   */
  ChartType type() const { return type_; }

  /*! \brief Sets the chart orientation.
   *
   * Sets the chart orientation, which corresponds to the orientation of
   * the Y axis: a Wt::Vertical orientation corresponds to the conventional
   * way of a horizontal X axis and vertical Y axis. A Wt::Horizontal
   * orientation is the other way around.
   *
   * The default orientation is Wt::Vertical.
   *
   * \sa orientation()
   */
  void setOrientation(Orientation orientation);

  /*! \brief Returns the chart orientation.
   *
   * \sa setOrientation()
   */  
  Orientation orientation() const { return orientation_; }

  /*! \brief Sets the the model column for the X series.
   *
   * Use this method to specify the default data for the X series.
   * For a \link Wt::Chart::ScatterPlot ScatterPlot\endlink this is
   * mandatory if an X series is not specified for every
   * \link Wt::Chart::WDataSeries WDataSeries\endlink. For a
   * \link Wt::Chart::CategoryChart CategoryChart\endlink, if not
   * specified, an increasing series of integer numbers will be
   * used (1, 2, ...).
   *
   * Scatterplot dataseries may each individually be given its own
   * X series data using WDataSeries::setXSeriesColumn(int modelColumn)
   *
   * The default value is -1 (not specified).
   *
   * The series column is reset to -1 when the model is set (or
   * changed). Thus you need to set a model before configuring the
   * series.
   *
   * \sa XSeriesColumn()
   */
  void setXSeriesColumn(int modelColumn);

  /*! \brief set the pen used to render the labels
   *
   * This method overwrites the pen for all axes
   *
   * \sa WAxis::setTextPen()
   */
  void setTextPen(const WPen& pen);

  /*! \brief Returns the model column for the X series.
   *
   * \sa setXSeriesColumn()
   */
  int XSeriesColumn() const { return XSeriesColumn_; }

  //later, activates a 3D plot
  //void setXYData(int modelColumnX, int modelColumnY);
  //bool is3D() const;

  /*! \brief Adds a data series.
   *
   * A single chart may display one or more data series. Each data series
   * displays data from a single model column in the chart. Series are
   * plotted in the order that they have been added to the chart.
   *
   * The series column is reset to -1 when the model is set (or
   * changed). Thus you need to set a model before configuring the
   * series.
   *
   * \sa removeSeries(), setSeries()
   */
  void addSeries(const WDataSeries& series);

  /*! \brief Removes a data series.
   *
   * This removes the first data series which plots the given
   * \p modelColumn.
   *
   * \sa addSeries(), setSeries()
   */
  void removeSeries(int modelColumn);

  /*! \brief Sets all data series.
   *
   * Replaces the current list of series with the new list.
   *
   * \sa series(), addSeries(), removeSeries()
   */
  void setSeries(const std::vector<WDataSeries>& series);

  /*! \brief Returns a data series corresponding to a data column.
   *
   * Returns a reference to the first data series that plots data
   * from \p modelColumn.
   */
  WDataSeries& series(int modelColumn);

  /*! \brief Returns a data series corresponding to a data column.
   *
   * Returns a const reference to the first data series that plots data
   * from \p modelColumn.
   */
  const WDataSeries& series(int modelColumn) const;

  /*! \brief Returns a list with the current data series.
   *
   * Returns the complete list of current data series.
   *
   * \sa setSeries(const std::vector<WDataSeries>&)
   */
  const std::vector<WDataSeries>& series() const { return series_; }

  /*! \brief Returns a chart axis.
   *
   * Returns a reference to the specified \p axis.
   */
  WAxis& axis(Axis axis);

  /*! \brief Accesses a chart axis.
   *
   * Returns a const reference to the specified \p axis.
   */
  const WAxis& axis(Axis axis) const;

  /*! \brief Sets an axis
   *
   * \if cpp
   * Ownership is transferred and a previously set (default) axis is deleted
   * first.
   * \endif
   *
   * \sa axis(Axis axis)
   */
  void setAxis(WAxis *waxis, Axis axis);

  /*! \brief Sets the margin between bars of different series.
   *
   * Use this method to change the margin that is set between bars of
   * different series. The margin is specified as a fraction of the
   * width. For example, a value of 0.1 adds a 10% margin between bars
   * of each series. Negative values are also allowed. For example, use
   * a margin of -1 to plot the bars of different series on top of
   * each other.
   *
   * The default value is 0.
   */
  void setBarMargin(double margin);

  /*! \brief Returns the margin between bars of different series.
   *
   * \sa setBarMargin(double)
   */
  double barMargin() const { return barMargin_; }

  /*! \brief Enables the legend.
   *
   * The location of the legend can be configured using
   * setLegendLocation(). Only series for which the legend is enabled
   * are included in this legend.
   *
   * The default value is \c false.
   *
   * \sa see WDataSeries::isLegendEnabled(), setLegendLocation()
   */
  void setLegendEnabled(bool enabled);

  /*! \brief Returns whether the legend is enabled.
   *
   * \sa setLegendEnabled()
   */
  bool isLegendEnabled() const { return legend_.isLegendEnabled(); }

  /*! \brief Configures the legend location.
   *
   * The legend can be renderd either inside or outside of the chart
   * area. When \p location is \link Wt::Chart::LegendInside
   * Chart::LegendInside\endlink, the legend will be rendered inside
   * the chart. When \p location is \link Wt::Chart::LegendOutside
   * Chart::Legendoutside\endlink, the legend is rendered outside the
   * chart, in the chart padding area.
   *
   * The provided \p side can either be Wt::Left, Wt::Right, Wt::Top,
   * Wt::Bottom and configures the side of the chart at which the
   * legend is displayed.
   *
   * The \p alignment specifies how the legend is aligned. This can be
   * a horizontal alignment flag (Wt::AlignLeft, Wt::AlignCenter, or
   * Wt::AlignRight), when the \p side is Bottom or Top, or a vertical
   * alignment flag (Wt::AlignTop, Wt::AlignMiddle, or Wt::AlignBottom)
   * when the \p side is Left or Right.
   *
   * The default location is \link Wt::Chart::LegendOutside
   * Chart::LegendOutside\endlink, Wt::Right and Wt::AlignMiddle.
   *
   * To have more control over the legend, you could reimplement the
   * renderLegendItem() method to customize how one item in the legend
   * is rendered, or, alternatively you can disable the legend
   * generated by the chart itself, and reimplement the paint() method
   * in which you use the renderLegendItem() method repeatedly to
   * render a customized legend.
   *
   * \sa WDataSeries::setLegendEnabled()
   */
  void setLegendLocation(LegendLocation location, Side side,
			 AlignmentFlag alignment);

  /*! \brief Configures the legend decoration.
   *
   * This configures the font, border and background for the legend.
   *
   * The default font is a 10pt sans serif font (the same as the
   * default axis label font), the default \p border is \link
   * Wt::NoPen NoPen\endlink and the default \p background is \link
   * Wt::NoBrush NoBrush\endlink.
   *
   * \sa setLegendEnabled()
   */
  void setLegendStyle(const WFont& font, const WPen& border,
		      const WBrush& background);

  /*! \brief Returns the legend location.
   *
   * \sa setLegendLocation()
   */
  LegendLocation legendLocation() const { return legend_.legendLocation(); }

  /*! \brief Returns the legend side.
   *
   * \sa setLegendLocation()
   */
  Side legendSide() const { return legend_.legendSide(); }

  /*! \brief Returns the legend alignment.
   *
   * \sa setLegendLocation()
   */
  AlignmentFlag legendAlignment() const { return legend_.legendAlignment(); }

  /*! \brief Returns the legend columns.
   *
   * \sa setLegendColumns()
   */
  int legendColumns() const { return legend_.legendColumns(); }

  /*! \brief Returns the legend column width.
   *
   * \sa setLegendColumns()
   */
  WLength legendColumnWidth() const { return legend_.legendColumnWidth(); }

  /*! \brief Returns the legend font.
   *
   * \sa setLegendStyle()
   */
  WFont legendFont() const { return legend_.legendFont(); }

  /*! \brief Returns the legend border pen.
   *
   * \sa setLegendStyle()
   */
  WPen legendBorder() const { return legend_.legendBorder(); }

  /*! \brief Returns the legend background brush.
   *
   * \sa setLegendStyle()
   */
  WBrush legendBackground() const { return legend_.legendBackground(); }

  /*! \brief Configures multiple legend columns.
   *
   * Multiple columns are typically useful when placing the legend at
   * the top or at the bottom of the chart.
   *
   * The default value is a single column, 100 pixels wide.
   *
   * When automatic chart layout is enabled, then the legend column width
   * is computed automatically, and this setting is ignored.
   *
   * \sa setAutoLayoutEnabled()
   */
  void setLegendColumns(int columns, const WLength& width);
  
  virtual void paint(WPainter& painter, const WRectF& rectangle = WRectF())
    const;

  /*! \brief Draws the marker for a given data series.
   *
   * Draws the marker for the indicated \p series in the \p result.
   * This method is called while painting the chart, and you may
   * want to reimplement this method if you wish to provide a custom
   * marker for a particular data series.
   *
   * \sa setLegendEnabled()
   */
  virtual void drawMarker(const WDataSeries& series, WPainterPath& result)
    const;

  /*! \brief Renders the legend icon for a given data series.
   *
   * Renders the legend icon for the indicated \p series in the
   * \p painter at position \p pos.
   *
   * This method is called while rendering a legend item, and you may
   * want to reimplement this method if you wish to provide a custom
   * legend icon for a particular data series.
   *
   * \sa renderLegendItem()
   */
  virtual void renderLegendIcon(WPainter& painter, const WPointF& pos,
				const WDataSeries& series) const;

  /*! \brief Renders the legend item for a given data series.
   *
   * Renders the legend item for the indicated \p series in the
   * \p painter at position \p pos. The default implementation
   * draws the marker, and the series description to the right. The
   * series description is taken from the model's header data for that
   * series' data column.
   *
   * This method is called while painting the chart, and you may
   * want to reimplement this method if you wish to provide a custom
   * marker for a particular data series.
   *
   * \sa setLegendEnabled()
   */
  virtual void renderLegendItem(WPainter& painter, const WPointF& pos,
				const WDataSeries& series) const;

  /*! \brief Maps from device coordinates to model coordinates.
   *
   * Maps a position in the chart back to model coordinates.
   *
   * This uses the axis dimensions that are based on the latest chart
   * rendering. If you have not yet rendered the chart, or wish to already
   * the mapping reflect model changes since the last rendering, you should
   * call initLayout() first.
   *
   * \sa mapToDevice()
   */
  WPointF mapFromDevice(const WPointF& point,
			Axis ordinateAxis = OrdinateAxis) const;

  /*! \brief Maps model values onto chart coordinates.
   *
   * This returns the chart device coordinates for a (x,y) pair of model
   * values.
   *
   * This uses the axis dimensions that are based on the latest chart
   * rendering. If you have not yet rendered the chart, or wish to already
   * the mapping reflect model changes since the last rendering, you should
   * call initLayout() first.
   *
   * The \p xSegment and \p ySegment arguments are relevant only when
   * the corresponding axis is broken using WAxis::setBreak(). Then,
   * its possible values may be 0 (below the break) or 1 (above the
   * break).
   *
   * \sa mapFromDevice()
   */
  WPointF mapToDevice(const boost::any& xValue, const boost::any& yValue,
		      Axis axis = OrdinateAxis, int xSegment = 0,
		      int ySegment = 0) const;

  /*! \brief Initializes the chart layout.
   *
   * The mapping between model and device coordinates is only
   * established after a rendering phase, or after calling initLayout
   * manually.
   *
   * You need a layout in order to use the mapFromDevice() and
   * mapToDevice() methods.
   *
   * Unless a specific chart rectangle is specified, the entire widget
   * area is assumed.
   */
  bool initLayout(const WRectF& rectangle = WRectF(), WPaintDevice *device = 0)
    const;

  /*! \brief Creates a widget which renders the a legend item.
   * 
   * The legend item widget will contain a text and a WPaintedWidget 
   * which draws the series' symbol.
   */
  WWidget* createLegendItemWidget(int index);

  /*! \brief Adds a data point area (used for displaying e.g. tooltips).
   *
   * You may want to specialize this is if you wish to modify (or
   * delete) the area.
   *
   * \note Currently, an area is only created if the Wt::ToolTipRole data at the
   *       data point is not empty.
   */
  virtual void addDataPointArea(const WDataSeries& series,
				const WModelIndex& xIndex,
				WAbstractArea *area);
  /*! \brief Sets the padding between the chart area and the axes.
   *
   * \sa axisPadding()
   */
  void setAxisPadding(int axisPadding);

  /*! \brief Returns the padding between the chart area and the axes.
   *
   * \sa setAxisPadding()
   */
  int axisPadding() const { return axisPadding_; }

  void iterateSeries(SeriesIterator *iterator,
		     WPainter *painter, bool reverseStacked = false) const;

private:
  WChart2DImplementation *interface_;

  Orientation orientation_;
  int XSeriesColumn_;
  ChartType type_;
  std::vector<WDataSeries> series_;
  WAxis *axes_[3];
  double barMargin_;
  WLegend legend_;
  int axisPadding_;
  WPen textPen_;

  /* render state */
  mutable int width_, height_;
  mutable WRectF chartArea_;
  mutable AxisValue location_[3];

  void init();

 protected:
  virtual void modelColumnsInserted(const WModelIndex& parent,
				    int start, int end);
  virtual void modelColumnsRemoved(const WModelIndex& parent,
				   int start, int end);
  virtual void modelRowsInserted(const WModelIndex& parent,
				 int start, int end);
  virtual void modelRowsRemoved(const WModelIndex& parent,
				int start, int end);
  virtual void modelDataChanged(const WModelIndex& topLeft,
			        const WModelIndex& bottomRight);
  virtual void modelHeaderDataChanged(Orientation orientation,
				      int start, int end);

  virtual void modelChanged();
  virtual void modelReset();

  /** @name Rendering logic
   */
  //@{
  /*! \brief Paints the widget.
   *
   * This calls render() to paint on the paint device.
   */
  void paintEvent(WPaintDevice *paintDevice);

  /*! \brief Renders the chart.
   *
   * Renders the chart within the given rectangle. To accomodate both
   * rendering of horizontal and vertically oriented charts, all rendering
   * logic assumes horizontal. This "chart coordinates" space is transformed
   * to painter coordinates using hv().
   *
   * \if cpp
   * The default implementation looks like:
   * \code
   * painter.save();
   * painter.translate(rectangle.topLeft());
   *
   * if (initLayout(rectangle)) {
   *   renderBackground(painter);
   *   renderGrid(painter, axis(XAxis));
   *   renderGrid(painter, axis(Y1Axis));
   *   renderGrid(painter, axis(Y2Axis));
   *   renderSeries(painter);
   *   renderAxes(painter, Line | Labels);
   *   renderLegend(painter);
   * }
   *
   * painter.restore();
   * \endcode
   * \endif
   */
  virtual void render(WPainter& painter, const WRectF& rectangle) const;

  /*! \brief Map (x, y) value pair to chart coordinates coordinates.
   *
   * The result needs further transformation using hv() to painter
   * coordinates.
   */
  virtual WPointF map(double xValue, double yValue, Axis yAxis = OrdinateAxis,
		      int currentXSegment = 0, int currentYSegment = 0)
    const;

  /*! \brief Utility function for rendering text.
   *
   * This method renders text on the chart position <i>pos</i>, with a
   * particular alignment <i>flags</i>. These are both specified in
   * chart coordinates. The position is converted to painter
   * coordinates using hv(), and the alignment flags are changed
   * accordingly. The rotation, indicated by <i>angle</i> is specified
   * in painter coordinates and thus an angle of 0 always indicates
   * horizontal text, regardless of the chart orientation.
   */
  virtual void renderLabel(WPainter& painter, const WString& text,
			   const WPointF& pos,
			   WFlags<AlignmentFlag> flags, double angle,
			   int margin) const;

  /*! \brief Conversion between chart and painter coordinates.
   *
   * Converts from chart coordinates to painter coordinates, taking
   * into account the chart orientation.
   */
  WPointF hv(double x, double y) const;

  /*! \brief Conversion between chart and painter coordinates.
   *
   * Converts from chart coordinates to painter coordinates, taking
   * into account the chart orientation.
   */
  WPointF hv(const WPointF& f) const;

  /*! \brief Conversion between chart and painter coordinates.
   *
   * Converts from chart coordinates to painter coordinates, taking
   * into account the chart orientation.
   */
  WRectF hv(const WRectF& f) const;

  /*! \brief Returns the segment area for a combination of X and Y
   *         segments.
   *
   * This segment area is used for clipping when rendering in a
   * particular segment.
   */
  WRectF chartSegmentArea(WAxis yAxis, int xSegment, int ySegment) const;

  /*! \brief Calculates the chart area.
   *
   * This calculates the chartArea(), which is the rectangle (in chart
   * coordinates) that bounds the actual chart (thus excluding axes,
   * labels, titles, legend, etc...).
   *
   * \sa plotAreaPadding()
   */
  virtual void calcChartArea() const;

  /*! \brief Prepares the axes for rendering.
   *
   * Computes axis properties such as the range (if not manually
   * specified), label interval (if not manually specified) and axis
   * locations. These properties are stored within the axes.
   *
   * \sa initLayout()
   */
  virtual bool prepareAxes() const;

  /*! \brief Renders the background.
   *
   * \sa render()
   */
  virtual void renderBackground(WPainter& painter) const;

  /*! \brief Renders one or more properties of the axes.
   *
   * This calls renderAxis() for each axis.
   *
   * \sa render()
   */
  virtual void renderAxes(WPainter& painter,
			  WFlags<AxisProperty> properties) const;

  /*! \brief Renders all series data, including value labels.
   *
   * \sa render()
   */
  virtual void renderSeries(WPainter& painter) const;

  /*! \brief Renders the (default) legend and chart titles.
   *
   * \sa render()
   */
  virtual void renderLegend(WPainter& painter) const;

  /*! \brief Renders properties of one axis.
   *
   * \sa renderAxes()
   */
  virtual void renderAxis(WPainter& painter, const WAxis& axis,
			  WFlags<AxisProperty> properties) const;

  /*! \brief Renders grid lines along the ticks of the given axis.
   *
   * \sa render()
   */
  virtual void renderGrid(WPainter& painter, const WAxis& axis) const;

  /*! \brief Calculates the total number of bar groups.
   */
  int calcNumBarGroups();

  using WWebWidget::render;

private:
  int calcNumBarGroups() const;
  int seriesIndexOf(int modelColumn) const;

  WPointF hv(double x, double y, double width) const;
  WPointF inverseHv(double x, double y, double width) const;

  class IconWidget : public WPaintedWidget {
  public:
    IconWidget(WCartesianChart *chart, 
	       int index, 
	       WContainerWidget *parent = 0);
    
  protected:
    virtual void paintEvent(WPaintDevice *paintDevice);
    
  private:
    WCartesianChart* chart_;
    int index_;
  };

  friend class WDataSeries;
  friend class SeriesRenderer;
  friend class LineSeriesRenderer;
  friend class BarSeriesRenderer;
  friend class LabelRenderIterator;
  friend class MarkerRenderIterator;
};

  }
}

#endif // CHART_WCARTESIAN_CHART_H_
