// 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_WDATA_SERIES_H_
#define CHART_WDATA_SERIES_H_

#include <Wt/Chart/WAxis>
#include <Wt/WBrush>
#include <Wt/WPen>

namespace Wt {
  namespace Chart {

/*! \brief Enumeration that specifies the type of a chart series.
 *
 * \sa WDataSeries::setType(SeriesType)
 * \sa WCartesianChart
 *
 * \ingroup charts
 */
enum SeriesType {
  PointSeries,  //!< Series rendered solely as point markers.
  LineSeries,   //!< Series rendered as points connected by straight lines.
  CurveSeries,  //!< Series rendered as points connected by curves.
  BarSeries     //!< Series rendered as bars.
};

/*! \brief Enumeration that specifies a type of point marker.
 *
 * \sa WDataSeries::setMarker(MarkerType marker)
 * \sa WCartesianChart
 *
 * \ingroup charts
 */
enum MarkerType {
  NoMarker,       //!< Do not draw point markers.
  SquareMarker,   //!< Mark points using a square.
  CircleMarker,   //!< Mark points using a circle.
  CrossMarker,    //!< Mark points using a cross (+).
  XCrossMarker,   //!< Mark points using a cross (x).
  TriangleMarker  //!< Mark points using a triangle.
};

/*! \brief Enumeration that specifies how an area should be filled.
 *
 * Data series of type LineSeries or CurveSerie may be filled under or
 * above the line or curve. This enumeration specifies the other limit
 * of this fill.
 *
 * \sa WDataSeries::setFillRange(FillRange range)
 * \sa WCartesianChart
 *
 * \ingroup charts
 */
enum FillRangeType {
  NoFill,           //!< Do not fill under the curve.
  MinimumValueFill, //!< Fill from the curve to the chart bottom (min)
  MaximumValueFill, //!< Fill from the curve to the chart top
  ZeroValueFill     //!< Fill from the curve to the zero Y value.
};

/*! \class WDataSeries Wt/Chart/WDataSeries Wt/Chart/WDataSeries
 *  \brief A single data series in a cartesian chart.
 *
 * This class configures all aspects for rendering a single data series
 * in a cartesian chart. A data series renders Y data from a single
 * model column against the X series configured for the chart.
 *
 * The data column should contain data that can be converted to
 * a number, but should not necessarily be of a number type, see
 * also asNumber(const boost::any&).
 *
 * Multiple series of different types may be combined on a single chart.
 *
 * \image html ChartWDataSeries-1.png "Different styles of data series"
 * 
 * For a category chart, series may be stacked on top of each other.
 * This is controlled by setStacked(bool stacked) for a series, which
 * if enabled, will stack that series on top of the preceding data
 * series. This works regardless of whether they are of the same type,
 * but obviously works visually best if these series are of the same
 * type. When not stacked, bar series are rendered next to each other.
 * The margin between bars of different data series is controlled using
 * WCartesianChart::setBarMargin(double).
 *
 * The line and color type are by default based on the \link
 * WCartesianChart::palette() chart palette\endlink, but may be
 * overridden for a series using setPen(const WPen&),
 * setBrush(const WBrush&), etc...
 *
 * \sa WCartesianChart::addSeries(const WDataSeries&)
 *
 * \ingroup charts
 */
class WDataSeries
{
public:
  /*! \brief Enumeration that indicates an aspect of the look.
   *
   * These flags are used to keep track of which aspects of the look
   * that are overridden from the values provided by the chart palette,
   * using one of the methods in this class.
   *
   * \sa setPen(), setBrush(), setMarkerPen(), setMarkerBrush(), setLabelColor()
   */
  enum CustomFlag {
    CustomPen = 0x1,         //!< A custom pen is set.
    CustomBrush = 0x2,       //!< A custom brush is set.
    CustomMarkerPen = 0x4,   //!< A custom marker pen is set.
    CustomMarkerBrush = 0x8, //!< A custom marker brush is set.
    CustomLabelColor = 0x10  //!< A custom label color is set.
  };

