/*
 * 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: Menu view.
 *
 */
#include <QActionGroup>

#include <HbAction>
#include <HbAbstractItemView>
#include <HbAbstractViewItem>
#include <HbGroupBox>
#include <HbListView>
#include <HbMainWindow>
#include <HbInstance>
#include <HbSearchPanel>
#include <HbPushButton>
#include <HbToolBar>
#include <HbView>
#include <HbToolBarExtension>
#include <HbShrinkingVkbHost>

#include "hsallappsstate.h"
#include "hsallcollectionsstate.h"
#include "hscollectionstate.h"
#include "hsmenuitemmodel.h"
#include "hsmenuview.h"
#include "hslistviewitem.h"

/*!
    \class HsMenuView
    \ingroup group_hsmenustateplugin

    \brief Menu View.

    Provides UI for Application Library states.

    \sa HsAppLibraryState
*/

/*!
 Constructor

 Builds UI objects
 Sets up signals connections.

 \param builder Menu View builder.
 \param viewContext variable representing view context the view is to be prepared for.
 */
HsMenuView::HsMenuView(HsMenuViewBuilder &builder, HsViewContext viewContext):
    mViewContext(viewContext),
    mBuilder(builder),
    mProxyModel(new QSortFilterProxyModel(this)),
    mView(NULL),
    mListView(NULL),
    mViewLabel(NULL),
    mSearchListView(NULL),
    mSearchPanel(NULL),
    mVkbHost(NULL)
{
    mBuilder.setOperationalContext(HsItemViewContext);
    mBuilder.setViewContext(mViewContext);
    mBuilder.build();

    mView = mBuilder.currentView();
    mListView = mBuilder.currentListView();

    mViewLabel= mBuilder.currentViewLabel();

    mCollectionButton = mBuilder.collectionButton();

    mProxyModel->setFilterRole(CaItemModel::TextRole);
    mProxyModel->setFilterKeyColumn(1);
    mProxyModel->setSortRole(CaItemModel::TextRole);

    mVkbHost.reset(new HbShrinkingVkbHost(mView));

    connect(mListView,
            SIGNAL(activated(QModelIndex)),
            this, SIGNAL(activated(QModelIndex)));
    connect(mListView,
            SIGNAL(longPressed(HbAbstractViewItem *, QPointF)),
            this, SIGNAL(longPressed(HbAbstractViewItem *, QPointF)));
}

/*!
 Destructor

 Disconnects signals.
 */
HsMenuView::~HsMenuView()
{
}


/*!
 Sets model for item view.

 \param model Model the view is to represent in HsItemViewMode.
 */
void HsMenuView::setModel(HsMenuItemModel *model)
{
    HSMENUTEST_FUNC_ENTRY("HsMenuView::setModel");

    if (mListView->model()) {
        disconnect(mListView->model(),
                   SIGNAL(scrollTo(int, QAbstractItemView::ScrollHint)),
                   this,
                   SLOT(scrollToRow(int, QAbstractItemView::ScrollHint)));
    }
    mListView->setModel(model);
    mListView->setItemPrototype(new HsListViewItem());
    if (mListView->model()) {
        connect(mListView->model(),
                SIGNAL(scrollTo(int, QAbstractItemView::ScrollHint)),
                this,
                SLOT(scrollToRow(int, QAbstractItemView::ScrollHint)));
    }
    HSMENUTEST_FUNC_EXIT("HsMenuView::setModel");
}


/*!
    Returns label appropriate for the view.
    \return pointer to the label or NULL if the view has no label.
 */
HbGroupBox *HsMenuView::viewLabel() const
{
    return mViewLabel;
}

/*!
    \return first visible item index if any or default QModelIndex otherwise.
 */
QModelIndex HsMenuView::firstVisibleItemIndex(const HbListView *view) const
{
    QModelIndex result;

    const QList<HbAbstractViewItem *> array =
        view->visibleItems();

    if (array.count() >= 1) {
        result = array[0]->modelIndex();
    }
    return result;
}

/*!
    Makes the UI to show or hide view search panel.
    When search panel is shown the view toolbar and status pane
    are hidden until search panel is hidden.
    \param visible When true search panel will be shown,
     otherwise it will be hidden.
 */
void HsMenuView::setSearchPanelVisible(bool visible)
{
    HSMENUTEST_FUNC_ENTRY("HsMenuView::setSearchPanelVisible");

    if (visible) {

        mSearchViewInitialIndex = firstVisibleItemIndex(mListView);

        searchBegins();

        connectSearchItemViewsSignals();
        connectSearchPanelSignals();
        
    } else if (mSearchListView != NULL) {

        mIndexToScrollAfterSearchDone =
            firstVisibleItemIndex(mSearchListView);

        disconnectSearchPanelSignals();
        disconnectSearchItemViewsSignals();

        searchFinished();
    }
    HSMENUTEST_FUNC_EXIT("HsMenuView::setSearchPanelVisible");
}

