/*  -*- C++ -*-
    This file is part of the KDE libraries
    Copyright (C) 2003 Jason Harris <kstars@30doradus.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#ifndef KPLOTWIDGET_H
#define KPLOTWIDGET_H

#include <kdeui_export.h>

#include <QtGui/QFrame>
#include <QtCore/QList>

class KPlotAxis;
class KPlotObject;
class KPlotPoint;

/**
 *@class KPlotWidget
 *
 *@short Generic data plotting widget.
 *
 *Widget for drawing plots. The basic idea behind KPlotWidget is that 
 *you don't have to worry about any transformation from your data's 
 *natural units to screen pixel coordinates; this is handled internally
 *by the widget.  
 *
 *Data to be plotted are represented by one or more instances of 
 *KPlotObject.  KPlotObject contains a list of QPointFs to be plotted 
 *(again, in the data's natural units), as well as information about how 
 *the data are to be rendered in the plot (i.e., as separate points or 
 *connected by lines?  With what color and point style? etc).  See 
 *KPlotObject for more information.
 *
 *KPlotWidget automatically adds axis labels with tickmarks and tick 
 *labels.  These are encapsulated in the KPlotAxis class.  All you have 
 *to do is set the limits of the plotting area in data units, and 
 *KPlotWidget wil figure out the optimal positions and labels for the 
 *tickmarks on the axes.
 *
 *Example of usage:
 *
 * @code
KPlotWidget *kpw = new KPlotWidget( parent );
// setting our limits for the plot
kpw->setLimits( 1.0, 5.0, 1.0, 25.0 );

// creating a plot object whose points are connected by red lines ...
KPlotObject *kpo = new KPlotObject( Qt::red, KPlotObject::Lines );
// ... adding some points to it ...
for ( float x = 1.0; x <= 5.0; x += 0.1 )
    kpo->addPoint( x, x*x );

// ... and adding the object to the plot widget
kpw->addPlotObject( kpo );
 * @endcode
 *
 *@note KPlotWidget will take care of the objects added to it, so when
 *clearing the objects list (eg with removeAllPlotObjects()) any previous 
 *reference to a KPlotObject already added to a KPlotWidget will be invalid.
 *
 *@author Jason Harris
 *@version 1.1
 */
class KDEUI_EXPORT KPlotWidget : public QFrame {
    Q_OBJECT
    Q_PROPERTY(int leftPadding READ leftPadding)
    Q_PROPERTY(int rightPadding READ rightPadding)
    Q_PROPERTY(int topPadding READ topPadding)
    Q_PROPERTY(int bottomPadding READ bottomPadding)
    Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
    Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor)
    Q_PROPERTY(QColor gridColor READ gridColor WRITE setGridColor)
    Q_PROPERTY(bool grid READ isGridShown WRITE setShowGrid)
    Q_PROPERTY(bool objectToolTip READ isObjectToolTipShown WRITE setObjectToolTipShown)
