/*
 * 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/>.
 */

#include <QDebug>
#include <QPainter>

#include "qziongroup.h"
#include "qzioncommon.h"
#include "qzionobject.h"
#include "qzionobject_p.h"
#include "qzionrectangle.h"
#include "qzionabstractcanvas.h"


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

    if (d->skipUpdate)
        d->skipUpdate = false;
    else
        d->item->update();
}

void QZionObject::updateChanges() {}

QZionObject::QZionObject(QZionAbstractCanvas*)
    : QObject()
{
    _QZionObject_data = new QZionObjectPrivate;

    QZD(QZionObject);
    d->owner = this;
    d->skipUpdate = false;
}

QZionObject::~QZionObject()
{
    QZD(QZionObject);

    // Clear the item to avoid removeObject() try to reparent it.
    // At this point the subclasses already deleted the inner item, since
    // their dtor runs before QZionObject's.
    d->item = 0;

    if (d->canvas)
        d->canvas->removeObject(this);

    setClipper(NULL);
    delete _QZionObject_data;
}

int QZionObject::opacity() const
{
    QZD(QZionObject);
    return d->opacity;
}


void QZionObject::setOpacity(const int value)
{
    QZD(QZionObject);
    if (value == d->opacity)
        return;

    d->opacity = (value < 0) ? 0 : (0 > 255) ? 255 : value;
    changed();
}

bool QZionObject::visible() const
{
    QZD(QZionObject);
    return d->item->isVisible();
}

void QZionObject::setVisible(const bool value)
{
    QZD(QZionObject);

    d->skipUpdate = true;
    changed();

    d->item->setVisible(value);
}

QPoint QZionObject::pos() const
{
    QZD(QZionObject);
    return d->item->pos().toPoint();
}

void QZionObject::setPos(const QPoint &newpos)
{
    QZD(QZionObject);

    d->skipUpdate = true;
    changed();

    d->item->setPos(newpos);
}

bool QZionObject::mouseEvents() const
{
    QZD(QZionObject);
    return (d->item->acceptedMouseButtons() != Qt::NoButton);
}

void QZionObject::setMouseEvents(const bool value)
{
    QZD(QZionObject);

    d->skipUpdate = true;
    changed();

    if (!value)
        d->item->setAcceptedMouseButtons(Qt::NoButton);
    else
        d->item->setAcceptedMouseButtons(Qt::LeftButton
                                         | Qt::RightButton
                                         | Qt::MidButton);
}

int QZionObject::zValue() const
{
    QZD(QZionObject);
    return d->item ? int(d->item->zValue()) : -1;
}

void QZionObject::setZValue(const int zValue)
{
    QZD(QZionObject);

    d->skipUpdate = true;
    changed();

    if (d->canvas)
        d->canvas->setZValue(this, zValue);
}

void QZionObject::raise()
{
    QZD(QZionObject);

    d->skipUpdate = true;
    changed();

    if (d->canvas)
        d->canvas->raise(this);
}


void QZionObject::lower()
{
    QZD(QZionObject);

    d->skipUpdate = true;
    changed();

    if (d->canvas)
        d->canvas->lower(this);
}

QZionRectangle *QZionObject::clipper() const
{
    QZD(QZionObject);
    return d->clipper;
}

void QZionObject::setClipper(QZionRectangle *rect)
{
    QZD(QZionObject);

    // There's no need to change clipper.
    if (d->clipper == rect)
        return;

    // Had an old clipper? Remove it.
    if (d->clipper) {
        d->clipper->removeClippee(this);
        d->clipper->decClipperRef();
        d->clipper = NULL;
    }

    // Has a new one? Add it.
    if (rect) {
        d->clipper = rect;
        d->clipper->addClippee(this);
        d->clipper->incClipperRef();
    }

    // Reflect changes
    if (d->item)
        changed();
}

QZionAbstractCanvas *QZionObject::canvas() const
{
    QZD(QZionObject);
    return d->canvas;
}

void QZionObject::stackAbove(QZionObject *ref)
{
    QZD(QZionObject);

    d->skipUpdate = true;
    changed();

    if (d->canvas)
        d->canvas->stackAbove(this, ref);
}

void QZionObject::stackBelow(QZionObject *ref)
{
    QZD(QZionObject);

    d->skipUpdate = true;
    changed();

    if (d->canvas)
        d->canvas->stackBelow(this, ref);
}

void QZionObject::setSize(const QSize &size)
{
    changed();

    if (testAttribute(EmitSizeChanges))
        emit sizeChanged(size);
}

bool QZionObject::testAttribute(const Attributes attribute) const
{
    QZD(QZionObject);
    return (bool)(d->attributes & (1 << attribute));
}

void QZionObject::setAttribute(const Attributes attribute, const bool on)
{
    QZD(QZionObject);
    if (on)
        d->attributes |= (1 << attribute);
    else
        d->attributes &= ~(1 << attribute);

    changed();
}

void QZionObject::paintInternal(QPainter*, const QRect&,
                                const QRegion&,
                                const QPoint&,
                                double)
{
}

QColor QZionObject::effectiveColor() const
{
    QColor result;

    result = color();
    result.setAlpha(opacity());

    QZionRectangle *clip = clipper();
    if (clip)
        result = composeColorClass(clip->effectiveColor(), result);
    else {
        QZionGroup *group = dynamic_cast<QZionGroup *>(canvas());
        if (group)
            result = composeColorClass(group->effectiveColor(), result);
    }

    return result;
}

QRect QZionObject::effectiveRect() const
{
    QRect result = rect();

    QZionRectangle *clip = clipper();
    if (clip)
        result &= clip->effectiveRect();

    return result;
}

bool QZionObject::contains(const QPoint &point) const
{
    QZD(QZionObject);
    QPointF mappedPt = d->item->mapFromParent(point);
    return d->item->contains(mappedPt);
}
