/*
 * 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:Conversation (chat) view for messaging application.
 *
 */

#include "msgconversationview.h"

// SYSTEM INCLUDES
#include <hbapplication.h>
#include <HbMenu>
#include <HbAction>
#include <HbListView>
#include <HbMessageBox>
#include <HbNotificationDialog>
#include <HbFrameBackground>
#include <xqservicerequest.h>
#include <HbStaticVkbHost>
#include <HbStyleLoader>
#include <xqaiwrequest.h>
#include <xqappmgr.h>
#include <HbMainWindow>

#include <QInputContext>
#include <QDir>
#include <QDateTime>
#include <QGraphicsLinearLayout>
#include <QStandardItemModel>

#include <cntservicescontact.h>
#include <ccsdefs.h>
#include <centralrepository.h>
#include <MmsEngineDomainCRKeys.h>

// USER INCLUDES
#include "msgcontactsutil.h"
#include "msgsendutil.h"
#include "msgconversationviewitem.h"
#include "conversationsengine.h"
#include "convergedmessageid.h"
#include "conversationsenginedefines.h"
#include "msgcontactcardwidget.h"
#include "msgeditorwidget.h"
#include "msgviewdefines.h"
#include "debugtraces.h"
#include "unidatamodelloader.h"
#include "unidatamodelplugininterface.h"
#include "ringbc.h"
#include "mmsconformancecheck.h"
#include "msgsettingsview.h"
#include "unieditorpluginloader.h"
#include "unieditorplugininterface.h"
#include "msgaudiofetcherdialog.h"

//Item specific menu.

#define LOC_COMMON_OPEN hbTrId("txt_common_menu_open")
#define LOC_COMMON_DELETE hbTrId("txt_common_menu_delete")
#define LOC_COMMON_FORWARD hbTrId("txt_common_menu_forward")
#define LOC_COMMON_DOWNLOAD hbTrId("txt_messaging_menu_download")
#define LOC_COMMON_SEND  hbTrId("txt_common_button_send") 
#define LOC_COMMON_SAVE  hbTrId("txt_common_menu_save")

#define LOC_DELETE_MESSAGE hbTrId("txt_messaging_dialog_delete_message")
#define LOC_DELETE_SHARED_MESSAGE hbTrId("txt_messaging_dialog_same_message_exists_in_multip")
#define LOC_SAVE_TO_CONTACTS hbTrId("txt_messaging_menu_save_to_contacts")

//main menu
#define LOC_ATTACH          hbTrId("txt_messaging_opt_attach")
#define LOC_PHOTO           hbTrId("txt_messaging_opt_attach_sub_photo")
#define LOC_SOUND           hbTrId("txt_messaging_opt_attach_sub_sound")
#define LOC_VCARD           hbTrId("txt_messaging_list_business_card")
#define LOC_ADD_RECIPIENTS  hbTrId("txt_messaging_opt_add_recipients")
#define LOC_ADD_SUBJECT     hbTrId("txt_messaging_opt_add_subject")

#define LOC_MSG_SEND_FAILED hbTrId("txt_messaging_dialog_message_sending_failed")
#define LOC_DIALOG_SMS_SETTINGS_INCOMPLETE hbTrId("txt_messaging_dialog_sms_message_centre_does_not_e")
#define LOC_DIALOG_SAVE_RINGTONE hbTrId("txt_conversations_dialog_save_ringing_tone")
#define LOC_MMS_RETRIEVAL_FAILED hbTrId("txt_messaging_dialog_mms_retrieval_failed")


const int INVALID_MSGID = -1;
const int INVALID_CONVID = -1;
const int CONTACT_INSERTION_MODE = 1;
const int VCARD_INSERTION_MODE = 0;

//---------------------------------------------------------------
// MsgConversationView::MsgConversationView
// @see header file
//---------------------------------------------------------------
MsgConversationView::MsgConversationView(MsgContactCardWidget *contactCardWidget,
    QGraphicsItem *parent) :
    MsgBaseView(parent),
    mConversationList(NULL),
    mMessageModel(NULL),
    mEditorWidget(NULL),
    mContactCardWidget(contactCardWidget),
    mSendUtil(NULL),
    mVkbHost(NULL),
    mDialog(NULL),
    mVisibleIndex(),
    mModelPopulated(false),
    mViewReady(false)
{
    //create send utils
    mSendUtil = new MsgSendUtil(this);
    //initialize view
    setupView();
    setupMenu();
    
    connect(ConversationsEngine::instance(), 
                     SIGNAL(conversationModelUpdated()),
                     this, 
                     SLOT(scrollToBottom()));

    connect(ConversationsEngine::instance(), 
                         SIGNAL(conversationViewEmpty()),
                         this, 
                         SLOT(onConversationViewEmpty()));
}