/*!
    Makes the UI to show or hide view add collection button
    \param visibility When true button will be shown,
     otherwise it will be hidden.
 */
void HsMenuView::setContext(HsViewContext viewContext,
                            HsOperationalContext context)
{
    mBuilder.setViewContext(viewContext);
    mBuilder.setOperationalContext(context);
    mBuilder.build();
}


/*!
\return View widget of the menu view.
 */
HbView *HsMenuView::view() const
{
    return mView;
}

/*!
\return List view widget of the menu view.
 */
HbListView *HsMenuView::listView() const
{
    return mListView;
}

/*!
\return Collection button
 */
HbPushButton *HsMenuView::collectionButton() const
{
    return mCollectionButton;
}

/*!
 Makes search panel visible.
 Equivalent to \a setSearchPanelVisible(true)
 */
void HsMenuView::showSearchPanel()
{
    HSMENUTEST_FUNC_ENTRY("HsMenuView::showSearchPanel");

    setSearchPanelVisible(true);

    HSMENUTEST_FUNC_EXIT("HsMenuView::showSearchPanel");
}
#ifdef COVERAGE_MEASUREMENT
#pragma CTC SKIP
#endif //COVERAGE_MEASUREMENT skipped: it doubles other method

/*!
 Makes search panel invisible.
 Equivalent to \a setSearchPanelVisible(false).
 */
void HsMenuView::hideSearchPanel()
{
    HSMENUTEST_FUNC_ENTRY("HsMenuView::hideSearchPanel");
    setSearchPanelVisible(false);
    HSMENUTEST_FUNC_EXIT("HsMenuView::hideSearchPanel");
}
#ifdef COVERAGE_MEASUREMENT
#pragma CTC ENDSKIP
#endif //COVERAGE_MEASUREMENT

/*!
 Disable or enable search action button.
 \param disable If true action is disabled.
 */
void HsMenuView::disableSearch(bool disable)
{
    mBuilder.searchAction()->setDisabled(disable);
}

/*!
 Scrolls item view to requested row.
 \param row The row which is to get at the position pointed by \a hint.
 \param hint Position in the view the row should be scrolled to.
  */
void HsMenuView::scrollToRow(int row, QAbstractItemView::ScrollHint hint)
{
    HSMENUTEST_FUNC_ENTRY("HsMenuView::scrollToRow");

    mListView->scrollTo(mListView->model()->index(row, 0),
                        convertScrollHint(hint));

    HSMENUTEST_FUNC_EXIT("HsMenuView::scrollToRow");
}

/*!
     Converts scroll hints.
     \param Qt hint Where the target item should be positioned in
     visible area after scroll.
     \return Orbit version of hint
 */
HbAbstractItemView::ScrollHint HsMenuView::convertScrollHint(
    QAbstractItemView::ScrollHint hint)
{
    switch (hint) {
    case QAbstractItemView::EnsureVisible:
        return HbAbstractItemView::EnsureVisible;
    case QAbstractItemView::PositionAtTop:
        return HbAbstractItemView::PositionAtTop;
    case QAbstractItemView::PositionAtBottom:
        return HbAbstractItemView::PositionAtBottom;
    default:
        return HbAbstractItemView::PositionAtCenter;
    }
}

/*
 Connects \a activated and \a longPressed signals coming from search list
 view to emit corresponding signal of this object with translated model index
*/
void HsMenuView::connectSearchItemViewsSignals()
{
    connect(mSearchListView, SIGNAL(activated(QModelIndex)),
            this, SLOT(activatedProxySlot(QModelIndex)));
    connect(mSearchListView, SIGNAL(longPressed(HbAbstractViewItem *, QPointF)),
            this, SLOT(longPressedProxySlot(HbAbstractViewItem *, QPointF)));

}

/*!
 Disconnects \a activated and \a longPressed signals coming from list
 view from to emit corresponding signal
 of this object with translated model index.
*/
void HsMenuView::disconnectSearchItemViewsSignals()
{
    disconnect(mSearchListView, SIGNAL(activated(QModelIndex)),
               this, SLOT(activatedProxySlot(QModelIndex)));
    disconnect(mSearchListView, SIGNAL(longPressed(HbAbstractViewItem *, QPointF)),
               this,
               SLOT(longPressedProxySlot(HbAbstractViewItem *, QPointF)));
}


/*!
 Connects signals \a exitClicked and \a criteriaChanged emitted
 by search panel with handling slots of the object or its members.
*/
void HsMenuView::connectSearchPanelSignals()
{
    connect(mSearchPanel, SIGNAL(exitClicked()),
            this, SLOT(hideSearchPanel()));
    connect(mSearchPanel, SIGNAL(criteriaChanged(QString)),
            this, SLOT(findItem(QString)));


}

