// 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_WCHART_2D_RENDERER_H_
#define CHART_WCHART_2D_RENDERER_H_

#include <Wt/Chart/WAxis>

#include <Wt/WPainterPath>
#include <Wt/WRectF>
#include <Wt/WGlobal>

namespace Wt {

class WColor;
class WModelIndex;
class WPointF;
class WPainter;
class WString;

  namespace Chart {

class WDataSeries;
class WCartesianChart;

/*! \class SeriesIterator Wt/Chart/WChart2DRenderer Wt/Chart/WChart2DRenderer
 *  \brief Abstract base class for iterating over series data in
 *         WChart2DRenderer.
 *
 * This class is specialized for rendering series data.
 *
 * \sa WChart2DRenderer::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 WChart2DRenderer Wt/Chart/WChart2DRenderer Wt/Chart/WChart2DRenderer
 *  \brief Helper class for rendering a cartesian chart.
 *
 * This class is used by WCartesianChart during rendering, and
 * normally, you will not need to use this class directly. You may want to
 * specialize this class if you want to override particular aspects of how
 * the chart is renderered. In that case, you will want to instantiate the
 * specialized class in WCartesianChart::createRenderer().
 *
 * To simplify the simulatenous handling of Horizontal and Vertical
 * charts, the renderer makes abstraction of the orientation of the
 * chart: regardless of the chart orientation, the width() corresponds
 * to the length along the X axis, and height() corresponds to the
 * length along the Y axis. Similarly, calcChartArea() and chartArea()
 * return a rectangle where the bottom side corresponds to the lowest
 * displayed Y values, and the left side corresponds to the lowest
 * displayed X values. To map these "chart coordinates" to painter
 * coordinates, use one of the hv() methods.
 *
 * <i>Note, this class is part of the internal charting API, and may
 * be subject of changes and refactorings.</i>
 *
 * \ingroup charts
 */
class WT_API WChart2DRenderer
{
public:
  /*! \brief Creates a renderer.
   *
   * Creates a renderer for the cartesian chart <i>chart</i>, for rendering
   * in the specified <i>rectangle</i> of the <i>painter</i>.
   */
  WChart2DRenderer(WCartesianChart *chart,
		   WPainter& painter, const WRectF& rectangle);

  /*! \brief Destructor.
   */
  virtual ~WChart2DRenderer();

  /*! \brief Returns the corresponding chart.
   */
  WCartesianChart *chart() { return chart_; }

  /*! \brief Returns a reference to the painter.
   */
  WPainter& painter() { return painter_; }

  /*! \brief Returns the main plotting area rectangle.
   *
   * This area is calculated and cached by calcChartArea().
   */
  WRectF chartArea() { return chartArea_; }

  /*! \brief Calculates the main plotting area rectangle.
   *
   * This method calculates the main plotting area, and stores it in
   * the member chartArea_. The default implementation simply removes
   * the plot area padding from the entire painting rectangle.
   *
   * \sa WCartesianChart::plotAreaPadding().
   */
  virtual void calcChartArea();

  /*! \brief Initializes the layout.
   *
   * This computes the chart plotting area dimensions, and intializes
   * the axes so that they provide a suitable mapping from logical
   * coordinates to device coordinates.
   */
  bool initLayout();

  /*! \brief Renders the chart.
   *
   * This method renders the chart. The default implementation does
   * the following:
   * \if cpp
   * \code
   * calcChartArea();           // sets chartArea_
   * prepareAxes();             // provides logical dimensions to the axes
   *
   * renderBackground();        // render the background
   * renderAxes(Grid);          // render the grid
   * renderSeries();            // render the data series
   * renderAxes(Line | Labels); // render the axes (lines & labels)
   * renderLegend();            // render legend and titles
   * \endcode
   * \elseif java
   * \code
   * calcChartArea();           // sets chartArea_
   * prepareAxes();             // provides logical dimensions to the axes
   * 
   * renderBackground();        // render the background
   * renderAxes(Grid);          // render the grid
   * renderSeries();            // render the data series
   * renderAxes(AxisProperty.Line, AxisProperty.Labels); // render the axes (lines & labels) 
   * renderLegend();            // render legend and titles
   * \endcode
   * \endif
   *
   * You may want to reimplement this method to change the sequence of
   * steps for rendering the chart.
   */
  virtual void render();

  /*! \brief Maps a (X, Y) point to chart coordinates.
   *
   * This method maps the point with given (<i>xValue</i>,
   * <i>yValue</i>) to chart coordinates. The y value is mapped by one
   * of the Y axes indicated by <i>axis</i>.
   *
   * Note that chart coordinates may not be the same as painter
   * coordinates, because of the chart orientation. To map from chart
   * coordinates to painter coordinates, use hv().
   *
   * The <i>currentXSegment</i> and <i>currentYSegment</i> specify the
   * axis segments in which you wish to map the point.
   */
  virtual WPointF map(double xValue, double yValue, Axis axis = 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.
   */
  void renderLabel(const WString& text, const WPointF& pos,
		   const WColor& color, WFlags<AlignmentFlag> flags,
		   double angle, int margin);

  /*! \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 Enumeration that specifies a property of the axes.
   */
  enum AxisProperty {
    Labels = 0x1,  //! Labels property. 
    Grid = 0x2,    //! Grid property.
    Line = 0x4     //! Line property.
  };

protected:
  /*! \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 (we may
   * want to change that later to allow for reentrant rendering by
   * multiple renderers ?).
   */
  virtual bool prepareAxes();

  /*! \brief Renders the background.
   */
  virtual void renderBackground();

  /*! \brief Renders one or more properties of the axes.
   */
  virtual void renderAxes(WFlags<AxisProperty> properties);

  /*! \brief Renders all series data, including value labels.
   */
  virtual void renderSeries();

  /*! \brief Renders the (default) legend and chart titles.
   */
  virtual void renderLegend();

  /*! \brief Returns the width along the X axis (as if orientation is Vertical)
   */
  int width() const { return width_; }

  /*! \brief Returns the height along the Y axis (as if orientation is Vertical)
   */
  int height() const { return height_; }

  /*! \brief Returns the segment margin.
   *
   * This is the separation between segments, and defaults to 40 pixels.
   */
  int segmentMargin() const { return segmentMargin_; }

private:
  WCartesianChart *chart_;

  WPainter& painter_;
  int       width_;
  int       height_;
  int       segmentMargin_;

  WRectF    chartArea_;

  WPainterPath tildeStartMarker_, tildeEndMarker_;

protected:
  /*! \brief The computed axis locations.
   *
   * \sa prepareAxes()
   */
  AxisValue location_[3];

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

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

  /*! \brief Iterates over the series using an iterator.
   */
  void iterateSeries(SeriesIterator *iterator, bool reverseStacked = false);

  friend class WAxis;
};

W_DECLARE_OPERATORS_FOR_FLAGS(WChart2DRenderer::AxisProperty)

  }
}

#endif // CHART_WCHART_2D_RENDERER_H_