//---------------------------------------------------------------
// MsgConversationView::~MsgConversationView
// @see header file
//---------------------------------------------------------------
MsgConversationView::~MsgConversationView()
{
    //delete the popup dialog
    delete mDialog;
}
//---------------------------------------------------------------
// MsgConversationView::setupView
// @see header file
//---------------------------------------------------------------
void MsgConversationView::setupView()
{
    // Create HbListView and set properties
    mConversationList = new HbListView();
    if (!HbStyleLoader::registerFilePath(":/layouts")) {
        QDEBUG_WRITE("ERROR: ConversationView -> HbStyleLoader::registerFilePath");
    }
    mConversationList->setLayoutName("custom");
    mConversationList->setItemRecycling(true);
    MsgConversationViewItem *item = new MsgConversationViewItem(this);
    HbFrameBackground defaultBackground;
    defaultBackground.setFrameGraphicsName(QString(""));
    item->setDefaultFrame(defaultBackground);
    mConversationList->setItemPrototype(item);
    mConversationList->setSelectionMode(HbListView::NoSelection);
    mConversationList->setClampingStyle(HbScrollArea::BounceBackClamping);
    mConversationList->setScrollingStyle(HbScrollArea::PanOrFlick);

    mMessageModel = ConversationsEngine::instance()->getConversationsModel();
    
    connect(ConversationsEngine::instance(), 
            SIGNAL(conversationModelPopulated()), 
            this, 
            SLOT(populateConversationsView()));
    
    connect(mConversationList, SIGNAL(activated(QModelIndex)),
            this, SLOT(openItem(QModelIndex)));
    connect(this->mainWindow(), SIGNAL(aboutToChangeOrientation()),
                this, SLOT(onOrientationAboutToBeChanged()));
    
    connect(this->mainWindow(), SIGNAL(orientationChanged(Qt::Orientation)),
                    this, SLOT(onOrientationChanged(Qt::Orientation)));

    // Long tap list item
    connect(mConversationList, SIGNAL(longPressed(HbAbstractViewItem*, QPointF)),
            this, SLOT(longPressed(HbAbstractViewItem*, QPointF)));

    // Create message editor widget, will be displayed based on msg type.
    mEditorWidget = new MsgEditorWidget(this);
    mEditorWidget->hide();

    connect(mEditorWidget, SIGNAL(sendMessage()), this, SLOT(send()));
    connect(mEditorWidget, SIGNAL(smsCharLimitReached()), 
        this, SLOT(handleSmsCharLimitReached()));
    connect(mEditorWidget, SIGNAL(replyStarted()), this, SIGNAL(replyStarted()));

    qreal spacing = HbDeviceProfile::profile(mConversationList).unitValue();
    
    mMainLayout = new QGraphicsLinearLayout(Qt::Vertical,this); 
    
    mMainLayout->setContentsMargins(CONTENT_MARGIN, CONTENT_MARGIN,
                                    CONTENT_MARGIN, CONTENT_MARGIN);   
    mMainLayout->setSpacing(spacing);

    mMainLayout->addItem(mConversationList);

    setLayout(mMainLayout);

    //Create VKB instance and listen to VKB open and close signals for resizing the view.
    mVkbHost = new HbStaticVkbHost(this);
	connect(mVkbHost, SIGNAL(keypadOpened()), this, SLOT(vkbOpened()));
	connect(mVkbHost, SIGNAL(keypadClosed()), this, SLOT(vkbClosed()));

    // Refresh view to show the header details
    refreshView();
}

//---------------------------------------------------------------
// MsgConversationView::addMenu
// @see header file
//---------------------------------------------------------------
void MsgConversationView::setupMenu()
{
    // Just create dummy menu.
    // Actual menu will be created in menuAboutToShow()
    HbMenu *mainMenu = this->menu();
    HbAction* clearConversation = mainMenu->addAction(QString());
    connect(mainMenu, SIGNAL(aboutToShow()), this, SLOT(menuAboutToShow()));
}

//---------------------------------------------------------------
// MsgConversationView::fetchMoreConversations
// @see header file
//---------------------------------------------------------------
void MsgConversationView::fetchMoreConversations()
{
    if (mViewReady && mModelPopulated) {
        ConversationsEngine::instance()->fetchMoreConversations();
        mViewReady = mModelPopulated = false;
    }
}

//---------------------------------------------------------------
// MsgConversationView::refreshView()
// @see header file
//---------------------------------------------------------------
void MsgConversationView::refreshView()
{
    // Hide editor in case of BT conversations.
    qint64 convId = ConversationsEngine::instance()->getCurrentConversationId();
    if (INVALID_CONVID != convId) {
        if (KBluetoothMsgsConversationId == convId) {
            mMainLayout->removeItem(mEditorWidget);
            mEditorWidget->hide();
            mEditorWidget->setParent(this);
        }
        else {
            mMainLayout->addItem(mEditorWidget);
            TRAP_IGNORE(mEditorWidget->setEncodingSettingsL());
            mEditorWidget->show();
        }
        mContactCardWidget->updateContents();
    }
}

//---------------------------------------------------------------
// MsgConversationView::scrollToBottom()
// @see header file
//---------------------------------------------------------------
void MsgConversationView::scrollToBottom()
{
    const int rowCnt = mMessageModel->rowCount();
    mConversationList->scrollTo(
            mMessageModel->index(rowCnt - 1, 0));
}

void MsgConversationView::onConversationViewEmpty()
{
    QVariantList param;
    param << MsgBaseView::CLV; // target view
    param << MsgBaseView::CV; // source view
    emit switchView(param);
}

//---------------------------------------------------------------
// MsgConversationView::longPressed
// @see header file
//---------------------------------------------------------------
void MsgConversationView::longPressed(HbAbstractViewItem* viewItem, const QPointF& point)
{ 
    showContextMenu(viewItem,point,HbPopup::TopLeftCorner);
}

//---------------------------------------------------------------
// MsgConversationView::setContextMenu
// @see header
//---------------------------------------------------------------
void MsgConversationView::setContextMenu(MsgConversationViewItem* item, HbMenu* contextMenu, int sendingState)
{
    addOpenItemToContextMenu(item , contextMenu,sendingState);
    addResendItemToContextMenu(item, contextMenu, sendingState);
    addForwardItemToContextMenu(item, contextMenu, sendingState);
    addDownloadItemToContextMenu(item, contextMenu);
    addSaveItemToContextMenu(item , contextMenu,sendingState);
    addDeleteItemToContextMenu(item, contextMenu, sendingState);
}


//---------------------------------------------------------------
// MsgEditorPrivate::addSaveItemToContextMenu
// @see header
//---------------------------------------------------------------
void MsgConversationView::addSaveItemToContextMenu(MsgConversationViewItem* item,
    HbMenu* contextMenu, int sendingState)
{
    Q_UNUSED(sendingState)

    int messageSubType = item->modelIndex().data(MessageSubType).toInt();
    int direction =  item->modelIndex().data(Direction).toInt();
    if ((messageSubType == ConvergedMessage::RingingTone) && 
        (direction == ConvergedMessage::Incoming)) {
        HbAction *contextItem = contextMenu->addAction(LOC_COMMON_SAVE);
        connect(contextItem, SIGNAL(triggered()), this, SLOT(saveRingingTone()));
    }
}

//---------------------------------------------------------------
// MsgEditorPrivate::addOpenItemToContextMenu
// @see header
//---------------------------------------------------------------

