/****************************************************************************
**
** 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 HbWidgets 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 "hbdataform_p.h"
#include <hbdataformviewitem.h>
#include "hbdataformviewitem_p.h"
#include "hbdataitemcontainer_p.h"
#include "hbdatagroup_p_p.h"
#include <hbcombobox.h>
#include <hbapplication.h>
#include <hbdataformmodel.h>
#include "hbdataformheadingwidget_p.h"
#include "hbtreemodeliterator_p.h"
#include <hbdatagroup_p.h>

#include <QStringListModel>
#include <QHash>

HbDataFormPrivate::HbDataFormPrivate() :
    HbAbstractItemViewPrivate(),
    mHeadingWidget(0)
{
}

HbDataFormPrivate::~HbDataFormPrivate()
{
}

int HbDataFormPrivate::childCount(const QModelIndex &index) const
{
    Q_Q(const HbDataForm);

    int itemCount = 0;
    int rowCount = 0;
    if(!index.isValid()) {
        return 0;
    }
    if (q->isExpanded(index)) {
        rowCount = index.model()->rowCount(index);
    }
    itemCount += rowCount;
    for (int row = 0; row < rowCount; ++row) {
        itemCount += childCount(index.child(row, 0));
    }
    return itemCount;
}

void HbDataFormPrivate::init()
{
    Q_Q(HbDataForm);

    QList<HbAbstractViewItem*> protos;
    protos.append(new HbDataFormViewItem());
    protos.append(new HbDataGroup());
    q->setItemPrototypes(protos);
    q->setClampingStyle(HbScrollArea::BounceBackClamping);
    q->setItemRecycling(false);
    treeModelIterator()->setItemContainer(mContainer);
}

void  HbDataFormPrivate::_q_page_changed(int index)
{
    Q_Q(const HbDataForm);

    QModelIndex childIndex = pageModelIndex(index);
    //QModelIndex childIndex = q->model()->index(index,0);
    HbDataFormModelItem::DataItemType itemType =
        static_cast<HbDataFormModelItem::DataItemType>(
        (childIndex.data(HbDataFormModelItem::ItemTypeRole)).toInt());

    if(mHeadingWidget->mPageCombo) {
        if(mHeadingWidget->mPageCombo->currentIndex() != index) {
            QObject::disconnect(mHeadingWidget->mPageCombo,SIGNAL(currentIndexChanged(int)),
                q,SLOT(_q_page_changed(int)));
            mHeadingWidget->mPageCombo->setCurrentIndex(index);
            QObject::connect(mHeadingWidget->mPageCombo,SIGNAL(currentIndexChanged(int)),
                q,SLOT(_q_page_changed(int)));
        }
    }

   /* QStringListModel *model = static_cast<QStringListModel*>(
        mHeadingWidget->mPageCombo->model());
    QModelIndex changedIndex = model->index(index, 0);*/
    if(itemType == HbDataFormModelItem::FormPageItem) {
        if(index != mHeadingWidget->mActivePage) {
            QModelIndex prevPageIndex = pageModelIndex( mHeadingWidget->mActivePage );
            QModelIndex newPageIndex = pageModelIndex( index );
            if(prevPageIndex.isValid()) {
                HbDataGroup *prevPage = static_cast<HbDataGroup *>(
                                               q->itemByIndex(prevPageIndex));
                if(prevPage) {
                    HbDataGroupPrivate::d_ptr(prevPage)->setExpanded(false);
                } else {
                    mContainer->setItemTransientStateValue(prevPageIndex, "expanded", false);

                }
            }
            if(newPageIndex.isValid()) {
                HbDataGroup *newPage = static_cast<HbDataGroup *>(
                                               q->itemByIndex(newPageIndex));
                if(newPage) {
                    HbDataGroupPrivate::d_ptr(newPage)->setExpanded(true);
                } else {
                    mContainer->setItemTransientStateValue(newPageIndex, "expanded", true);
                }
            }
            mHeadingWidget->mActivePage = index;
        }
    }
}

/*
    Function to return valid modelIndex for corresponding formpage combo index.
*/
QModelIndex HbDataFormPrivate::pageModelIndex(int index) const 
{
    const Q_Q(HbDataForm);
    int pageIndex = -1;
    QModelIndex modelIndex;
    
    // Make sure that the child is groupPage type and current item is group.
    if( index >= 0) {

        HbDataFormModelItem *groupModelItem = static_cast<HbDataFormModel*>(q->model())->invisibleRootItem();
        int childCount = groupModelItem->childCount();

        for( int i = 0; i < childCount; i++) {
            HbDataFormModelItem *child = groupModelItem->childAt(i);
            if( child->type() == HbDataFormModelItem::FormPageItem ) {
                pageIndex ++;
                // get the index of groupPage
                if(pageIndex == index) {
                    modelIndex = static_cast<HbDataFormModel*>(q->model())->indexFromItem(child);
                    break;                  
                }
            }        
        }
    }
    return modelIndex;
}