public:
    /**
     *@short Constructor.
     *@param parent the parent widget
     */
    explicit KPlotWidget( QWidget * parent = 0 );

    /**
     *@short Destructor.
     */
    virtual ~KPlotWidget();

    /**
     * The four types of plot axes.
     */
    enum Axis
    {
        LeftAxis = 0,  ///< the left axis
        BottomAxis,    ///< the bottom axis
        RightAxis,     ///< the right axis
        TopAxis        ///< the top axis
    };

    /**
     *@return suggested minimum size for the plot widget
     */
    virtual QSize minimumSizeHint() const;

    /**
     *@return suggested size for the plot widget
     */
    virtual QSize sizeHint() const;

    /**
     * Set new data limits for the plot.
     * @param x1 the minimum X value in data units
     * @param x2 the maximum X value in data units
     * @param y1 the minimum Y value in data units
     * @param y2 the maximum Y value in data units
     */
    void setLimits( double x1, double x2, double y1, double y2 );

    /**
     * @short Reset the secondary data limits, which control the 
     * values displayed along the top and right axes.
     * 
     * All data points are *plotted* using the coordinates 
     * defined by setLimits(), so this function is only useful for 
     * showing alternate tickmark labels along the top and right 
     * edges.  For example, if you were plotting temperature on the 
     * X-axis, you could use Centigrade units for the primary 
     * (bottom) axis, using setLimits( 0.0, 100.0, 0.0, 1.0 ).  If
     * you also wanted to show Farenheit units along the secondary 
     * (top) axis, you would additionally use 
     * setSecondaryLimits( 32.0, 212.0, 0.0, 1.0 ).  The data 
     * added to the plot would have x-coordinates in Centigrade degrees.
     *
     * @param x1 the minimum X value in secondary data units
     * @param x2 the maximum X value in secondary data units
     * @param y1 the minimum Y value in secondary data units
     * @param y2 the maximum Y value in secondary data units
     * @sa setLimits()
     */
    void setSecondaryLimits( double x1, double x2, double y1, double y2 );

    /**
     * Unset the secondary limits, so the top and right axes 
     * show the same tickmarks as the bottom and left axes (no tickmark
     * labels will be drawn for the top and right axes in this case)
     */
    void clearSecondaryLimits();

    /**
     * @return the rectangle representing the boundaries of the current plot, 
     * in natural data units.
     * @sa setLimits()
     */
    QRectF dataRect() const;

    /**
     * @return the rectangle representing the boundaries of the secondary 
     * data limits, if they have been set.  Otherwise, this function 
     * behaves the same as dataRect().
     * @sa setSecondaryLimits()
     */
    QRectF secondaryDataRect() const;

    /**
     * @return the rectangle representing the boundaries of the current plot, 
     * in screen pixel units.
      */
    QRect pixRect() const;

    /**
     * Add an item to the list of KPlotObjects to be plotted.
     * @note do not use this multiple time if many objects have to be added,
     * addPlotObjects() is strongly suggested in this case
     * @param object the KPlotObject to be added
     */
    void addPlotObject( KPlotObject *object );

    /**
     * Add more than one KPlotObject at one time.
     * @param objects the list of KPlotObjects to be added
     */
    void addPlotObjects( const QList< KPlotObject* >& objects );

    /**
     * @return the current list of plot objects
     */
    QList< KPlotObject* > plotObjects() const;

    /**
     * Remove and delete all items from the list of KPlotObjects
     */
    void removeAllPlotObjects();

    /**
     * Reset the mask used for non-overlapping labels so that all 
     * regions of the plot area are considered empty.
     */
    void resetPlotMask();

    /**
     * Clear the object list, reset the data limits, and remove axis labels
     */
    void resetPlot();

    /**
     * Replace an item in the KPlotObject list.
     * @param i the index of the item to be replaced
     * @param o pointer to the replacement KPlotObject
     */
    void replacePlotObject( int i, KPlotObject *o );

    /**
     * @return the background color of the plot.
     *
     * The default color is black.
     */
    QColor backgroundColor() const;

    /**
     * @return the foreground color, used for axes, tickmarks and associated
     * labels.
     *
     * The default color is white.
     */
    QColor foregroundColor() const;

    /**
     * @return the grid color.
     *
     * The default color is gray.
     */
    QColor gridColor() const;

    /**
     * Set the background color
     * @param bg the new background color
     */
    void setBackgroundColor( const QColor &bg );

    /**
     * Set the foreground color
     * @param fg the new foreground color
     */
    void setForegroundColor( const QColor &fg );

    /**
     * Set the grid color
     * @param gc the new grid color
     */
    void setGridColor( const QColor &gc );

    /**
     * @return whether the grid lines are shown
     * Grid lines are not shown by default.
     */
    bool isGridShown() const;

    /**
     * @return whether the tooltip for the point objects is shown.
     * Tooltips are enabled by default.
     */
    bool isObjectToolTipShown() const;

    /**
     * @return whether the antialiasing is active
     * Antialiasing is not active by default.
     */
    bool antialiasing() const;

    /**
     * Toggle antialiased drawing.
     * @param b if true, the plot graphics will be antialiased.
     */
    void setAntialiasing( bool b );

    /**
     * @return the number of pixels to the left of the plot area.
     *
     * Padding values are set to -1 by default; if unchanged, this 
     * function will try to guess a good value, based on whether 
     * ticklabels and/or axis labels need to be drawn.
     */
    int leftPadding() const;

    /**
     * @return the number of pixels to the right of the plot area.
     * Padding values are set to -1 by default; if unchanged, this 
     * function will try to guess a good value, based on whether 
     * ticklabels and/or axis labels are to be drawn.
     */
    int rightPadding() const;

    /**
     * @return the number of pixels above the plot area.
     * Padding values are set to -1 by default; if unchanged, this 
     * function will try to guess a good value, based on whether 
     * ticklabels and/or axis labels are to be drawn.
     */
    int topPadding() const;

    /**
     * @return the number of pixels below the plot area.
     * Padding values are set to -1 by default; if unchanged, this 
     * function will try to guess a good value, based on whether 
     * ticklabels and/or axis labels are to be drawn.
     */
    int bottomPadding() const;

    /**
     * @short Set the number of pixels to the left of the plot area.
     * Set this to -1 to revert to automatic determination of padding values.
     */
    void setLeftPadding( int padding );

    /**
     * @short Set the number of pixels to the right of the plot area.
     * Set this to -1 to revert to automatic determination of padding values.
     */
    void setRightPadding( int padding );

    /**
     * @short Set the number of pixels above the plot area.
     * Set this to -1 to revert to automatic determination of padding values.
     */
    void setTopPadding( int padding );

    /**
     * @short Set the number of pixels below the plot area.
     * Set this to -1 to revert to automatic determination of padding values.
     */
    void setBottomPadding( int padding );

    /**
     * @short Revert all four padding values to -1, so that they will be 
     * automatically determined.
     */
    void setDefaultPaddings();

    /**
     * @short Map a coordinate @param p from the data rect to the physical 
     * pixel rect.
     * Used mainly when drawing.
     * @param p the point to be converted, in natural data units
     * @return the coordinate in the pixel coordinate system
     */
    QPointF mapToWidget( const QPointF& p ) const;

    /**
     * Indicate that object labels should try to avoid the given 
     * rectangle in the plot.  The rectangle is in pixel coordinates.
     *
     * @note You should not normally call this function directly.
     * It is called by KPlotObject when points, bars and labels are drawn.
     * @param r the rectangle defining the region in the plot that 
     * text labels should avoid (in pixel coordinates)
     * @param value Allows you to determine how strongly the rectangle 
     * should be avoided.  Larger values are avoided more strongly.
     */
    void maskRect( const QRectF &r, float value=1.0 );

    /**
     * Indicate that object labels should try to avoid the line 
     * joining the two given points (in pixel coordinates).
     *
     * @note You should not normally call this function directly.
     * It is called by KPlotObject when lines are drawn in the plot.
     * @param p1 the starting point for the line
     * @param p2 the ending point for the line
     * @param value Allows you to determine how strongly the line
     * should be avoided.  Larger values are avoided more strongly.
     */
    void maskAlongLine( const QPointF &p1, const QPointF &p2, float value=1.0 );

    /**
     * Place an object label optimally in the plot.  This function will
     * attempt to place the label as close as it can to the point to which 
     * the label belongs, while avoiding overlap with regions of the plot 
     * that have been masked. 
     *
     * @note You should not normally call this function directly.
     * It is called internally in KPlotObject::draw().
     *
     * @param painter Pointer to the painter on which to draw the label
     * @param pp pointer to the KPlotPoint whose label is to be drawn.
     */
    void placeLabel( QPainter *painter, KPlotPoint *pp );

    /**
     * @return the axis of the specified @p type, or 0 if no axis has been set.
     * @sa Axis
     */
    KPlotAxis* axis( Axis type );

    /**
     * @return the axis of the specified @p type, or 0 if no axis has been set.
     * @sa Axis
     */
    const KPlotAxis* axis( Axis type ) const;


