/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbCore module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/



#include <hbanchor_p.h>
#include <hbanchorarrowdrawer_p.h>
#include <hbcolorscheme.h>
#include <hbevent.h>
#include <hblayoututils_p.h>
#include <hbmeshlayout_p.h>
#include <hbmeshlayoutdebug_p.h>
#include <QBrush>
#include <QPainter>
#include <QPen>

const int ARROW_HEAD_SIZE = 2;
const int LINE_WIDTH = 1;
const QString BOX_COLOR = "qtc_view_separator_normal";
const QString VALID_COLOR = "qtc_default_main_pane_normal";
const QString INVALID_COLOR = "qtc_view_visited_normal";

HbAnchorArrowDrawer::HbAnchorArrowDrawer(HbMeshLayout* mesh, QGraphicsItem *parent)
    : HbWidgetBase(parent), mLayout(mesh), mDrawOutlines(true), mDrawArrows(true), mDrawSpacers(true)
{
#if defined(HB_DEVELOPER) || defined(CSS_INSPECTOR)
    updateColors();
#endif
}

HbAnchorArrowDrawer::~HbAnchorArrowDrawer()
{
}

void HbAnchorArrowDrawer::changeEvent(QEvent *event)
{
#if defined(HB_DEVELOPER) || defined(CSS_INSPECTOR)
    if (event->type() == HbEvent::ThemeChanged)
        updateColors();
#endif
    HbWidgetBase::changeEvent(event);
}

void HbAnchorArrowDrawer::updateColors()
{
#if defined(HB_DEVELOPER) || defined(CSS_INSPECTOR)
    mValidColor = HbColorScheme::color(VALID_COLOR);
    mInvalidColor = HbColorScheme::color(INVALID_COLOR);
    mBoxColor = HbColorScheme::color(BOX_COLOR);
#endif
}

void HbAnchorArrowDrawer::updateFocusItem(const QGraphicsItem *item)
{
#if defined(HB_DEVELOPER) || defined(CSS_INSPECTOR)
    mLayout = 0;
    if (item && item->isWidget()) {
        const QGraphicsWidget *widget = static_cast<const QGraphicsWidget*>(item);
        setGeometry(item->sceneBoundingRect());
        QGraphicsLayout *layout = widget->layout();
        if (layout) {
            HbMeshLayout *mesh = dynamic_cast<HbMeshLayout *>(layout);
            if (mesh) {
                mLayout = mesh;
            }
        }
    }
#else
    Q_UNUSED(item);
#endif
}

void HbAnchorArrowDrawer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