/*
    collapses all grouppages .
*/
void HbDataFormPrivate::collapseAllGroupPages(const QModelIndex index)  
{
    Q_Q(HbDataForm);  
    HbDataFormModelItem *modelItem = static_cast<HbDataFormModel *>(q->model())->itemFromIndex(index);
    for(int i = 0; i < modelItem->childCount() ; i++) {
        if( modelItem->childAt(i)->type() == HbDataFormModelItem::GroupPageItem ) {
            QModelIndex childIndex = static_cast<HbDataFormModel *>(q->model())->indexFromItem(modelItem->childAt(i));
            mContainer->setItemTransientStateValue(childIndex, "expanded", false);
        }
    }
}

/*!
    Creates a DataForm Page \a page in DataForm .
    DataForm Page is an invisible DataItem which can be changed/selected using combo box.
    \sa addFormPage
*/
void HbDataFormPrivate::addFormPage(const QString& page)
{
    Q_Q(HbDataForm);

    // Create combobox if not created 
    if(!mHeadingWidget) {
        mHeadingWidget = new HbDataFormHeadingWidget();
        static_cast<HbDataItemContainer*>(mContainer)->setFormHeading(mHeadingWidget);
    }

    if(!mHeadingWidget->mPageCombo) {
        mHeadingWidget->createPrimitives();
        mHeadingWidget->mPageCombo = new HbComboBox(mHeadingWidget);
        HbStyle::setItemName(mHeadingWidget->mPageCombo,"dataForm_Combo");
        mHeadingWidget->callPolish();
        // setFormHeading to the layout
        if(mHeadingWidget->mPageCombo || !mHeadingWidget->mDescription.isEmpty() || 
            !mHeadingWidget->mHeading.isEmpty()) {
            static_cast<HbDataItemContainer*>(mContainer)->setFormHeading(mHeadingWidget);
        }
        QObject::connect(mHeadingWidget->mPageCombo,SIGNAL(currentIndexChanged(int)),
            q,SLOT(_q_page_changed(int)));
    }

    // Get the model and add the page string to the model of combobox
    QStringListModel *model = static_cast<QStringListModel*>(mHeadingWidget->mPageCombo->model());
    if(!model) {
        QStringList list;
        model = new QStringListModel(list);
    }

    QStringList list = model->stringList();
    if(!list.contains(page)){
        list.append(page);
        model->setStringList(list);
        mHeadingWidget->mPageCombo->setModel(model);
        //mHeadingWidget->mPageCombo->setCurrentIndex(model->index(0,0));
        mHeadingWidget->mPageCombo->setCurrentIndex(0);
        _q_page_changed((mHeadingWidget->mPageCombo)->currentIndex());
    }
    mHeadingWidget->updatePrimitives();
}

void HbDataFormPrivate::removeFormPage(const QString& page) 
{
    Q_Q(HbDataForm);    

    if(mHeadingWidget && mHeadingWidget->mPageCombo) {
        // if we are emoving the current page
        if(mHeadingWidget->mPageCombo->currentText() == page){
        // if we are removing the last page then set the current page as 0th
            if( mHeadingWidget->mPageCombo->findText(page) + 1 == mHeadingWidget->mPageCombo->count()) {
                mHeadingWidget->mPageCombo->setCurrentIndex(0);
            } else {// set next page as the curent page
                mHeadingWidget->mPageCombo->setCurrentIndex(mHeadingWidget->mPageCombo->findText(page) + 1);
            }
        }
    }
    QObject::disconnect(mHeadingWidget->mPageCombo,SIGNAL(currentIndexChanged(int)),
            q,SLOT(_q_page_changed(int)));

    mHeadingWidget->mPageCombo->removeItem(mHeadingWidget->mPageCombo->findText(page));
    mHeadingWidget->mActivePage = mHeadingWidget->mPageCombo->currentIndex();
        
    QObject::connect(mHeadingWidget->mPageCombo,SIGNAL(currentIndexChanged(int)),
            q,SLOT(_q_page_changed(int)));


   mHeadingWidget->callPolish();    
}

/*void HbDataFormPrivate::_q_item_displayed(const QModelIndex &index)
{
    Q_Q( HbDataForm);
    emit q->itemShown(index);
    emit q->activated(index);
    qWarning("activated signal will not be emitted when items are created ," 
        "instead itemShown SIGNAL should be used");
}*/

void HbDataFormPrivate::makeConnection(QModelIndex index, HbWidget* widget)
{
    Q_Q( HbDataForm);
    if(!index.isValid()){
        return;
    }
    if(q->model()) {
        HbDataFormModelItem *modelItem = static_cast<HbDataFormModel *>(q->model())->itemFromIndex(index);
        if(modelItem){
            QList<ItemSignal> signalList = mConnectionList.values(modelItem);
            if(signalList.count() > 0){
                if(widget){
                    foreach(const ItemSignal& signal, signalList) {
                        QObject *objct = signal.receiver;
                        QString signalName = signal.signal;
                        QString slot = signal.slot;
                        // Make connection
                        if(objct) { 
                            QObject::connect(widget, signalName.toAscii().data(), 
                                objct,slot.toAscii().data());
                        }
                        
                    }
                }
            }
        }
    }
}