void MsgConversationView::addOpenItemToContextMenu(MsgConversationViewItem* item, HbMenu* contextMenu, int sendingState)
{
    int direction = item->modelIndex().data(Direction).toInt();
    int messageType = item->modelIndex().data(MessageType).toInt();
    int messageSubType = item->modelIndex().data(MessageSubType).toInt();
    
    if((messageSubType == ConvergedMessage::VCal) ||
        (messageSubType == ConvergedMessage::RingingTone) ||
        (messageType == ConvergedMessage::MmsNotification))
        {
        return;
        }
    if ((messageSubType == ConvergedMessage::VCard) &&
        (direction == ConvergedMessage::Incoming))
        {
        HbAction *contextItem = contextMenu->addAction(LOC_SAVE_TO_CONTACTS);
        connect(contextItem, SIGNAL(triggered()),this, SLOT(saveVCard()));
        return;
        }
    if( (sendingState == ConvergedMessage::SentState &&
         messageSubType != ConvergedMessage::VCard) ||
        (direction == ConvergedMessage::Incoming))
    {
        HbAction *contextItem = contextMenu->addAction(LOC_COMMON_OPEN);
        connect(contextItem, SIGNAL(triggered()),this, SLOT(openItem()));
    }
    
}

//---------------------------------------------------------------
// MsgEditorPrivate::addResendItemToContextMenu
// @see header
//---------------------------------------------------------------

void MsgConversationView::addResendItemToContextMenu(MsgConversationViewItem* item, HbMenu* contextMenu, int sendingState)
{
    Q_UNUSED(item)
    int direction = item->modelIndex().data(Direction).toInt();
    int messageSubType = item->modelIndex().data(MessageSubType).toInt();
    
    
    if( ((direction == ConvergedMessage::Outgoing) &&
        (messageSubType != ConvergedMessage::VCard))&&
        ((sendingState == ConvergedMessage::Resend ) ||
        
        (sendingState == ConvergedMessage::Failed )))
    {
        HbAction *contextItem = contextMenu->addAction(LOC_COMMON_SEND);
        connect(contextItem, SIGNAL(triggered()),this, SLOT(resendMessage()));
    }
}

//---------------------------------------------------------------
// MsgEditorPrivate::addForwardItemToContextMenu
// @see header
//---------------------------------------------------------------
void MsgConversationView::addForwardItemToContextMenu(MsgConversationViewItem* item, HbMenu* contextMenu, int sendingState)
{
    int messageType = item->modelIndex().data(MessageType).toInt();
    int direction = item->modelIndex().data(Direction).toInt();
    int messageSubType = item->modelIndex().data(MessageSubType).toInt();
    
    if( (messageType == ConvergedMessage::BT) ||
        (messageType == ConvergedMessage::MmsNotification) ||
        (messageSubType == ConvergedMessage::Provisioning ) ||
        (messageSubType == ConvergedMessage::RingingTone) ||
        (messageSubType == ConvergedMessage::VCal))
    {
        return;
    }
    
    qint32 messageId = item->modelIndex().data(ConvergedMsgId).toInt();
    qint32 messageProperty = item->modelIndex().data(MessageProperty).toInt();
    
    bool canForwardMessage  = true;
    if (messageType == ConvergedMessage::Mms){
        canForwardMessage = (messageProperty & EPreviewForward)? true:false;
    }            

    if( ((sendingState == ConvergedMessage::SentState) ||
        (sendingState == ConvergedMessage::Resend) ||
        (sendingState == ConvergedMessage::Failed) ||
        (sendingState == ConvergedMessage::Suspended )||
        (direction == ConvergedMessage::Incoming) ) &&
        canForwardMessage)
    {
        HbAction *contextItem = contextMenu->addAction(LOC_COMMON_FORWARD);
        connect(contextItem, SIGNAL(triggered()),this, SLOT(forwardMessage()));
    }

}

//---------------------------------------------------------------
// MsgEditorPrivate::addDeleteItemToContextMenu
// @see header
//---------------------------------------------------------------
void MsgConversationView::addDeleteItemToContextMenu(MsgConversationViewItem* item, HbMenu* contextMenu, int sendingState)
{
    int direction = item->modelIndex().data(Direction).toInt();
    int messageType = item->modelIndex().data(MessageType).toInt();
    int notificationState = item->modelIndex().data(NotificationStatus).toInt();
    
    if( (messageType == ConvergedMessage::MmsNotification) && 
        ((notificationState == ConvergedMessage::NotifNull) || 
         (notificationState ==  ConvergedMessage::NotifRetrieving) ||
         (notificationState == ConvergedMessage::NotifWaiting)))
    {
        return;
    }
    
    if( (sendingState == ConvergedMessage::SentState) ||
        (sendingState == ConvergedMessage::Resend) ||
        (sendingState == ConvergedMessage::Suspended) ||
        (sendingState == ConvergedMessage::Failed) ||
        (direction == ConvergedMessage::Incoming))
    {
        HbAction *contextItem = contextMenu->addAction(LOC_COMMON_DELETE);
        connect(contextItem, SIGNAL(triggered()),this, SLOT(deleteItem()));
    }  
}

//---------------------------------------------------------------
// MsgEditorPrivate::addDownloadItemToContextMenu
// @see header
//---------------------------------------------------------------
void MsgConversationView::addDownloadItemToContextMenu(MsgConversationViewItem* item, HbMenu* contextMenu)
{
    int notificationState = item->modelIndex().data(NotificationStatus).toInt();
    int messageType = item->modelIndex().data(MessageType).toInt();
    qint32 messageId = item->modelIndex().data(ConvergedMsgId).toLongLong();
        
    if( messageType == ConvergedMessage::MmsNotification &&
        ConversationsEngine::instance()->downloadOperationSupported(messageId))
    {            
        HbAction *contextItem = contextMenu->addAction(LOC_COMMON_DOWNLOAD);
        connect(contextItem, SIGNAL(triggered()),this, SLOT(downloadMessage()));
    }
}
//---------------------------------------------------------------
// MsgEditorPrivate::send
// @see header
//---------------------------------------------------------------
void MsgConversationView::send()
{
    activateInputBlocker();
    ConvergedMessageAddressList addresses;
    addresses = mContactCardWidget->address();
    int sendResult = KErrNone;

    // Populate the ConvergedMessage.
    if (!addresses.isEmpty())
    {
        ConvergedMessage msg;
        populateForSending(msg);
        msg.addToRecipients(addresses);

        // Send
        sendResult = mSendUtil->send(msg);
                   
        if( sendResult == KErrNone)
        {
            mEditorWidget->clear();
        }
    }
    deactivateInputBlocker();

    // make sure virtual keyboard is closed
    QInputContext *ic = qApp->inputContext();
    if (ic) {
    QEvent *closeEvent = new QEvent(QEvent::CloseSoftwareInputPanel);
    ic->filterEvent(closeEvent);
    delete closeEvent;
    }

    if( sendResult == KErrNotFound)
    {
    HbMessageBox::question(LOC_DIALOG_SMS_SETTINGS_INCOMPLETE, this,
                           SLOT(onDialogSettingsLaunch(HbAction*)), 
                           HbMessageBox::Ok | HbMessageBox::Cancel);
    }
}

