/*
 * wbscene.cpp - an SVG whiteboard scene class
 * Copyright (C) 2006  Joonas Govenius
 *
 * 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) any later version.
 *
 * 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */

#include "wbscene.h"

WbScene::WbScene(SxeSession* session, QObject * parent) : QGraphicsScene(parent) {
    session_ = session;
};

void WbScene::queueTransformationRegeneration(WbItem* item) {
    if(item && !pendingTranformations_.contains(item))
        pendingTranformations_.append(QPointer<WbItem>(item));
}

void WbScene::regenerateTransformations() {
    foreach(QPointer<WbItem> item, pendingTranformations_) {
        if(item) {
            // qDebug() << QString("Regenerating %1 transform.").arg((unsigned int) &(*item)).toLatin1();
            item->regenerateTransform();
        }
    }
    pendingTranformations_.clear();
    session_->flush();
}

QPointF WbScene::selectionCenter() const {
    QList<QGraphicsItem*> items = selectedItems();

    if(items.size() < 1) {
        return QPointF();
    }

    QRectF box = items.at(0)->sceneBoundingRect();
    foreach(QGraphicsItem* item, items) {
        box = box.united(item->sceneBoundingRect());
    }

    // qDebug() << QString("Selection center: %1 %2").arg(box.center().x()).arg(box.center().y()).toLatin1();

    return box.center();
}

void WbScene::bringForward(int n) {
    bring(n, false);
}

void WbScene::bringToFront() {
    bring(1, true);
}

void WbScene::sendBackwards(int n) {
    bring(-n, false);
}

void WbScene::sendToBack() {
    bring(-1, true);
}

static bool zValueLessThan(QGraphicsItem* item1, QGraphicsItem* item2) {
    return (item1->zValue() < item2->zValue());
}

void WbScene::group() {
    if(selectedItems().size() > 1) {
        // Create the group
        QDomElement temp = QDomDocument().createElement("g");
        temp.setAttribute("id", "e" + SxeSession::generateUUID());
        const QDomNode group = session_->insertNodeAfter(temp, session_->document().documentElement());

        // Arrange the items by Z coordinate
        QList<QGraphicsItem*> selected = selectedItems();
        qSort(selected.begin(), selected.end(), zValueLessThan);

        // Reparent each selected item
        foreach(QGraphicsItem* item, selected) {
            WbItem* wbitem = dynamic_cast<WbItem*>(item);
            if(wbitem)
                session_->insertNodeAfter(wbitem->node(), group);
        }
        clearSelection();

        session_->flush();
    }
}

void WbScene::ungroup() {
    foreach(QGraphicsItem* item, selectedItems()) {
        // find the QDomElement matching the selected item
        WbItem* wbitem = dynamic_cast<WbItem*>(item);
        if(wbitem) {
            QDomElement group = wbitem->node().toElement();

            if(group.nodeName() == "g") {
                QDomNodeList children = group.childNodes();

                QMatrix groupTransform = WbItem::parseSvgTransform(group.attribute("transform"));

                for(int i = children.size() - 1; i >= 0; i--) {
                    QDomElement child = children.at(i).toElement();
                    if(!child.isNull()) {

                        if(!groupTransform.isIdentity()) {
                            // combine the transformations of the group and the child
                            QMatrix childTransform = WbItem::parseSvgTransform(child.attribute("transform"));
                            session_->setAttribute(child, "transform", WbItem::toSvgTransform(childTransform * groupTransform));
                        }

                        // move the child from the group to the root <svg/>
                        session_->insertNodeAfter(children.at(i), session_->document().documentElement(), group);

                    }
                }

                // delete the group itself
                session_->removeNode(group);
            }

        }
    }
    clearSelection();

    session_->flush();
}

void WbScene::bring(int n, bool toExtremum) {
    if(n == 0)
         return;

    // bring each selected item
    foreach(QGraphicsItem* selecteditem, selectedItems()) {

        if (!(selecteditem->parentItem() && selecteditem->parentItem()->isSelected())) {

            WbItem* selectedwbitem = dynamic_cast<WbItem*>(selecteditem);

            if(selectedwbitem) {

                QList<QGraphicsItem*> colliding = selecteditem->collidingItems();

                // find the relative position of selecteditem itself and
                // remove other selected items from the list colliding
                int i = 0;
                while(i < colliding.size()) {
                    if(selectedItems().contains(colliding[i])) {
                        colliding.removeAt(i);
                    } else if(colliding[i]->zValue() < selecteditem->zValue()) {
                        break;
                    } else
                        i++;
                }
                if(n < 0)
                    i--;

                // bring the selected node n levels above/below the item itself
                if(colliding.size() > 0) {
                    WbItem* referencewbitem;
                    if(n > 0) {
                        if(i - n > 0 && !toExtremum)
                            referencewbitem = dynamic_cast<WbItem*>(colliding[i - n]);
                        else
                            referencewbitem = dynamic_cast<WbItem*>(colliding[0]);
                    } else {
                        if(i - n < colliding.size() && !toExtremum)
                            referencewbitem = dynamic_cast<WbItem*>(colliding[i - n]);
                        else
                            referencewbitem = dynamic_cast<WbItem*>(colliding[colliding.size() - 1]);
                    }

                    if(referencewbitem && !referencewbitem->node().isNull()) {
                        if(n > 0)
                            session_->insertNodeAfter(selectedwbitem->node(), selectedwbitem->node().parentNode(), referencewbitem->node());
                        else
                            session_->insertNodeBefore(selectedwbitem->node(), selectedwbitem->node().parentNode(), referencewbitem->node());
                    }
                }
            }
        }
    }

    session_->flush();
}