  /*! \brief Construct a new data series.
   *
   * Creates a new data series which plots the Y values from the
   * model column <i>modelColumn</i>, with the indicated
   * <i>seriesType</i>. The Y values are mapped to the indicated
   * <i>axis</i>, which should correspond to one of the two Y axes.
   *
   * \sa WCartesianChart::addSeries(const WDataSeries&)
   */
  WDataSeries(int modelColumn, SeriesType seriesType = PointSeries,
	      Axis axis = Y1Axis);

  /*! \brief Change the series type.
   *
   * The series type specifies how the data is plotted, i.e. using
   * mere point markers, lines, curves, or bars.
   */
  void setType(SeriesType t);

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

  /*! \brief Change the model column.
   *
   * This specifies the model column from which the Y data is retrieved
   * that is plotted by this series.
   *
   * The data column should contain data that can be converted to
   * a number (but should not necessarily be of a number type).
   *
   * \sa asNumber(const boost::any&)
   */
  void setModelColumn(int modelColumn);

  /*! \brief Returns the model column.
   *
   * \sa setModelColumn(int)
   */
  int modelColumn() const { return modelColumn_; }

  /*! \brief Sets whether this series is stacked on top of the preceding series.
   *
   * For category charts, data from different series may be rendered
   * stacked on top of each other. The rendered value is the sum of the
   * value of this series plus the rendered value of the preceding
   * series. For line series, you probably will want to add filling
   * under the curve. A stacked bar series is rendered by a bar on top
   * of the preceding bar series.
   *
   * The default value is false.
   */
  void setStacked(bool stacked);

  /*! \brief Returns whether this series is stacked on top of the preceding
   *         series.
   *
   * \sa setStacked(bool)
   */
  bool isStacked() const { return stacked_; }

  /*! \brief Bind this series to a chart axis.
   *
   * A data series may be bound to either the first or second Y axis.
   * Note that the second Y axis is by default not displayed.
   *
   * The default value is the first Y axis.
   *
   * \sa WAxis::setDisplayEnabled(bool)
   */
  void bindToAxis(Axis axis);

  /*! \brief Returns the chart axis used for this series.
   *
   * \sa bindToAxis(Axis)
   */
  Axis axis() const { return axis_; }

  /*! \brief Set which aspects of the look are overriden.
   *
   * Set which aspects of the look, that are by default based on the
   * chart palette, or overridden by custom settings.
   *
   * The default value is 0 (nothing overridden).
   */
  void setCustomFlags(int customFlags);

  /*! \brief Returns which aspects of the look are overriden.
   *
   * \sa setCustomFlags(int)
   */
  int curstomFlags() const { return customFlags_; }

  /*! \brief Override the pen used for drawing lines for this series.
   *
   * Overrides the pen that is used to draw this series. Calling this
   * method automatically adds CustomPen to the custom flags.
   *
   * The default value is a default WPen().
   *
   * \sa WChartPalette:strokePen(int), WChartPalette::borderPen(int)
   */
  void setPen(const WPen& pen);

  /*! \brief Returns the pen used for drawing lines for this series.
   *
   * \sa setPen(const WPen&)
   */
  WPen pen() const;

  /*! \brief Override the brush used for filling areas for this series.
   *
   * Overrides the brush that is used to draw this series. For a bar plot,
   * this is the brush used to fill the bars. For a line chart, this is the
   * brush used to fill the area under (or above) the line. Calling
   * this method automatically adds CustomBrush to the custom flags.
   *
   * \sa WChartPalette::brush(int)
   */
  void setBrush(const WBrush& brush);

  /*! \brief Returns the brush used for filling areas for this series.
   *
   * \sa setBrush(const WBrush&)
   */
  WBrush brush() const;

  /*! \brief Sets the fill range for line or curve series.
   *
   * Line or curve series may be filled under or above the curve, using the
   * brush(). This setting specifies the range that is filled.
   */
  void setFillRange(FillRangeType fillRange);