public Q_SLOTS:
    /**
     * Toggle whether grid lines are drawn at major tickmarks.
     * @param show if true, grid lines will be drawn.
     * @sa isGridShown()
     */
    void setShowGrid( bool show );

    /**
     * Toggle the display of a tooltip for point objects.
     * @param show whether show the tooltip.
     * @sa isObjectToolTipShown()
     */
    void setObjectToolTipShown( bool show );

protected:
    /**
     * Generic event handler.
     */
    virtual bool event( QEvent* );

    /**
     * The paint event handler, executed when update() or repaint() is called.
     */
    virtual void paintEvent( QPaintEvent* );

    /**
     * The resize event handler, called when the widget is resized.
     */
    virtual void resizeEvent( QResizeEvent* );

    /**
     * Draws the plot axes and axis labels.
     * @internal Internal use only; one should simply call update()
     * to draw the widget with axes and all objects.
     * @param p pointer to the painter on which we are drawing
     */
    virtual void drawAxes( QPainter *p );

    /**
     * Synchronize the PixRect with the current widget size and 
     * padding settings.
     */
    void setPixRect();

    /**
     * @return a list of points in the plot which are within 4 pixels
     * of the screen position given as an argument.
     * @param p The screen position from which to check for plot points.
     */
    QList<KPlotPoint*> pointsUnderPoint( const QPoint& p ) const;

private:
    class Private;
    Private * const d;

    Q_DISABLE_COPY( KPlotWidget )
};

#endif