void HbDataFormPrivate::removeConnection(HbDataFormModelItem * modelItem, 
                                  QString signal, 
                                  QObject *receiver, 
                                  QString slot)
{
    Q_Q( HbDataForm);
    if(q->model()) {
        if(modelItem){
            QList<ItemSignal> signalList = mConnectionList.values(modelItem);
            mConnectionList.remove(modelItem);
            if(signalList.count() > 0){
            QModelIndex index = 
                static_cast<HbDataFormModel*>(q->model())->indexFromItem(modelItem);
                HbDataFormViewItem *viewItem = static_cast<HbDataFormViewItem*>(q->itemByIndex(index));
                if(viewItem){
                    HbWidget *contentWidget = HbDataFormViewItemPrivate::d_ptr(viewItem)->mContentWidget;
                    if(contentWidget){
                        //foreach(ItemSignal signalItem, signalList) {
                        for(int i = 0; i < signalList.count() ;i++){
                            ItemSignal signalItem = signalList.at(i);
                            if(receiver == signalItem.receiver &&
                            signal == signalItem.signal &&
                            slot == signalItem.slot){
                            // disconnect
                                QObject::disconnect(contentWidget, signal.toAscii().data(), 
                                    receiver,slot.toAscii().data());
                                signalList.removeAt(i);
                                for(int j = 0; j < signalList.count(); j++){
                                    mConnectionList.insertMulti(modelItem, signalList.at(j));
                                }
                                break;
                            }                            
                        }
                    }
                }
            }
        }
    }
}

void HbDataFormPrivate::connectNow(HbDataFormModelItem * modelItem, 
                                   QString signal, 
                                   QObject *receiver, 
                                   QString slot)
{
    Q_Q( HbDataForm);    
    
    if(q->model()) {
        QModelIndex index = static_cast<HbDataFormModel*>(q->model())->indexFromItem(modelItem);
        if(modelItem){
            HbDataFormViewItem *viewItem =static_cast<HbDataFormViewItem*>( q->itemByIndex(index) );
            if(viewItem){
                if(HbDataFormViewItemPrivate::d_ptr(viewItem)->mContentWidget) {
                    // Make connection
                        QObject::connect(HbDataFormViewItemPrivate::d_ptr(viewItem)->mContentWidget, signal.toAscii().data(), 
                            receiver,slot.toAscii().data());
                }
            }
        }
    }
}

void HbDataFormPrivate::removeAllConnection()
{
    Q_Q( HbDataForm);
    if(q->model()) {
        QList<HbDataFormModelItem*> keys = mConnectionList.uniqueKeys();

        foreach(HbDataFormModelItem* item ,keys) {
            QList<ItemSignal> signalList = mConnectionList.values(item);
            mConnectionList.remove(item);
            if(signalList.count() > 0){
                QModelIndex index = static_cast<HbDataFormModel*>(q->model())->indexFromItem(item);
                HbDataFormViewItem *viewItem = static_cast<HbDataFormViewItem*> (q->itemByIndex(index));
                if(viewItem){
                    HbWidget *contentWidget = HbDataFormViewItemPrivate::d_ptr(viewItem)->mContentWidget;
                    // disconnect signal and remove signal from list
                    for(int i = 0;i<signalList.count();) {
                        ItemSignal signalItem = signalList.takeAt(i);
                            // Make connection
                        QObject::disconnect(contentWidget, signalItem.signal.toAscii().data(), 
                                signalItem.receiver,signalItem.slot.toAscii().data());
                            
                        
                    }
                }
            }
        }
    }
}

void HbDataFormPrivate::removeAllConnection(HbDataFormModelItem *modelItem)
{
    Q_Q( HbDataForm);
    if(q->model()) {
        if(modelItem){
            QList<ItemSignal> signalList = mConnectionList.values(modelItem);
            mConnectionList.remove(modelItem);
            if(signalList.count() > 0){
                QModelIndex index = static_cast<HbDataFormModel*>(q->model())->indexFromItem(modelItem);
                HbDataFormViewItem *viewItem =static_cast<HbDataFormViewItem*>( q->itemByIndex(index));
                if(viewItem){
                    HbWidget *contentWidget = HbDataFormViewItemPrivate::d_ptr(viewItem)->mContentWidget;
                    // disconnect signal and remove signal from list
                    for(int i = 0;i<signalList.count(); ) {
                        ItemSignal signalItem = signalList.takeAt(i);
                            // Make connection
                        QObject::disconnect(contentWidget, signalItem.signal.toAscii().data(), 
                                signalItem.receiver,signalItem.slot.toAscii().data());
                    }
                }
            }
        }
    }
}

void HbDataFormPrivate::emitActivated(const QModelIndex &modelIndex)
{
    Q_Q( HbDataForm);
    emit q->activated(modelIndex);
}

