/*
 * 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/>.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 *  Copyright 2006 Maurizio Monge <maurizio.monge@gmail.com>
 *
 *  BSD License
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Attention: note that even being named "BSD License" by the
 * copyright holder, the license above is a variation of the license
 * known as BSD License.  This variation is compatible with GNU GPL.
 */

#include <QDebug>
#include <QRegion>
#include <QPainter>
#include <QPaintEvent>

#include "qzionrectangle.h"
#include "qzionobject_p.h"

class QZionRectanglePrivate
{
public:
    QZionRectangle *owner;
    QSet<QZionObject*> clipees;
    QRect effectiveRect;
    QColor effectiveColor;
    bool rectDirty;
    bool colorDirty;

    QSize size;
    QColor color;

    QZionRectanglePrivate(const QSize &s, const QColor &c);

    void updateEffectiveRect();
    void updateEffectiveColor();
    void updateChildren();

    friend class QZionRectangle;
};


QZionRectanglePrivate::QZionRectanglePrivate(const QSize &s,
                                             const QColor &c)
    : size(s), color(c)
{
}

QZionRectangle::QZionRectangle(QZionAbstractCanvas *canvas)
    : QZionObject(canvas), _clipReferences(0)
{
    _QZionRectangle_data = new QZionRectanglePrivate(QSize(50, 50), Qt::white);
    _QZionRectangle_data->owner = this;
}

QZionRectangle::QZionRectangle(QZionAbstractCanvas *canvas,
                               const QColor &color, const QSize &size)
    : QZionObject(canvas), _clipReferences(0)
{
    _QZionRectangle_data = new QZionRectanglePrivate(size, color);
    _QZionRectangle_data->owner = this;
}

QZionRectangle::~QZionRectangle()
{
    QZD(QZionRectangle);

    // Update clipees
    foreach (QZionObject *clipee, d->clipees)
        clipee->setClipper(NULL);

    delete _QZionRectangle_data;
    _QZionRectangle_data = 0;
}

QColor QZionRectangle::color() const
{
    QZD(QZionRectangle);
    return d->color;
}

void QZionRectangle::setColor(const QColor &color)
{
    QZD(QZionRectangle);

    _QZionObject_data->opacity = color.alpha();
    d->color.setRgb(color.rgb());

    if (visible() && canvas())
        changed();
}

QSize QZionRectangle::size() const
{
    QZD(QZionRectangle);
    return d->size;
}

void QZionRectangle::setSize(const QSize &size)
{
    QZD(QZionRectangle);
    QZionObject::setSize(size);
    d->size = size;
    if (visible() && canvas())
        changed();
}

void QZionRectangle::paint(QPainter *p)
{
    QZD(QZionRectangle);
    // only paint if it's not a clipper
    if (_clipReferences == 0)
        p->fillRect(rect(), d->color);
}

QRect QZionRectangle::rect() const
{
    QZD(QZionRectangle);
    return QRect(pos(), d->size);
}

void QZionRectangle::addClippee(QZionObject* clippee)
{
    QZD(QZionRectangle);

    /* Rect became a clipper itself */
    if (d->clipees.isEmpty())
        changed();

    d->clipees.insert(clippee);
}

void QZionRectangle::removeClippee(QZionObject* clippee)
{
    QZD(QZionRectangle);

    d->clipees.remove(clippee);

    /* Rect used to be a clipper */
    if (d->clipees.isEmpty())
        changed();
}

QColor QZionRectangle::effectiveColor() const
{
    QZD(QZionRectangle);

    if (d->colorDirty)
        return QZionObject::effectiveColor();
    else
        return d->effectiveColor;
}

QRect QZionRectangle::effectiveRect() const
{
    QZD(QZionRectangle);

    if (d->rectDirty)
        return QZionObject::effectiveRect();
    else
        return d->effectiveRect;
}

void QZionRectangle::changed()
{
    QZD(QZionRectangle);

    d->updateEffectiveRect();
    d->updateEffectiveColor();
    if (_clipReferences)
        d->updateChildren();

    QZionObject::changed();
}

inline void QZionRectanglePrivate::updateEffectiveRect()
{
    rectDirty = true;
    effectiveRect = owner->effectiveRect();
    rectDirty = false;
}

inline void QZionRectanglePrivate::updateEffectiveColor()
{
    colorDirty = true;
    effectiveColor = owner->effectiveColor();
    colorDirty = false;
}

inline void QZionRectanglePrivate::updateChildren()
{
    foreach(QZionObject* clippee, clipees) {
        clippee->setClipper(owner);
        clippee->changed();
    }
}