#if defined(HB_DEVELOPER) || defined(CSS_INSPECTOR)
    if(!mLayout || (!mDrawOutlines && !mDrawArrows)) {
        return;
    }
    painter->save();

    QList<HbAnchor*> anchors = HbMeshLayoutDebug::getAnchors(mLayout);

    // Draw boxes round anchored child items
    if (mDrawOutlines) {
        QList<QGraphicsLayoutItem*> anchoredChildren;
        foreach (const HbAnchor *anchor, anchors) {
            if (!anchoredChildren.contains(anchor->mStartItem)) {
                anchoredChildren.append(anchor->mStartItem);
            }
            if (!anchoredChildren.contains(anchor->mEndItem)) {
                anchoredChildren.append(anchor->mEndItem);
            }
        }
        painter->setPen(QPen(QBrush(mBoxColor), LINE_WIDTH));
        painter->setBrush(Qt::NoBrush);
        foreach (const QGraphicsLayoutItem *item, anchoredChildren) {
            painter->drawRect(item->geometry());
        }
    }

    if (mDrawSpacers) {
        painter->save();
        for (int li=0 ; li<mLayout->count(); li++) {
            QGraphicsLayoutItem *layoutItem = mLayout->itemAt(li);
            //if (!layoutItem->graphicsItem()) {
            QRectF rectArea = layoutItem->geometry();
            if (rectArea.width() == 0 || rectArea.height() == 0) {
                if (rectArea.height() == 0 && rectArea.width() > 0) {
                    rectArea.setHeight(contentsRect().height());
                    bool overridden = false;
                    // find all the horizontal anchors
                    foreach(HbAnchor *anchor, anchors) {
                        if (anchor->mStartEdge == Hb::LeftEdge || anchor->mStartEdge == Hb::RightEdge ||anchor->mStartEdge == Hb::CenterHEdge) {
                            QGraphicsLayoutItem *other = 0;
                            if (anchor->mStartItem == layoutItem && anchor->mEndItem != mLayout) {
                                other = anchor->mEndItem;
                            } else if (anchor->mEndItem == layoutItem && anchor->mStartItem != mLayout) {
                                other = anchor->mStartItem;
                            }
                            if (other) {
                                if (overridden) {
                                    rectArea.setTop(qMin(rectArea.top(), other->geometry().top()));
                                    rectArea.setBottom(qMax(rectArea.bottom(), other->geometry().bottom()));
                                    break;
                                } else {
                                    rectArea.setTop(other->geometry().top());
                                    rectArea.setHeight(other->geometry().height());
                                    overridden = true;
                                }
                            }
                        }
                    }

                }

                if (rectArea.width() == 0 && rectArea.height() > 0) {
                    rectArea.setWidth(contentsRect().width());
                    bool overridden = false;
                    // find all the vertical anchors
                    foreach(HbAnchor *anchor, anchors) {
                        if (anchor->mStartEdge == Hb::TopEdge || anchor->mStartEdge == Hb::BottomEdge ||anchor->mStartEdge == Hb::CenterVEdge) {
                            QGraphicsLayoutItem *other = 0;
                            if (anchor->mStartItem == layoutItem && anchor->mEndItem != mLayout) {
                                other = anchor->mEndItem;
                            } else if (anchor->mEndItem == layoutItem && anchor->mStartItem != mLayout) {
                                other = anchor->mStartItem;
                            }
                            if (other) {
                                if (overridden) {
                                    rectArea.setLeft(qMin(rectArea.left(), other->geometry().left()));
                                    rectArea.setRight(qMax(rectArea.right(), other->geometry().right()));
                                    break;
                                } else {
                                    rectArea.setLeft(other->geometry().left());
                                    rectArea.setWidth(other->geometry().width());
                                    overridden = true;
                                }
                            }
                        }
                    }
                }

                painter->setOpacity(0.2); // so that we can see overlapping spacers
                painter->fillRect(rectArea, QBrush(mInvalidColor, Qt::SolidPattern));
            }
        }
        painter->restore();
    } // End spacers


    // Draw anchor lines
    if (mDrawArrows) {
        Qt::LayoutDirection dir = HbLayoutUtils::visualDirection(mLayout);
        for (int i=0; i<anchors.count(); i++) {
            Hb::Edge arrowType = Hb::RightEdge;
            QPointF start, start2, end, end2;
            HbAnchor *anchor = anchors.at(i);

            // Ignore some primitives
            if (anchor->mStartItem) {
                if (QGraphicsItem *asGraphicsItem = anchor->mStartItem->graphicsItem()) {
                    if (asGraphicsItem->isWidget()) {
                        const QGraphicsWidget *widget = static_cast<const QGraphicsWidget *>(asGraphicsItem);
                        QString itemText(widget->metaObject()->className());
                        if (itemText == "HbFrameItem"
                            || itemText == "HbTouchArea") {
                            continue;
                        }
                    }
                }
            }
            // if edge is connected to parent on same edge, and if the gap is zero, then don't show an arrow head
            if(anchor->mEndItem->isLayout()
                && anchor->mStartEdge == anchor->mEndEdge
                && anchor->mValue == 0) {
                    continue;
            }
            // Mirroring
            if (dir == Qt::RightToLeft) {
                if (anchor->mStartEdge == Hb::LeftEdge) {
                    anchor->mStartEdge = Hb::RightEdge;
                    anchor->mValue = -(anchor->mValue);
                } else if (anchor->mStartEdge == Hb::RightEdge) {
                    anchor->mStartEdge = Hb::LeftEdge;
                    anchor->mValue = -(anchor->mValue);
                } else if (anchor->mStartEdge == Hb::CenterHEdge) {
                    anchor->mValue = -(anchor->mValue);
                }

                if (anchor->mEndEdge == Hb::LeftEdge) {
                    anchor->mEndEdge = Hb::RightEdge;
                } else if (anchor->mEndEdge == Hb::RightEdge) {
                    anchor->mEndEdge = Hb::LeftEdge;
                }
            }

            QRectF startRect = anchor->mStartItem->geometry();
            QRectF endRect = anchor->mEndItem->geometry();

            // Fix non-pinned spacers issue
            if (startRect.left() == 0 && startRect.width() == 0) {
                startRect.adjust(endRect.left(), 0, endRect.left() + endRect.width(), 0);
            } else if (startRect.top() == 0 && startRect.height() == 0) {
                startRect.adjust(0, endRect.top(), 0, endRect.top() + endRect.height());
            }
            if (endRect.left() == 0 && endRect.width() == 0) {
                endRect.adjust(startRect.left(), 0, startRect.left() + startRect.width(), 0);
            } else if (endRect.top() == 0 && endRect.height() == 0) {
                endRect.adjust(0, startRect.top(), 0, startRect.top() + startRect.height());
            }

            // Work out the arrow line start point
            switch (anchor->mStartEdge) {
                case Hb::LeftEdge: start.rx() = startRect.left(); break;
                case Hb::RightEdge: start.rx() = startRect.right(); break;
                case Hb::CenterHEdge: start.rx() = startRect.center().x(); break;
                case Hb::TopEdge: start.ry() = startRect.top(); break;
                case Hb::BottomEdge: start.ry() = startRect.bottom(); break;
                case Hb::CenterVEdge: start.ry() = startRect.center().y(); break;
            }
            start2 = start;

            switch (anchor->mStartEdge) {
                case Hb::LeftEdge:
                case Hb::RightEdge:
                case Hb::CenterHEdge:
                {
                    // Set arrow end point
                    end.rx() = start.x() + anchor->mValue;

                    // Set arrow direction
                    arrowType = anchor->mValue < 0
                        ? Hb::LeftEdge
                        : Hb::RightEdge;

                    // Set vertical centering and staggered line point
                    qreal maxTop = qMax(startRect.top(), endRect.top());
                    qreal minBottom = qMin(startRect.bottom(), endRect.bottom());
                    if (maxTop < minBottom) {
                        start.ry() = (maxTop + minBottom) / 2;
                        start2.ry() = start.y();
                    } else {
                        const bool startAboveEnd = startRect.top() > endRect.top();
                        start.ry() = startAboveEnd ? endRect.bottom() : endRect.top();
                        start2.ry() = startAboveEnd ? startRect.top() : startRect.bottom();
                    }
                    end.ry() = start.y();
                    end2.ry() = start.y();

                    // Set end staggered point
                    if (anchor->mEndEdge == Hb::LeftEdge) {
                        end2.rx() = endRect.left();
                    } else if (anchor->mEndEdge == Hb::RightEdge) {
                        end2.rx() = endRect.right();
                    } else {
                        end2.rx() = endRect.center().x();
                    }
                }
                break;

                case Hb::TopEdge:
                case Hb::BottomEdge:
                case Hb::CenterVEdge:
                {
                    // Set arrow end point
                    end.ry() = start.y() + anchor->mValue;

                    // Set arrow direction
                    arrowType = anchor->mValue < 0
                        ? Hb::TopEdge
                        : Hb::BottomEdge;

                    // Set horizontal centering and staggered line point
                    qreal maxLeft = qMax(startRect.left(), endRect.left());
                    qreal minRight = qMin(startRect.right(), endRect.right());
                    if (maxLeft < minRight) {
                        start.rx() = (maxLeft + minRight) / 2;
                        start2.rx() = start.x();
                    } else {
                        bool startLeftOfEnd = startRect.left() > endRect.left();
                        start.rx() = startLeftOfEnd ? endRect.right() : endRect.left();
                        start2.rx() = startLeftOfEnd ? startRect.left() : startRect.right();
                    }
                    end.rx() = start.x();
                    end2.rx() = start.x();

                    // Set end staggered point
                    if (anchor->mEndEdge == Hb::TopEdge) {
                        end2.ry() = endRect.top();
                    } else if (anchor->mEndEdge == Hb::BottomEdge) {
                        end2.ry() = endRect.bottom();
                    } else {
                        end2.ry() = endRect.center().y();
                    }
                }
                break;
            }

            // Start painting block
            QPen myPen;
            QColor arrowColor = mLayout->isValid()
                ? mValidColor
                : mInvalidColor;
            QColor centerColor = Qt::yellow;

            myPen.setWidth(LINE_WIDTH);
            myPen.setColor(arrowColor);
            myPen.setStyle(Qt::DashLine);
            painter->setPen(myPen);
            painter->setBrush(arrowColor);
            painter->drawLine(start2, start);

            myPen.setStyle(Qt::SolidLine);
            painter->setPen(myPen);
            painter->drawLine(start, end);

            if (anchor->mStartEdge == Hb::CenterHEdge || anchor->mStartEdge == Hb::CenterVEdge) {
                painter->setBrush(centerColor);
            }

            // Only draw the start box if the anchor is long enough to show 3 times the head size
            // (head, stalk, and tail) otherwise it turns into a mush,
            // so the best thing is to show the triangle which at least shows the direction
            if (qAbs(anchor->mValue) > ARROW_HEAD_SIZE*3) {
                painter->drawRect(QRectF(
                    start2.x() - ARROW_HEAD_SIZE,
                    start2.y() - ARROW_HEAD_SIZE,
                    ARROW_HEAD_SIZE*2,
                    ARROW_HEAD_SIZE*2));
            }

            // Draw arrow head
            QPointF points[3] = {
                QPointF(0.0, 0.0),
                QPointF(0.0, 0.0),
                QPointF(end.x(), end.y())
            };
            if (arrowType == Hb::RightEdge) {
                points[0] = QPointF(end.x()-ARROW_HEAD_SIZE*2, end.y()-ARROW_HEAD_SIZE);
                points[1] = QPointF(end.x()-ARROW_HEAD_SIZE*2, end.y()+ARROW_HEAD_SIZE);
            } else if (arrowType == Hb::LeftEdge) {
                points[0] = QPointF(end.x()+ARROW_HEAD_SIZE*2, end.y()-ARROW_HEAD_SIZE);
                points[1] = QPointF(end.x()+ARROW_HEAD_SIZE*2, end.y()+ARROW_HEAD_SIZE);
            } else if (arrowType == Hb::TopEdge) {
                points[0] = QPointF(end.x()-ARROW_HEAD_SIZE, end.y()+ARROW_HEAD_SIZE*2);
                points[1] = QPointF(end.x()+ARROW_HEAD_SIZE, end.y()+ARROW_HEAD_SIZE*2);
            } else {
                points[0] = QPointF(end.x()-ARROW_HEAD_SIZE, end.y()-ARROW_HEAD_SIZE*2);
                points[1] = QPointF(end.x()+ARROW_HEAD_SIZE, end.y()-ARROW_HEAD_SIZE*2);
            }
            painter->drawPolygon(points, 3);

            // Draw invalid difference
            if (end != end2) {
                myPen.setColor(mInvalidColor);
                myPen.setStyle(Qt::DashLine);
                painter->setPen(myPen);
                painter->drawLine(end, end2);
            }

        } // End anchors for loop
    }
    painter->restore();
#else
    Q_UNUSED(painter);
#endif
}