//---------------------------------------------------------------
// MsgConversationView::contactsFetchedForVCards
// @see header file
//---------------------------------------------------------------
void MsgConversationView::contactsFetchedForVCards(const QVariant& value)
{
    // get received contact-list and launch unieditor
    CntServicesContactList contactList = 
            qVariantValue<CntServicesContactList>(value);
    int cntCount = contactList.count();
    if(cntCount > 0)
    {
        QVariantList params;
        params << MsgBaseView::ADD_VCARD;
        params << value;
        launchUniEditor(params);        
    }
}

//---------------------------------------------------------------
// MsgConversationView::fetchContacts
// @see header file
//---------------------------------------------------------------
void MsgConversationView::fetchContacts()
{
    HbAction* action = qobject_cast<HbAction*>(sender());

    if(!action)
        return;

    QList<QVariant> args;
    QString serviceName("com.nokia.services.phonebookservices");
    QString operation("fetch(QString,QString,QString)");
    XQAiwRequest* request;
    XQApplicationManager appManager;
    request = appManager.create(serviceName, "Fetch", operation, true); //embedded
    if ( request == NULL )
    {
        return;       
    }

    int mode = action->data().toInt();
    
    if( VCARD_INSERTION_MODE == mode) //vcard-insert mode
    {
        connect(request, SIGNAL(requestOk(const QVariant&)),
            this, SLOT(contactsFetchedForVCards(const QVariant&)));      
    }
    else  //contact-insert mode
    {
        connect(request, SIGNAL(requestOk(const QVariant&)),
            this, SLOT(contactsFetched(const QVariant&)));
    }
    connect (request, SIGNAL(requestError(int,const QString&)), 
        this, SLOT(serviceRequestError(int,const QString&)));

    args << QString(tr("Phonebook")); 
    args << KCntActionAll;
    args << KCntFilterDisplayAll;

    request->setArguments(args);
    request->send();
    delete request;
}
//---------------------------------------------------------------
// MsgConversationView::fetchImages
// @see header file
//---------------------------------------------------------------
void MsgConversationView::fetchImages()
{
    QString service("photos");
    QString interface("com.nokia.symbian.IImageFetch");
    QString operation("fetch()");
    XQAiwRequest* request = NULL;
    XQApplicationManager appManager;
    request = appManager.create(service,interface, operation, true); // embedded
    request->setSynchronous(true); // synchronous
    if(!request)
    {
        QDEBUG_WRITE("AIW-ERROR: NULL request");
        return;
    }

    connect(request, SIGNAL(requestOk(const QVariant&)),
        this, SLOT(imagesFetched(const QVariant&)));
    connect(request, SIGNAL(requestError(int,const QString&)),
        this, SLOT(serviceRequestError(int,const QString&)));

    // Make the request
    if (!request->send())
    {
        QDEBUG_WRITE_FORMAT("AIW-ERROR: Request Send failed:" , request->lastError());
    }  
    delete request;
}

//---------------------------------------------------------------
// MsgConversationView::fetchAudio
// @see header file
//---------------------------------------------------------------
void MsgConversationView::fetchAudio()
{
    // Launch Audio fetcher dialog
    if (!mDialog)
    {
        mDialog = new MsgAudioFetcherDialog();
        bool b = connect(mDialog,
                SIGNAL(audioSelected(QString&)),
                this,
                SLOT(onAudioSelected(QString&)));
    }

    //show the dialog
    mDialog->show();    
}

//---------------------------------------------------------------
// MsgConversationView::contactsFetched
// @see header file
//---------------------------------------------------------------
void MsgConversationView::contactsFetched(const QVariant& value)
{
    //switch to editor.
    QVariantList params;
    params << MsgBaseView::ADD_RECIPIENTS;
    params << value;
    launchUniEditor(params);
}

//---------------------------------------------------------------
// MsgConversationView::imagesFetched()
// @see header file
//---------------------------------------------------------------
void MsgConversationView::imagesFetched(const QVariant& result )
{
    if(result.canConvert<QStringList>())
    {
        QStringList fileList = result.value<QStringList>();
        if ( fileList.size()>0 )
        {
            QString filepath(QDir::toNativeSeparators(fileList.at(0)));
            QVariantList params;
            params << MsgBaseView::ADD_PHOTO;
            params << filepath;
            launchUniEditor(params);
        }
    }
}

//---------------------------------------------------------------
// MsgConversationView::addSubject()
// @see header file
//---------------------------------------------------------------
void MsgConversationView::addSubject()
{

    QString filepath;
    QVariantList params;
    params << MsgBaseView::ADD_SUBJECT;
    launchUniEditor(params);
}

//---------------------------------------------------------------
// MsgConversationView::forwardMessage()
// Forwards the message
//---------------------------------------------------------------
void MsgConversationView::forwardMessage()
{
    QModelIndex index = mConversationList->currentIndex();
    //messageId & messageType to be forwarded
    qint32 messageId = index.data(ConvergedMsgId).toLongLong();
    int messageType = index.data(MessageType).toInt();
 
    //Mark the message to read before forwarding.
    if(index.data(UnReadStatus).toBool())
    {
        QList<int> msgIdList;
        msgIdList.append(messageId);
        ConversationsEngine::instance()->markMessagesRead(msgIdList);
    }
    // populate params and launch editor 
    QVariantList params;
    params << MsgBaseView::FORWARD_MSG;
    params << messageId;
    params << messageType;
    launchUniEditor(params);
}

//---------------------------------------------------------------
// MsgConversationView::resendMessage()
// Resends the message in the failed messages case
//---------------------------------------------------------------
void MsgConversationView::resendMessage()
{
    QModelIndex index = mConversationList->currentIndex();
    if(index.isValid())
    {
        qint32 messageId = index.data(ConvergedMsgId).toLongLong();    
        if(!(ConversationsEngine::instance()->resendMessage(messageId)))
        {
            HbMessageBox::warning(LOC_MSG_SEND_FAILED, 0, 0, HbMessageBox::Ok);
        }
    }
    
}

