/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/

#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsLinearLayout>
#include <QDir>

#include <HbMainWindow>
#include <HbVkbHost>

#include "hsidlewidget.h"
#include "hsscene.h"
#include "hspage.h"
#include "hswidgethost.h"
#include "hswallpaper.h"
#include "hstrashbinwidget.h"
#include "hspageindicator.h"
#include "hsdocumentloader.h"
#include "hsconfiguration.h"

namespace
{
    const char gControlLayerDocmlName[] = "controllayer.docml";
    const char gControlLayerName[]      = "controlLayer";
    const char gTrashBinName[]          = "trashBin";
    const char gPageIndicatorName[]     = "pageIndicator";
}

/*!
    \class HsIdleWidget
    \ingroup group_hshomescreenstateplugin
    \brief View part of the home screen idle state.

    Maintains the idle view ui layers and takes care of 
    receiving user input and communicating it to the idle 
    state for further processing.
*/

/*!
    Constructs a new idle widget with the given \a parent.
*/
HsIdleWidget::HsIdleWidget(QGraphicsItem *parent)
  : HbWidget(parent),
    mControlLayer(0), mPageLayer(0), mSceneLayer(0),
    mDelayedPressEvent(0),
    mTrashBin(0), mPageIndicator(0)
{
    setFlag(ItemHasNoContents);
    
    loadControlLayer();

    QGraphicsLinearLayout *linearLayout = 0;

    linearLayout = new QGraphicsLinearLayout(Qt::Horizontal);
    linearLayout->setContentsMargins(0, 0, 0, 0);
    linearLayout->setSpacing(0);
    mPageLayer = new HbWidget(this);
    mPageLayer->setLayout(linearLayout);
    mPageLayer->setZValue(1);
    
    linearLayout = new QGraphicsLinearLayout(Qt::Horizontal);
    linearLayout->setContentsMargins(0, 0, 0, 0);
    linearLayout->setSpacing(0);
    mSceneLayer = new HbWidget(this);
    mSceneLayer->setLayout(linearLayout);
    mSceneLayer->setZValue(0);
}

/*!
    Destroys this idle widget.
*/
HsIdleWidget::~HsIdleWidget()
{
    clearDelayedPress();

    QList<HsPage *> pages = HsScene::instance()->pages();
    foreach (HsPage *page, pages) {
        page->setParentItem(0);
        if (page->scene()) {
            page->scene()->removeItem(page);
        }
    }

    HsWallpaper *wallpaper = HsScene::instance()->wallpaper();
    wallpaper->setParentItem(0);
    if (wallpaper->scene()) {
        wallpaper->scene()->removeItem(wallpaper);
    }
}

/*!
    Layouts the ui layers according to the given \a rect.
*/
void HsIdleWidget::setGeometry(const QRectF &rect)
{
    int n = HsScene::instance()->pages().count();
	QRectF layoutRect(HsScene::instance()->mainWindow()->layoutRect());
    if (layoutRect == rect || (layoutRect.height() == rect.width() && layoutRect.width() == rect.height())) {
        mControlLayer->resize(rect.size());
		mPageLayer->resize(n * rect.width(), rect.height());
		mSceneLayer->resize(2 * rect.width() + HsConfiguration::bounceEffect(), rect.height());
		HbWidget::setGeometry(rect);
	} else {
		QRectF sceneRect = mapToScene(rect).boundingRect();
		sceneRect.setTop(-sceneRect.top());
		HbWidget::setGeometry(sceneRect);
	}
}

