/*
 * Copyright (C) 2008 Instituto Nokia de Tecnologia. All rights reserved.
 *
 * This file is part of QZion.
 *
 * QZion 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 3 of the License, or
 * (at your option) any later version.
 *
 * QZion 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 QZion.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __QZIONOBJECT_H__
#define __QZIONOBJECT_H__

#include <QResizeEvent>

#include "qzionmacros.h"
#include "qzionabstractcanvas.h"


class QZionGroup;
class QZionCanvas;
class QZionRectangle;
class QZionObjectPrivate;
class QZionAbstractCanvasPrivate;


/*!
    \brief The QZionObject class is the base class for all graphical
    objects in a QZionCanvas.

    It provides a light-weight foundation for writing your own custom objects.
    This includes defining the object's geometry, its painting implementation
    and object interaction through its event handlers.

    Standard graphical objects are already provided. These are:

    QZionText provides a text object
    QZionRectangle provides a rectangle object
    QZionImage provides an image object
    QZionGroup provides a group of objects
    QZionClippedGroup provides a group of objects with a clipper

    \sa QZionCanvas
*/
class QZionObject : public QObject
{
    Q_OBJECT

public:

    /* The numbers represent the bit to be used by the flag */
    enum Attributes {
        EmitSizeChanges = 0,
        HandleOutOfBoundsEvents = 1
    };

    /*!
      Constructs a QZionObject in the given \a canvas.

      If \a canvas is NULL you can add it to a canvas later using
      QZionAbstractCanvas::addObject().

      The object is always hidden after being created.
    */
    QZionObject(QZionAbstractCanvas *canvas = NULL);

    /*!
      Destroys the QZionObject. If the object is associated with a canvas,
      the object will be removed from the canvas before deleted.
    */
    virtual ~QZionObject();

    /*!
      Returns a pointer to the object's canvas if there is one, otherwise
      returns NULL.
    */
    QZionAbstractCanvas *canvas() const;

    inline void show();
    inline void hide();

    /*!
      Test if an attribute is set.
    */
    bool testAttribute(const Attributes attribute) const;
    /*!
      Sets the attribute \a attribute on this object if \a on is true;
      otherwise clears the attribute.
    */
    void setAttribute(const Attributes attribute, const bool on = true);

    /*!
      Returns the object position.

      \sa setPos()
    */
    QPoint pos() const;

    /*!
      Sets the object position.
    */
    virtual void setPos(const QPoint &newpos);

    /*!
      Sets the object position.
    */
    inline void setPos(const int x, const int y);

    /*!
      Returns the opacity of the object in the range [0, 255].

      \sa setOpacity()
    */
    int opacity() const;

    /*!
      Sets the object opacity. The \a value must be in the range [0, 255].
      Where \a value 0 means full transparent (invisible) and \a value 255
      means full opaque.

      By default object's opacity is equal to 255 (full opaque).

      Note that even if the object is full transparent and cannot be seen,
      it still receives mouse events if \a visible() is true. So if you
      want the object do not receive any mouse events you need to call
      setVisible() with false.

      \sa opacity(), setVisible()
    */
    void setOpacity(const int value);

    /*!
      Returns true if the widget is visible otherwise returns false.

      \sa setVisible()
    */
    bool visible() const;

    /*!
      Sets the object visibility to true if \a visible is true otherwise the
      object is made invisible. Invisible objects are not painted and do not
      receive any mouse events.

      By default, objects are invisible when created. You need to call
      setVisible() to show them.

      \sa visible(), show(), hide()
    */
    void setVisible(const bool value);

    /*!
      Returns true if the object receives mouse events, otherwise
      returns false.

      \sa setMouseEvents()
    */
    bool mouseEvents() const;

    /*!
      Sets mouse events flag for the object. When mouse events flag is
      false the object will not receive any mouse events, all these events
      will be passed to the objects behind it.

      By default mouse events flag is true. If you want an object do not
      receive mouse events you must call this method with \a value false.

      Note that even if mouse events is true, invisible objects do not
      receive mouse events.

      \sa mouseEvents()
    */
    void setMouseEvents(const bool value);

    /*!
      Returns the Z index of the object in canvas.

      \sa setZValue()
    */
    int zValue() const;

    /*!
      Sets the Z index of the object. You can bring to front or bring to
      back the object in the canvas just changing the \a zValue.

      \sa zValue(), stackOver(), stackUnder(), raise(), lower()
    */
    void setZValue(const int zValue);

    /*!
      Bring the object to the front of the canvas.

      \sa setZValue()
    */
    void raise();

    /*!
      Bring the object to the back of the canvas.

      \sa setZValue()
    */
    void lower();

    /*!
      Put the object in front of the reference object passed by \a ref.
      The reference object must be in the same canvas.
    */
    void stackAbove(QZionObject *ref);

    /*!
      Put the object behind the reference object passed by \a ref.
      The reference object must be in the same canvas.
    */
    void stackBelow(QZionObject *ref);

    /*!
      Schedule an object update. You can call this method when you need to
      repaint the object contents.

      \sa updateChanges()
    */
    virtual void changed();

    /*!
      Returns the QZionRectangle that this object is clipped to, if there
      is one, otherwise returns NULL.

      \sa setClipper()
    */
    QZionRectangle *clipper() const;