//---------------------------------------------------------------
// MsgConversationView::downloadMessage()
// @see header
//---------------------------------------------------------------
void MsgConversationView::downloadMessage()
{
    QModelIndex index = mConversationList->currentIndex();
    if(index.isValid())
    {
        qint32 messageId = index.data(ConvergedMsgId).toLongLong();
        if(ConversationsEngine::instance()->downloadMessage(messageId)!=KErrNone)
        {
            HbMessageBox::warning(LOC_MMS_RETRIEVAL_FAILED, 0, 0, HbMessageBox::Ok);
        }
    }
    
}

//---------------------------------------------------------------
// MsgConversationView::deleteItem()
// Deletes the message
//---------------------------------------------------------------
void MsgConversationView::deleteItem()
{
    QString str = LOC_DELETE_MESSAGE;

    QModelIndex index = mConversationList->currentIndex();
    if(index.isValid())
    {
        int messageType = index.data(MessageType).toInt();        
        int direction = index.data(Direction).toInt();

        if ( direction == ConvergedMessage::Outgoing &&
            messageType == ConvergedMessage::Mms )
        {
            qint32 messageId = index.data(ConvergedMsgId).toLongLong();

            if(isSharedMessage(messageId))
            {
                str = LOC_DELETE_SHARED_MESSAGE;  
            }
        }
    }


    HbMessageBox::question(str,this,SLOT(onDialogdeleteMsg(HbAction*)),
                           HbMessageBox::Delete | HbMessageBox::Cancel);
}

//---------------------------------------------------------------
// MsgConversationView::openItem()
// Opens the message
//---------------------------------------------------------------
void MsgConversationView::openItem()
{
    QModelIndex index = mConversationList->currentIndex();
    openItem(index);
}

//---------------------------------------------------------------
// MsgConversationView::clearEditors()
// @See header
//---------------------------------------------------------------
void MsgConversationView::clearEditors()
{
    mEditorWidget->clear();
    mConversationList->setModel(NULL);
    mContactCardWidget->clearContent();
}

//---------------------------------------------------------------
// MsgConversationView::saveContentToDrafts()
// @See header
//---------------------------------------------------------------
int MsgConversationView::saveContentToDrafts()
{
    int msgId = INVALID_MSGID;
    if(!mEditorWidget->content().isEmpty())
    {
        activateInputBlocker();
        ConvergedMessageAddressList addresses;
        addresses = mContactCardWidget->address();

        // Populate the ConvergedMessage.
        if (!addresses.isEmpty())
        {
            ConvergedMessage msg;
            populateForSending(msg);
            msg.addToRecipients(addresses);
            
            // save to drafts
            msgId = mSendUtil->saveToDrafts(msg);
                     
            mEditorWidget->clear();
        }
        deactivateInputBlocker();
    }
    return msgId;
}

//---------------------------------------------------------------
//MsgConversationView::populateForSending()
//@see header
//---------------------------------------------------------------
void MsgConversationView::populateForSending(ConvergedMessage &message)
{
    message.setMessageType(ConvergedMessage::Sms);
    message.setBodyText(mEditorWidget->content());
    message.setDirection(ConvergedMessage::Outgoing);
    QDateTime time = QDateTime::currentDateTime();
    message.setTimeStamp(time.toTime_t());
}

//---------------------------------------------------------------
//MsgConversationView::launchBtDisplayService()
//@see header
//---------------------------------------------------------------
void MsgConversationView::launchBtDisplayService(const QModelIndex & index)
{
    qint32 messageId = index.data(ConvergedMsgId).toLongLong();

    QList<QVariant> args;
    QString serviceName("com.nokia.services.btmsgdispservices");
    QString operation("displaymsg(int)");
    XQAiwRequest* request;
    XQApplicationManager appManager;
    request = appManager.create(serviceName, "displaymsg", operation, false); // embedded
    
    if ( request == NULL )
        {
        return;
        }

    args << QVariant(messageId);
    request->setSynchronous(true);
    request->setEmbedded(true);
    request->setArguments(args);
    request->send();
    delete request;    
}

//---------------------------------------------------------------
// MsgConversationView::menuAboutToShow()
// @See header
//---------------------------------------------------------------
void MsgConversationView::menuAboutToShow()
{
    // Clear all the previously added actions.
    HbMenu *mainMenu = this->menu();
    mainMenu->clearActions();

    // Message type specific menu items
    QModelIndex index = ConversationsEngine::instance()->getConversationsModel()->index(0, 0);
    if (ConvergedMessage::BT != index.data(MessageType).toInt())
    {
        // Attach sub-menu
        HbMenu *attachSubMenu = mainMenu->addMenu(LOC_ATTACH);
        
        attachSubMenu->addAction(LOC_PHOTO,this, SLOT(fetchImages()));
        attachSubMenu->addAction(LOC_SOUND,this, SLOT(fetchAudio()));
		
        HbAction* addVCard = attachSubMenu->addAction(LOC_VCARD);
        addVCard->setData(VCARD_INSERTION_MODE);        
        connect(addVCard, SIGNAL(triggered()),this,SLOT(fetchContacts()));

        HbAction *addRecipients = mainMenu->addAction(LOC_ADD_RECIPIENTS);
        addRecipients->setData(CONTACT_INSERTION_MODE);        
        connect(addRecipients, SIGNAL(triggered()), this, SLOT(fetchContacts()));

        mainMenu->addAction(LOC_ADD_SUBJECT,this, SLOT(addSubject()));
    }
}