/*!
    Stores the given mouse press \a event.
*/
void HsIdleWidget::captureDelayedPress(QGraphicsSceneMouseEvent *event)
{
    if (event) {
        mDelayedPressEvent = new QMouseEvent(QEvent::MouseButtonPress,
            event->scenePos().toPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
        mDelayedPressEvent->setAccepted(false);
    }
}

/*!
    Sends the stored mouse press event.
*/
void HsIdleWidget::sendDelayedPress()
{
    if (mDelayedPressEvent) {
        QApplication::sendEvent(HsScene::mainWindow()->viewport(), mDelayedPressEvent);
        clearDelayedPress();     
    }
}

/*!
    Deletes the stored mouse press event.
*/
void HsIdleWidget::clearDelayedPress()
{    
    if (mDelayedPressEvent) {
        delete mDelayedPressEvent;
        mDelayedPressEvent = 0;
    }
}

/*!
    Sets the active page \a index to the page 
    indicator.
*/
void HsIdleWidget::setActivePage(int index)
{
    mPageIndicator->setActiveItemIndex(index);
}

/*!
    Inserts the given \a page at index position
    \a index in the page layer.
*/
void HsIdleWidget::insertPage(int index, HsPage *page)
{
    QGraphicsLinearLayout *layout = 
        static_cast<QGraphicsLinearLayout *>(mPageLayer->layout());
    layout->insertItem(index, page);
    mPageLayer->resize(
        layout->count() * size().width(), size().height());
}

/*!
    Removes the page at index position
    \a index in the page layer.
*/
void HsIdleWidget::removePage(int index)
{
    QGraphicsLinearLayout *layout = 
        static_cast<QGraphicsLinearLayout *>(mPageLayer->layout());
    layout->removeAt(index);
    mPageLayer->resize(
        layout->count() * size().width(), size().height());
}

/*!
    \fn HsIdleWidget::controlLayer() const

    Returns the control layer.
*/

/*!
    \fn HsIdleWidget::pageLayer() const

    Returns the page layer.
*/

/*!
    \fn HsIdleWidget::sceneLayer() const

    Returns the scene layer.
*/

/*!
    \fn HsIdleWidget::trashBin() const

    Returns the trashbin widget.
*/

/*!
    \fn HsIdleWidget::pageIndicator() const

    Returns the page indicator widget.
*/

/*!
    \fn HsIdleWidget::mousePressed(QGraphicsItem *, QGraphicsSceneMouseEvent *, bool &)

    The idle state connects to this signal for handling mouse
    press events. It filters events for the item \a watched. 
    \a event is the filtered event. Sets the \a filtered true 
    if the event was filtered by this handler.
*/

/*!
    \fn HsIdleWidget::mouseMoved(QGraphicsItem *, QGraphicsSceneMouseEvent *, bool &)

    The idle state connects to this signal for handling mouse
    move events. It filters events for the item \a watched. 
    \a event is the filtered event. Sets the \a filtered true 
    if the event was filtered by this handler.
*/

/*!
    \fn HsIdleWidget::mouseReleased(QGraphicsItem *, QGraphicsSceneMouseEvent *, bool &)

    The idle state connects to this signal for handling mouse
    release events. It filters events for the item \a watched. 
    \a event is the filtered event. Sets the \a filtered true 
    if the event was filtered by this handler.
*/

/*!
    Sets the trashbin visible and hides the page indicator.
*/
void HsIdleWidget::showTrashBin()
{
    mPageIndicator->hide();
    mTrashBin->show();
}
 
/*!
    Sets the page indicator visible and hides the trashbin.
*/
void HsIdleWidget::showPageIndicator()
{
    mTrashBin->hide();
    mTrashBin->deactivate();
    mPageIndicator->setSpacing(HsConfiguration::pageIndicatorSpacing()); // for usability optimization widget, can be removed later on
    mPageIndicator->setVisible(1 < mPageIndicator->itemCount());
}

/*!
    Filters the main window's graphics scene (\a object) \a event.
*/
bool HsIdleWidget::eventFilter(QObject *object, QEvent *event)
{
    Q_UNUSED(object)

    if (HbVkbHost::activeVkbHost()) {
        setFiltersChildEvents(false);
        return false;
    }

    bool filtered = false;

    switch (event->type()) {
    case QEvent::GraphicsSceneMousePress:
        if (mDelayedPressEvent &&
            scene()->mouseGrabberItem()) {
            scene()->mouseGrabberItem()->ungrabMouse();
        }
        setFiltersChildEvents(!mDelayedPressEvent);
        break;
    case QEvent::GraphicsSceneMouseMove:
        emit mouseMoved(0, static_cast<QGraphicsSceneMouseEvent *>(event), filtered);
        break;
    case QEvent::GraphicsSceneMouseRelease:
        setItemsFocusable();
        emit mouseReleased(0, static_cast<QGraphicsSceneMouseEvent *>(event), filtered);
        if (filtered && scene()->mouseGrabberItem()) {
            scene()->mouseGrabberItem()->ungrabMouse();
        }        
        break;
    default:
        break;
    }

    return filtered;
}

/*!
    Filters events for the item \a watched. \a event is the filtered event.
*/
bool HsIdleWidget::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
{
    bool filtered = false;

    switch (event->type()) {
    case QEvent::GraphicsSceneMousePress:
        emit mousePressed(watched, static_cast<QGraphicsSceneMouseEvent *>(event), filtered);
        if (filtered) {
            setItemsUnfocusable(static_cast<QGraphicsSceneMouseEvent *>(event));
        }
        break;    
    default:
        break;
    }

    return filtered;
}

/*!
    Reimplements QGraphicsWidget::polishEvent().
*/
void HsIdleWidget::polishEvent()
{
    HsScene *scene = HsScene::instance();
    Q_ASSERT(scene);

    QGraphicsLinearLayout *layout = 
        static_cast<QGraphicsLinearLayout *>(mPageLayer->layout());
    QList<HsPage *> pages = scene->pages();
    foreach (HsPage *page, pages) {
        layout->addItem(page);
    }

    layout = static_cast<QGraphicsLinearLayout *>(mSceneLayer->layout());
    HsWallpaper *wallpaper = HsScene::instance()->wallpaper();
    layout->addItem(wallpaper);
           
    mPageIndicator->initialize(pages.count(), scene->activePageIndex());    
    showPageIndicator();

    HsScene::mainWindow()->scene()->installEventFilter(this);
}

/*!
    Loads the control layer declared in a docml file.
*/
void HsIdleWidget::loadControlLayer()
{
    HsDocumentLoader loader;
    bool loaded = false;

#ifndef Q_OS_SYMBIAN
    QString path = QDir::currentPath();
#else
    QString path = "c:";
#endif

    QString file = path + "/hsresources/" + gControlLayerDocmlName;
    QString fallbackPath = QString(":/") + gControlLayerDocmlName;

    if (QFile::exists(file)) {
        loader.load(file, &loaded);
        if (!loaded) {
            loader.load(fallbackPath, &loaded);
        }
    } else {
        loader.load(fallbackPath, &loaded);       
    }

    if (loaded) {
        mControlLayer = qobject_cast<HbWidget *>(loader.findWidget(gControlLayerName));
        mControlLayer->setZValue(2);
        mControlLayer->setParentItem(this);

        mTrashBin = qobject_cast<HsTrashBinWidget *>(loader.findWidget(gTrashBinName));
        mTrashBin->setZValue(1e6);

        mPageIndicator = qobject_cast<HsPageIndicator *>(loader.findWidget(gPageIndicatorName));
        mPageIndicator->setZValue(1e6);
    } else {
        // TODO: Handle error.
    }
}

/*!
    Sets the items under the given mouse \a event scene position 
    unfocusable and stores the items.
*/
void HsIdleWidget::setItemsUnfocusable(QGraphicsSceneMouseEvent *event)
{
    mFocusableItems.clear();

    if (!scene()) {
        return;
    }

    QList<QGraphicsItem *> items = scene()->items(event->scenePos());
    if (items.isEmpty()) {
        return;
    }

    int i = 0;
    for (; i < items.count(); ++i) {
        if (mPageLayer->isAncestorOf(items[i])) {
            ++i;
            break;
        }
    }
 
    HsPage *page = HsScene::instance()->activePage();
    QList<HsWidgetHost *> widgets = page->widgets();
    
    for (; i < items.count(); ++i) {
        QGraphicsItem *item = items.at(i);
        if (page->isAncestorOf(item)) {
            foreach (HsWidgetHost *widget, widgets) {
                if ((item == widget || widget->isAncestorOf(item))&& item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) {
                    if (!item->isWidget() || static_cast<QGraphicsWidget *>(item)->focusPolicy() & Qt::ClickFocus) {
                        item->setFlag(QGraphicsItem::ItemIsFocusable, false);
                        mFocusableItems.append(item);
                    }
                }
            }
        }
    }
}
 
/*!
    Sets the stored items focusable.
*/
void HsIdleWidget::setItemsFocusable()
{
    foreach (QGraphicsItem *item, mFocusableItems) {
        item->setFlag(QGraphicsItem::ItemIsFocusable);
    }
    mFocusableItems.clear();
}