/*!
 Disconnects signals \a exitClicked and \a criteriaChanged emitted
 by search panel from handling slots of the object or its members
 Scrolls view to state before connections.
*/
void HsMenuView::disconnectSearchPanelSignals()
{
    disconnect(mSearchPanel, SIGNAL(exitClicked()),
               this, SLOT(hideSearchPanel()));

    disconnect(mSearchPanel, SIGNAL(criteriaChanged(QString)),
               this, SLOT(findItem(QString)));

}


/*!
 Looks up for item and if found scrolls to it.
 \param criteriaStr The item name to find.
 */
void HsMenuView::findItem(QString criteriaStr)
{
   qDebug() << QString("hsmenuview::findItem: %1").arg(criteriaStr);
    HSMENUTEST_FUNC_ENTRY("hsmenuview::findItem");

    mProxyModel->invalidate();
    mProxyModel->setSourceModel(mListView->model());
    mProxyModel->setFilterRegExp(QRegExp(
            QString("(^|\\b)%1").arg(criteriaStr), Qt::CaseInsensitive));

    mSearchListView->scrollTo(mProxyModel->index(0,0),
                              HbAbstractItemView::PositionAtTop);
    
    HSMENUTEST_FUNC_EXIT("hsmenuview::findItem");
}

/*!
 Makes the view display search panel with view representing search results.
 */
void HsMenuView::searchBegins()
{
    HSMENUTEST_FUNC_ENTRY("hsmenuview::searchBegins");


    mBuilder.setViewContext(mViewContext);
    mBuilder.setOperationalContext(HsSearchContext);
    mBuilder.build();
    mSearchListView = mBuilder.currentListView();
    mSearchPanel = mBuilder.currentSearchPanel();

    mView->hideItems(Hb::AllItems);

    mProxyModel->invalidate();
    mProxyModel->setSourceModel(mListView->model());
    mProxyModel->setFilterRegExp(QRegExp(QString(".*"), Qt::CaseInsensitive,
                                         QRegExp::RegExp));
    mSearchListView->setModel(mProxyModel);
    mSearchListView->setItemPrototype(new HsListViewItem());
    
    mSearchListView->scrollTo(
        mProxyModel->mapFromSource(mSearchViewInitialIndex),
        HbAbstractItemView::PositionAtTop);
    HSMENUTEST_FUNC_EXIT("hsmenuview::searchBegins");
}

/*!
  Ensures view does not contain search related elements and is scrolled
  to item chosen in search mode.
 */
void HsMenuView::searchFinished()
{
    HSMENUTEST_FUNC_ENTRY("hsmenuview::searchFinished");
    
    mBuilder.setViewContext(mViewContext);
    mBuilder.setOperationalContext(HsItemViewContext);
    mBuilder.build();
    mView->showItems(Hb::AllItems);

    const QModelIndex indexToScroll =
        mProxyModel->mapToSource(mIndexToScrollAfterSearchDone);

    mListView->scrollTo(indexToScroll, HbAbstractItemView::PositionAtTop);

    mSearchListView = NULL;
    mSearchPanel = NULL;
    HSMENUTEST_FUNC_EXIT("hsmenuview::searchFinished");
}

/*!
 Slot used to translate activated signal from proxy model to normal model.
 \param index representing an item activated in search list view.
 */
void HsMenuView::activatedProxySlot(const QModelIndex &index)
{
    emit activated(mProxyModel->mapToSource(index));
}

/*!
 Slot used to forward 'long pressed' signal with item description transladed 
 from search view context to context of the view search was requested from. 
 */
void HsMenuView::longPressedProxySlot(HbAbstractViewItem *item,
                                      const QPointF &coords)
{
    /*
      this is a kind of hack, introduced for reasons:
      item object should be reusable later, but orbit (or qt) prevents setting
      its index model to previous state
      */
    mSearchViewLongPressedIndex =
        mProxyModel->mapToSource(item->modelIndex());
    QScopedPointer<HbAbstractViewItem> itemNew(item->createItem());
    itemNew->setModelIndex(mSearchViewLongPressedIndex);
    emit longPressed(itemNew.data(), coords);
}

/*!
 Add the view to main window and search action with \a showSearchPanel
 slot of the window.
 */
void HsMenuView::activate()
{
    connect(mBuilder.searchAction(), SIGNAL(triggered()),
            this, SLOT(showSearchPanel()));
}

/*!
 Disconnecs search action and \a showSearchPanel slot of the window.
 */
void HsMenuView::inactivate()
{
    // handle app key event
    mBuilder.toolBarExtension()->close();

    disconnect(mBuilder.searchAction(), SIGNAL(triggered()),
               this, SLOT(showSearchPanel()));
}