//---------------------------------------------------------------
//MsgConversationView::openItem
//@see header
//---------------------------------------------------------------
void MsgConversationView::openItem(const QModelIndex & index)
{
    // Return if invalid index.
    if (!index.isValid())
    {
        return;
    }
    
    int messageType = index.data(MessageType).toInt();
    int messageSubType = index.data(MessageSubType).toInt();
    int messageId = index.data(ConvergedMsgId).toInt();
    
    if (ConvergedMessage::BioMsg == messageType)
    {
        if (ConvergedMessage::RingingTone == messageSubType)
        {
            HbMessageBox::question(LOC_DIALOG_SAVE_RINGTONE, this,
                                   SLOT(onDialogSaveTone(HbAction*)), 
                                   HbMessageBox::Save | HbMessageBox::Cancel);
            return;
        }
        else if(ConvergedMessage::Provisioning == messageSubType)
        {
            int messageId = index.data(ConvergedMsgId).toInt();
            handleProvisoningMsg(messageId);
            QList<int> msgIdList;
            if(index.data(UnReadStatus).toInt())
            {
                msgIdList.clear();
                msgIdList << messageId;
                ConversationsEngine::instance()->markMessagesRead(msgIdList);
            }
            return;
        }
        else if(ConvergedMessage::VCard == messageSubType)
        {
            handleShortTap();
            return;
        }
        else if(ConvergedMessage::VCal == messageSubType)
        {
            return;
        }
    }
    else if (ConvergedMessage::BT == messageType)
    {
        launchBtDisplayService(index);
        QList<int> msgIdList;
        if(index.data(UnReadStatus).toInt())
            {
            msgIdList.clear();
            msgIdList << messageId;
            ConversationsEngine::instance()->markMessagesRead(msgIdList);
            }
        return;
    }
    else if(ConvergedMessage::MmsNotification == messageType)
    {
        qint32 messageId = index.data(ConvergedMsgId).toLongLong();
        if(!ConversationsEngine::instance()->downloadOperationSupported(messageId))           
        {
           int notificationState = index.data(NotificationStatus).toInt();
           if( notificationState == ConvergedMessage::NotifExpired)
           {
               deleteItem();
           }
           return;
        }
        else
        {
            //TODO: use logical str name
            HbMessageBox::question("Download Message?",this,
                                   SLOT(onDialogDownLoadMsg(HbAction*)),
                                   HbMessageBox::Yes | HbMessageBox::Cancel);
            return;
        }
    }

    int direction = index.data(Direction).toInt();

    if (direction == ConvergedMessage::Outgoing && ConvergedMessage::Sms == messageType
        && ConversationsEngine::instance()->getMsgSubType(messageId)== ConvergedMessage::NokiaService)
    {
        return;
    }
    
    // check whether message is in sending progress, then donot launch viewer.
    int location = index.data(MessageLocation).toInt();
    int sendingState = index.data(SendingState).toInt();
    
    // For suspended message both short tap and long tap needs to show the same
    // context menu.....
    if(direction == ConvergedMessage::Outgoing 
        	&&sendingState == ConvergedMessage::Suspended )
    {
        handleShortTap();
        return;
    }
    
    //If message is in any other state other than 'Sent'
    //do not open the message
    if(direction == ConvergedMessage::Outgoing 
            && sendingState != ConvergedMessage::SentState )
           
    {
        return;
    }

   
    
    // contact Id
    qint32 contactId = index.data(ContactId).toLongLong();  
	    
    //if message unread, mark as read before opening view
    QList<int> msgIdList;
    if(index.data(UnReadStatus).toInt())
    {
        msgIdList.clear();
        msgIdList << messageId;
        ConversationsEngine::instance()->markMessagesRead(msgIdList);
    }

    //switch view
    QVariantList param;
    param << MsgBaseView::UNIVIEWER;  // target view
    param << MsgBaseView::CV; // source view

    param << contactId;
    param << messageId;
    param << mMessageModel->rowCount();
    emit switchView(param);
}

//---------------------------------------------------------------
// MsgConversationView::launchUniEditor
// @see header file
//---------------------------------------------------------------
void MsgConversationView::launchUniEditor(const QVariantList& data)
{
    // param list for switching to editor view
    QVariantList params;
    QByteArray dataArray;
    QDataStream messageStream
    (&dataArray, QIODevice::WriteOnly | QIODevice::Append);

    // first arg is always the editor operation
    int editorOperation = data.at(0).toInt();
    
    ConvergedMessage message;
    QVariant data2;
    if( editorOperation != MsgBaseView::FORWARD_MSG )
    {
        message.setBodyText(mEditorWidget->content());

        // add address from contact-card to to-field
        ConvergedMessageAddress address;
        address.setAlias(mContactCardWidget->address().at(0)->alias());
        address.setAddress(mContactCardWidget->address().at(0)->address());
        message.addToRecipient(address);

        if(editorOperation == MsgBaseView::ADD_PHOTO ||
           editorOperation == MsgBaseView::ADD_AUDIO ||
           editorOperation == MsgBaseView::ADD_VIDEO )
        {
            // filepath is sent in cases like ADD_PHOTO, ADD_AUDIO etc.
            QString filepath;
            filepath = data.at(1).toString();
            if(!filepath.isEmpty())
            {
                ConvergedMessageAttachment* attachment = 
                        new ConvergedMessageAttachment(filepath);
                ConvergedMessageAttachmentList attachmentList;
                attachmentList.append(attachment);
                message.addAttachments(attachmentList);
            }            
        }
        else if(editorOperation == MsgBaseView::ADD_VCARD)
        {
            // filepath is not sent in cases like VCards & recipients
            // instead, we will get a list of contacts. Pass it as it is.
            data2 = data.at(1);
        }
        else if(editorOperation == MsgBaseView::ADD_RECIPIENTS)
        {
            ConvergedMessageAddressList addresses;
            CntServicesContactList contactList = 
                    qVariantValue<CntServicesContactList>(data.at(1));
            // now add fetched contacts from contact selection dialog
            for(int i = 0; i < contactList.count(); i++ )
            {
                ConvergedMessageAddress* address = new ConvergedMessageAddress;
                address->setAlias(contactList[i].mDisplayName);
                if(!contactList[i].mPhoneNumber.isEmpty())
                {
                    address->setAddress(contactList[i].mPhoneNumber);
                }
                else
                {
                    address->setAddress(contactList[i].mEmailAddress);
                }
                addresses.append(address);
            }
            message.addToRecipients(addresses);
        }
    }
    else
    {
        qint32 msgId = (qint32)data.at(1).toInt();
        int msgType = data.at(2).toInt();
        ConvergedMessageId id(msgId);
        message.setMessageId(id);
        message.setMessageType((ConvergedMessage::MessageType)msgType);
    }

    message.serialize(messageStream);
    params << MsgBaseView::UNIEDITOR;
    params << MsgBaseView::CV;
    params << dataArray;
    params << editorOperation;
    if(!data2.isNull())
        params << data2;

    clearEditors();
    emit switchView(params);
}

