/***************************************************************************
 *   Copyright 2007-2008 Wouter Haffmans <cheetah@simply-life.net>         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU General Public License as        *
 *   published by the Free Software Foundation; either version 2 of        *
 *   the License or (at your option) version 3 or any later version        *
 *   accepted by the membership of KDE e.V. (or its successor approved     *
 *   by the membership of KDE e.V.), which shall act as a proxy            *
 *   defined in Section 14 of version 3 of the license.                    *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/

#ifndef CANVAS_H
#define CANVAS_H

#include <QWidget>
#include <QPen>
#include <QLinkedList>

#include "canvasscene.h"

class QMouseEvent;
class QGraphicsView;
class QGraphicsLineItem;
class QGraphicsPathItem;
class GraphicsLinesItem;

/**
 * Widget that can be painted on. The pen can be changed and set up and down.
 * If the pen is down, it will capture the mouse cursor position and draw on
 * that position.
 */
class Canvas : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(QBrush backgroundBrush
            READ backgroundBrush
            WRITE setBackgroundBrush
            STORED false)
    Q_PROPERTY(QImage backgroundImage
            READ backgroundImage
            WRITE setBackgroundImage
            STORED false)
    Q_PROPERTY(CanvasScene::BackgroundStyle backgroundStyle
            READ backgroundStyle
            WRITE setBackgroundStyle
            STORED false)
    Q_PROPERTY(QPen pen READ pen WRITE setPen)
    Q_PROPERTY(bool penDown READ isPenDown WRITE setPenDown)
    Q_PROPERTY(qint16 tag READ tag WRITE setTag)

public:
    /**
     * Canvas Constructor. The canvas will get a white background color, and the
     * pen will be initialized to a 1 pixel wide, solid black line.
     * @param parent The parent widget.
     */
    Canvas(QWidget *parent = 0);

    /**
     * Destructor.
     */
    ~Canvas();

    /**
     * Retrieve the current background brush.
     * @return The current background brush.
     */
    QBrush  backgroundBrush() const;

    /**
     * Retrieve the current background image.
     * @return The current background image, 0 if none is set.
     */
    QImage  backgroundImage() const;

    /**
     * Retrieve the current background style.
     * @return The current background style.
     */
    CanvasScene::BackgroundStyle backgroundStyle() const;

    /**
     * Retrieve the line items drawn on the canvas so far.
     * @return  the line items drawn on the canvas so far.
     */
    QList<GraphicsLinesItem *> lines() const;

    /**
     * Retrieve the current pen.
     * @return The current pen.
     */
    QPen    pen() const;

    /**
     * Check if the pen is down or not.
     * @return True if the pen is down, false if not.
     */
    bool    isPenDown() const;

    /**
     * Retrieve a QImage copy of the drawn image.
     * @return The drawn image as QImage.
     */
    QImage  image() const;

    /**
     * Retrieve the tag number currently used for adding lines.
     * @return The tag number currently used for adding lines.
     */
    qint16  tag() const;

    /**
     * Save the image to an SVG file with the given name.
     * @param fileName The filename to save the image on the canvas to.
     */
    void    saveToFile(const QString fileName) const;

    /**
     * Retrieve the default size hint.
     */
    QSize   sizeHint() const;

    /**
     * Retrieve the default minimum size hint.
     */
    QSize   minimumSizeHint() const;

public slots:
    /**
     * Set the background brush of the canvas.
     * @param backgroundBrush The new background brush.
     */
    void    setBackgroundBrush(QBrush brush);

    /**
     * Set the background image of the canvas.
     * @param backgroundImage The new background image.
     */
    void    setBackgroundImage(QImage Image);

    /**
     * Set the style of the background (i.e. its position and repeating).
     * @param style The new background style.
     */
    void    setBackgroundStyle(CanvasScene::BackgroundStyle style);

    /**
     * Set the lines to show on the canvas. This will clear the existing image
     * and overwrite it with the given lines.
     * @param lines A list of lines to place on the canvas.
     */
    void setLines(QList<GraphicsLinesItem *> lines);

    /**
     * Clear the canvas entirely with the given background color. Sets the
     * background brush as well. Equivalent to calling setBackgroundBrush() and
     * clear().
     * @param backgroundBrush The background brush to clear the canvas with.
     */
    void    clear(QBrush backgroundBrush);

    /**
     * Clear the canvas entirely with the set background color. This will not
     * unset remove the background image.
     */
    void    clear();

    /**
     * Set the pen up (drawing is disabled). Equivalent to calling
     * setPenDown(false).
     */
    void    penUp();

    /**
     * Set the pen down (start drawing). Equivalent to calling
     * setPenDown(true).
     */
    void    penDown();

    /**
     * Set the pen up or down.
     * @param penDown Boolean indicating whether the pen should be set up or
     *   down.
     */
    void    setPenDown(bool penDown);

    /**
     * Set the pen to use. If the pen's color does not appear in the color
     *  table, the color will remain unchanged.
     * @param pen The pen to use.
     */
    void    setPen(QPen pen);

    /**
     * Set the current tag to use for newly added lines. Previous lines are not
     * adjusted.
     * @param tag The tag to use for newly added lines.
     */
    void    setTag(qint16 tag);

    /**
     * Move the pencil to a new position. If the pen is down, a line will be
     * drawn from the old to the new position.
     */
    void    moveTo(QPoint pos);

    /**
     * Adjust the existing lines to change the colors according to the new tag/
     * color mapping. If a tag is not found in the mapping, the color of
     * lines with that tag remain unchanged.
     * @param colorMap A mapping of tags to colors to use.
     */
    void    adjustColors(QMap<qint16, QColor> colorMap);

protected:
    /**
     * Reimplementation from QWidget: receive mouse events, and if pen is down,
     * do some painting.
     */
    void    mouseMoveEvent(QMouseEvent *event);

    /**
     * Reimplementation of QWidget's paintEvent
     * @param event The paint event (unused).
     */
    void    paintEvent(QPaintEvent *event);

    /**
     * Reimplementation of QWidget's resizeEvent.
     * @param event The resize event.
     */
    void    resizeEvent(QResizeEvent *event);

private:
    /**
     * Draw a line from the last point to the given end point. Set the last
     * point to be the given end point.
     * @param endPoint The end point of the line to draw.
     */
    void    drawLine(const QPointF &endPoint);

    /**
     * Stores the lines drawn so far to a new GraphicsLinesItem on the scene and
     * in the local cache. If no points are in the m_currentPoints list, then
     * nothing will happen.
     */
    void    storeLines();

    /**
     * Stores the given line to the scene amd local cache.
     * @param lines A GraphicsLinesItem with the line set to store.
     */
    void    storeLines(GraphicsLinesItem *lines);

    /**
     * Updates the cached image, which is painted on the widget. This renders
     * the scene and places it in the cache.
     */
    void    updateCache();

    /**
     * Resize the image to a new size. This will update the scene to new sizes.
     * The image will not be scaled. If the new size is larger, it will be
     * centered.
     * @param newSize The new size the resize the image to.
     */
    void    resizeImage(const QSize &newSize);

    // Qt Graphics View Framework members
    CanvasScene         *m_scene;

    QPixmap             m_cacheImage;

    // Member properties
    QColor  m_backgroundColor;
    QImage  m_backgroundImage;
    QPen    m_pen;
    bool    m_isPenDown;
    qint16  m_tag;
    qreal   m_currentZValue;
    QLinkedList<QPointF> m_currentPoints;
    QList<GraphicsLinesItem *> m_lines;

    QPointF  m_lastPoint;
};

#endif