  /*! \brief Returns the fill range for line or curve series.
   *
   * \sa setFillRange(FillRangeType)
   */
  FillRangeType fillRange() const { return fillRange_; }

  /*! \brief Sets the data point marker.
   *
   * Specifies a marker that is displayed at the (X,Y) coordinate for each
   * series data point.
   *
   * The default value is a CircleMarker for a PointSeries, or NoMarker
   * otherwise.
   *
   * \sa setMarkerPen(const WPen&), setMarkerBrush(const WBrush&).
   */
  void setMarker(MarkerType marker);

  /*! \brief Returns the data point marker.
   *
   * \sa setMarker(MarkerType)
   */
  MarkerType marker() const { return marker_; }

  /*! \brief Sets the marker pen.
   *
   * Overrides the pen used for stroking the marker. By default the
   * marker pen is the same as pen(). Calling this method automatically adds
   * CustomMarkerPen to the custom flags.
   *
   * \sa setPen(const WPen&), setMarkerBrush(const WBrush&)
   */
  void setMarkerPen(const WPen& pen);

  /*! \brief Returns the marker pen.
   *
   * \sa setMarkerPen(const WPen&)
   */
  WPen markerPen() const;

  /*! \brief Sets the marker brush.
   *
   * Overrides the brush used for filling the marker. By default the
   * marker brush is the same as brush(). Calling this method
   * automatically adds CustomMarkerBrush to the custom flags.
   *
   * \sa setBrush(const WBrush&), setMarkerPen(const WPen&)
   */
  void setMarkerBrush(const WBrush& brush);

  /*! \brief Returns the marker brush.
   *
   * \sa setMarkerBrush(const WBrush&)
   */
  WBrush markerBrush() const;

  /*! \brief Enable the entry for this series in the legend.
   *
   * When <i>enabled</i>, this series is added to the chart
   * legend.
   *
   * The default value is true.
   *
   * \sa WCartesianChart::setLegendEnabled(bool).
   */
  void setLegendEnabled(bool enabled);

  /*! \brief Returns whether this series has an entry in the legend.
   *
   * \sa setLegendEnabled(bool)
   */
  bool isLegendEnabled() const { return legend_; }

  /*! \brief Enable a label that is shown at the series data points.
   *
   * You may enable labels for the XAxis, YAxis or both axes. The
   * label that is displayed is the corresponding value on that
   * axis. If both labels are enabled then they are combined in a
   * single text using the format: "<x-value>: <y-value>".
   *
   * The default values are false for both axes (no labels).
   *
   * \sa isLabelsEnabled()
   */
  void setLabelsEnabled(Axis axis, bool enabled = true);

  /*! \brief Returns whether labels are enabled for the given axis.
   *
   * \sa setLabelsEnabled(Axis, bool)
   */
  bool isLabelsEnabled(Axis axis) const;

  /*! \brief Set the label color.
   *
   * Specify the color used for the rendering labels at the data
   * points.
   *
   * \sa setLabelsEnabled(Axis, bool)
   */
  void setLabelColor(const WColor& color);

  /*! \brief Returns the label color.
   *
   * \sa setLabelColor(const WColor&)
   */
  WColor labelColor() const;

private:
  WCartesianChart *chart_;
  int              modelColumn_;
  bool             stacked_;
  SeriesType       type_;
  Axis             axis_;
  int              customFlags_;
  WPen             pen_, markerPen_;
  WBrush           brush_, markerBrush_;
  WColor           labelColor_;
  FillRangeType    fillRange_;
  MarkerType       marker_;
  bool             legend_;
  bool             xLabel_;
  bool             yLabel_;

  template <typename T>
  bool set(T& m, const T& v);

  void setChart(WCartesianChart *chart);
  void update();

  friend class WCartesianChart;
};

template <typename T>
bool WDataSeries::set(T& m, const T& v)
{
  if (m != v) {
    m = v;
    update();
    return true;
  } else
    return false;
}

  }
}

#endif // CHART_WDATA_SERIES_H_