//---------------------------------------------------------------
// MsgConversationView::populateConversationsView
// @see header file
//---------------------------------------------------------------
void MsgConversationView::populateConversationsView()
{    
    mModelPopulated = true;
    mConversationList->setModel(mMessageModel);
    
    refreshView();
    scrollToBottom();
    fetchMoreConversations();
}

//---------------------------------------------------------------
// MsgConversationView::saveRingingTone
// @see header file
//---------------------------------------------------------------
void MsgConversationView::saveRingingTone()
{
    QModelIndex index = mConversationList->currentIndex();
    int messageId = index.data(ConvergedMsgId).toInt();

    UniDataModelLoader* pluginLoader = new UniDataModelLoader();
    UniDataModelPluginInterface* pluginInterface = 
    pluginLoader->getDataModelPlugin(ConvergedMessage::BioMsg);
    pluginInterface->setMessageId(messageId);
    UniMessageInfoList attachments = pluginInterface->attachmentList();
    if(attachments.count() > 0)
    {
        QString attachmentPath = attachments[0]->path();

        RingBc* ringBc = new RingBc();

        ringBc->saveTone(attachmentPath);

        // clear attachement list : its allocated at data model
        while(!attachments.isEmpty())
        {
            delete attachments.takeFirst();
        }

        delete ringBc;
    }
    delete pluginLoader;
}

//---------------------------------------------------------------
// MsgConversationView::handleSmsCharLimitReached
// @see header file
//---------------------------------------------------------------
void MsgConversationView::handleSmsCharLimitReached()   
{
    QString filepath;
    QVariantList params;
    params << MsgBaseView::ADD_OTHERS;
    launchUniEditor(params);   
}

//---------------------------------------------------------------
// MsgConversationView::vkbOpened
// @see header file
//---------------------------------------------------------------
void MsgConversationView::vkbOpened()
{
    emit vkbOpened(true);
    
    scrollToBottom();
    
    QRectF appRect = mVkbHost->applicationArea();    
    qreal spacing = 0.0;
    qreal cardHeight = 0.0;
    if(mContactCardWidget->isVisible())
        {
        cardHeight = mContactCardWidget->rect().height();
        spacing = HbDeviceProfile::profile(this).unitValue();
        }
    
    this->setMaximumHeight(appRect.height()- cardHeight - spacing);
    mConversationList->adjustSize();
    
    disconnect(mVkbHost,SIGNAL(keypadOpened()),this,SLOT(vkbOpened()));

}
	  
//---------------------------------------------------------------
// MsgConversationView::vkbClosed
// @see header file
//---------------------------------------------------------------
void MsgConversationView::vkbClosed()
{
    emit vkbOpened(false);
    
    scrollToBottom();
    
    this->setMaximumHeight(-1);
    mConversationList->adjustSize();    
    
    connect(mVkbHost,SIGNAL(keypadOpened()),this,SLOT(vkbOpened()));
}

//---------------------------------------------------------------
// MsgConversationView::serviceRequestError
// @see header file
//---------------------------------------------------------------
void MsgConversationView::serviceRequestError(int errorCode, const QString& errorMessage)
{
    QDEBUG_WRITE_FORMAT(errorMessage,errorCode);
}

//---------------------------------------------------------------
// MsgConversationView::activateInputBlocker
// @see header file
//---------------------------------------------------------------
void MsgConversationView::activateInputBlocker()
{
    mainWindow()->setInteractive(false);
}

//---------------------------------------------------------------
// MsgConversationView::deactivateInputBlocker
// @see header file
//---------------------------------------------------------------
void MsgConversationView::deactivateInputBlocker()
{
    mainWindow()->setInteractive(true);
}

//---------------------------------------------------------------
// MsgConversationView::handleProvisoningMsg
// @see header file
//---------------------------------------------------------------
void MsgConversationView::handleProvisoningMsg(int msgId)
	{
		QString messageId;
    messageId.setNum(msgId);

    XQApplicationManager* aiwMgr = new XQApplicationManager();

    XQAiwRequest* request = aiwMgr->create("com.nokia.services.MDM", 
            "Provisioning",
            "ProcessMessage(QString)", true); // embedded

    if (request) {
    QList<QVariant> args;
    args << QVariant(messageId);
    request->setArguments(args);

    // Send the request
    bool res = request->send();

    // Cleanup
    delete request;
    }

    delete aiwMgr;
	}

//---------------------------------------------------------------
// MsgConversationView::onDialogSettingsLaunch
// @see header file
//---------------------------------------------------------------
void MsgConversationView::onDialogSettingsLaunch(HbAction* action)
{
    HbMessageBox *dlg = qobject_cast<HbMessageBox*> (sender());
    if (action == dlg->actions().at(0)) {
        //switch to settings view
         QVariantList param;
         param << MsgBaseView::MSGSETTINGS;
         param << MsgBaseView::CV;
         param << MsgSettingsView::SMSView;
         emit switchView(param);
    }

}

//---------------------------------------------------------------
// MsgConversationView::onDialogdeleteMsg
// @see header file
//---------------------------------------------------------------
void MsgConversationView::onDialogdeleteMsg(HbAction* action)
{
    HbMessageBox *dlg = qobject_cast<HbMessageBox*> (sender());
    if (action == dlg->actions().at(0)) {
        QModelIndex index = mConversationList->currentIndex();
        if (index.isValid()) {
            int count = mMessageModel->rowCount();
            //delete message
            qint32 messageId = index.data(ConvergedMsgId).toLongLong();
            if (messageId) {
                QList<int> msgIdList;
                msgIdList.append(messageId);
                ConversationsEngine::instance()->deleteMessages(msgIdList);
                //switch view               
                if (count == 1) {
                    QVariantList param;
                    param << MsgBaseView::CLV; // target view
                    param << MsgBaseView::CV; // source view
                    emit switchView(param);
                }
            }
        }

    }

}