    /*!
      Sets the clipper of the object. The clipper is a QZionRectangle
      that confines the object contents to it's area and change object
      opacity if it's opacity is changed. To remove a clipper you need
      to call this method with NULL.

      \sa clipper()
    */
    virtual void setClipper(QZionRectangle *rect);

    /*!
      Returns the object size.

      \sa setSize()
    */
    virtual QSize size() const = 0;

    /*!
      Sets the object size.

      \sa size()
    */
    virtual void setSize(const QSize &size);
    inline virtual void setSize(const int width, const int height);

    /*!
      Returns the object color.

      \sa setColor()
    */
    virtual QColor color() const = 0;

    /*!
      Returns the scene color of the object already composed.
     */
    virtual QColor effectiveColor() const;

    /*!
      Returns the visible rect of the object already calculated.
     */
    virtual QRect effectiveRect() const;

    /*!
      Sets the object color.

      \sa color()
    */
    virtual void setColor(const QColor &color) = 0;
    inline virtual void setColor(const int r, const int g,
                                 const int b, const int a=255);

    /*!
      Override this function to draw the item with the painter.
    */
    virtual void paint(QPainter *p) = 0;

    /*!
      Override this function to return the rect the item will be drawn into.
    */
    virtual QRect rect() const = 0;

    /*!
      Returns true if items contains a given point.

      This function is used for dispatching mouseEvents, the default
      implementation respects the object boundaries, but you can override
      to change this behaviour.
    */
    virtual bool contains(const QPoint &point) const;

    inline virtual bool layered() const;

    inline virtual void mouseMoveEvent(QMouseEvent *e);
    inline virtual void mousePressEvent(QMouseEvent *e);
    inline virtual void mouseReleaseEvent(QMouseEvent *e);
    inline virtual void wheelEvent(QWheelEvent *e);

    virtual bool isClipper() const { return false; }

Q_SIGNALS:
    void sizeChanged(const QSize &size);
    void signalMouseMoveEvent(QZionObject *, QMouseEvent *);
    void signalMousePressEvent(QZionObject *, QMouseEvent *);
    void signalMouseReleaseEvent(QZionObject *, QMouseEvent *);
    void signalWheelEvent(QZionObject *, QWheelEvent *);

protected:
    QZionObjectPrivate *_QZionObject_data;

    bool _changed;

    virtual void paintInternal(QPainter *p, const QRect &prect,
                               const QRegion &preg, const QPoint &delta,
                               double cumulativeOpacity);

    /*!
      Check if the object has changed and invalidate it's region to repaint
      it's content on canvas. This method is called from parent.

      \sa changed()
    */
    virtual void updateChanges();
    inline virtual void canvasResizeEvent(QResizeEvent *);

private:
    Q_DISABLE_COPY(QZionObject)

    friend class QZionAbstractCanvas;
    friend class QZionAbstractCanvasPrivate;
    friend class QZionGroup;
    friend class QZionCanvas;
    friend class QZionCanvasPrivate;
    friend class QZionObjectPrivate;
};

/*!
    Shows the object. (Objects are hidden by default)

    This convenience function is equivalent to calling \c setVisible(true).

    \sa hide(), setVisible()
*/
inline void QZionObject::show()
{
    setVisible(true);
}

/*!
    Hides the object. (Objects are hidden by default)

    This convenience function is equivalent to calling \c setVisible(false).

    \sa show(), setVisible()
*/
inline void QZionObject::hide()
{
    setVisible(false);
}

/*!
    \overload

    It's equivalent to calling setPos(QPoint(x, y)).
*/
inline void QZionObject::setPos(const int x, const int y)
{
    setPos(QPoint(x, y));
}

/*!
    Override this function to specify if the painting operations will
    paint over each other. If not, the item will be drawn more quickly
    when opacity is != 255, because it does not have to be painted onto
    a pixmap first.
*/
inline bool QZionObject::layered() const
{
    return false;
}

/*!
    Called on mouseMoveEvent over this object.
*/
inline void QZionObject::mouseMoveEvent(QMouseEvent *e)
{
    emit signalMouseMoveEvent(this, e);
}

/*!
    Called on mousePressEvent over this object.
*/
inline void QZionObject::mousePressEvent(QMouseEvent *e)
{
    emit signalMousePressEvent(this, e);
}

/*!
    Called on mouseReleaseEvent over this object.
*/
inline void QZionObject::mouseReleaseEvent(QMouseEvent *e)
{
    emit signalMouseReleaseEvent(this, e);
}

/*!
    Called on wheelEvent over this object.
*/
inline void QZionObject::wheelEvent(QWheelEvent *e)
{
    emit signalWheelEvent(this, e);
}

/*!
    Called on canvasResizeEvent.
*/
inline void QZionObject::canvasResizeEvent(QResizeEvent *)
{
}

/*!
    \overload

    It's equivalent to calling setSize(QSize(width, height)).

    \sa size()
*/
inline void QZionObject::setSize(const int width, const int height)
{
    return setSize(QSize(width, height));
}

/*!
    \overload

    It's equivalent to calling setColor(QColor(r, g, b, a)).

    \sa color()
*/
inline void QZionObject::setColor(const int r, const int g,
                                  const int b, const int a)
{
    setColor(QColor(r, g, b, a));
}


#endif