//---------------------------------------------------------------
// MsgConversationView::onDialogDownLoadMsg
// @see header file
//--------------------------------------------------------------
void MsgConversationView::onDialogDownLoadMsg(HbAction* action)
{
    HbMessageBox *dlg = qobject_cast<HbMessageBox*> (sender());
    if (action == dlg->actions().at(0)) {
        downloadMessage();
    }
    
    //if message unread, mark as read now
    QModelIndex index = mConversationList->currentIndex();
    qint32 messageId = index.data(ConvergedMsgId).toLongLong();
    QList<int> msgIdList;
    if(index.data(UnReadStatus).toInt())
    {
        msgIdList.clear();
        msgIdList << messageId;
        ConversationsEngine::instance()->markMessagesRead(msgIdList);
    }
}

//---------------------------------------------------------------
// MsgConversationView::onDialogSaveTone
// @see header file
//--------------------------------------------------------------
void MsgConversationView::onDialogSaveTone(HbAction* action)
    {
    HbMessageBox *dlg = qobject_cast<HbMessageBox*> (sender());
    if (action == dlg->actions().at(0)) {
        saveRingingTone();
    }
}

//---------------------------------------------------------------
// MsgConversationView::onOrientationChanged
// @see header file
//---------------------------------------------------------------
void MsgConversationView::onOrientationChanged(Qt::Orientation newOrientation)
{
    Q_UNUSED(newOrientation)

    // On orientation change always make the preserved index(last visible item) to be visible
    if(mVisibleIndex.isValid())
    {
    mConversationList->scrollTo(mVisibleIndex, HbAbstractItemView::PositionAtBottom);
    mVisibleIndex = QModelIndex();
    }
}

//---------------------------------------------------------------
// MsgConversationView::onOrientationAboutToBeChanged
// @see header file
//---------------------------------------------------------------
void MsgConversationView::onOrientationAboutToBeChanged()
{
    // Preserve the model index of the last visible item to be scrolled on orientation change
    QList<HbAbstractViewItem *>items = mConversationList->visibleItems();
    if (items.count() > 0) {
        mVisibleIndex = items.last()->modelIndex();
    }
}

//---------------------------------------------------------------
// MsgConversationView::onViewReady
// @see header file
//---------------------------------------------------------------
void MsgConversationView::onViewReady()
{
    mViewReady = true;
    //Disconnect list View's signals, for avoiding execution of the default implementaion
    disconnect(mainWindow(), SIGNAL(aboutToChangeOrientation()), mConversationList, 0);
    disconnect(mainWindow(), SIGNAL(orientationChanged(Qt: rientation)), mConversationList, 0);
    
   fetchMoreConversations();
}

//---------------------------------------------------------------
// MsgConversationView::handleShortTap
// @see header file
//---------------------------------------------------------------
void MsgConversationView::handleShortTap()
{
    HbAbstractViewItem* item = mConversationList->currentViewItem();
    QRectF rc = item->rect();
    QPointF p = item->mapToScene(rc.center());
    
    showContextMenu(item,p,HbPopup::TopEdgeCenter);
}

//---------------------------------------------------------------
// MsgConversationView::handleShortTap
// @see header file
//---------------------------------------------------------------
void MsgConversationView::showContextMenu(HbAbstractViewItem* viewItem,const QPointF& point, int placement)
{    
    MsgConversationViewItem* item = qgraphicsitem_cast<MsgConversationViewItem *>(viewItem);

    // Show the item-specific menu
    if (this->isVisible())
    {
        //If message is in Sending state or is Scheduled to be sent later,
        //do not allow any operations on the message
        int sendingState = item->modelIndex().data(SendingState).toInt();
        
        if(sendingState == ConvergedMessage::Scheduled ||
            sendingState == ConvergedMessage::Sending ||
            sendingState == ConvergedMessage::Waiting)
            {
                return;
            }
        // Create new menu
        HbMenu* contextMenu = new HbMenu();
        contextMenu->setAttribute(Qt::WA_DeleteOnClose);
        contextMenu->setPreferredPos(point,HbPopup::Placement(placement));
        setContextMenu(item, contextMenu, sendingState);
        contextMenu->show();
    }
}

//---------------------------------------------------------------
// MsgConversationView::saveVCard
// @see header file
//---------------------------------------------------------------
void MsgConversationView::saveVCard()
{
    QModelIndex index = mConversationList->currentIndex();
    
    QString filepath = index.data(Attachments).toStringList().at(0);
    bool result = MsgContactsUtil::launchVCardViewer(filepath);
    if(result)
    {
        int messageId = index.data(ConvergedMsgId).toInt();
        QList<int> msgIdList;
        if(index.data(UnReadStatus).toInt())
        {
            msgIdList.clear();
            msgIdList << messageId;
            ConversationsEngine::instance()->markMessagesRead(msgIdList);
        }
    } 
}

//---------------------------------------------------------------
// MsgConversationView::isSharedMessage
// @see header file
//---------------------------------------------------------------
bool MsgConversationView::isSharedMessage(qint32 messageId)
{
    bool shared = false;

    UniDataModelLoader* pluginLoader = new UniDataModelLoader();
    
    UniDataModelPluginInterface* pluginInterface =
        pluginLoader->getDataModelPlugin(ConvergedMessage::Mms);
    
    CMsvSession* session = pluginInterface->session();

    TMsvEntry entry; 
    TMsvId service;
    session->GetEntry(messageId, service, entry);
    
    if(entry.MultipleRecipients())
    {
        shared = true;
    }
    delete pluginLoader;    

    return shared;
}

void MsgConversationView::onAudioSelected(QString& filePath)
{
    QVariantList params;
    QByteArray dataArray;
    QDataStream messageStream
    (&dataArray, QIODevice::WriteOnly | QIODevice::Append);

    ConvergedMessage message;
    message.setBodyText(mEditorWidget->content());
    // add address from contact-card to to-field
    ConvergedMessageAddress address;
    address.setAlias(mContactCardWidget->address().at(0)->alias());
    address.setAddress(mContactCardWidget->address().at(0)->address());
    message.addToRecipient(address);
    
    //add the attachment as selected from audio picker
    ConvergedMessageAttachment* attachment =
                new ConvergedMessageAttachment(filePath);
    ConvergedMessageAttachmentList attachmentList;
    attachmentList.append(attachment);
    message.addAttachments(attachmentList);
    message.serialize(messageStream);

    params << MsgBaseView::UNIEDITOR; // target view
    params << MsgBaseView::CV; // source view
    params << dataArray;
    params << MsgBaseView::ADD_AUDIO;
    emit switchView(params);
}

// EOF
