/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, 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 qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "cntthumbnailcreator.h"

#include <cntdb.h> 
#include <cntitem.h>

#include <cntdef.h>
#include <cntfield.h>
#include <cntfldst.h>
#include <imageconversion.h>
#include <gdi.h>
#include <bitmaptransforms.h>
#include <bautils.h>

#if QT_VERSION < QT_VERSION_CHECK(4, 6, 0)
#define QT_TRAP_THROWING QT_TRANSLATE_SYMBIAN_LEAVE_TO_EXCEPTION
#define QT_TRYCATCH_LEAVING QT_TRANSLATE_EXCEPTION_TO_SYMBIAN_LEAVE
#endif

const TSize KThumbnailSizeUninitialized(0, 0);

#ifdef _DEBUG
_LIT(KPanicCategory, "ThumbnailCreator");
enum TPanicReasons {
    KPanicUnitialized = 0
};
#endif

CntThumbnailCreator::CntThumbnailCreator() :
    CActive(EPriorityStandard),
    m_state(EStateInitialized),
    m_thumbnailSize(KThumbnailSizeUninitialized),
    m_thumbnailFieldFromTemplate(0),
    m_err(KErrNone)
{
    CActiveScheduler::Add(this);
}

CntThumbnailCreator::~CntThumbnailCreator()
{
    delete m_encoder;
    delete m_imageData;
    delete m_bitmapScaler;
    delete m_bitmap;
    delete m_decoder;
    delete m_activeSchedulerWait;
    if (m_rfs.Handle() != KNullHandle)
        m_rfs.Close();
}

/*!
 * After calling this function \a fieldList will contain thumbnail field (field
 * types KUidContactFieldVCardMapJPEG, KUidContactFieldPicture in this order)
 * with thumbnail data. If the file \a filename does not exist, no fields are
 * added to the field list. If the file \a filename is not a valid image file,
 * a leave will occur. \a maxSize is the maximum size of the thumbnail image
 * that is stored into contact database. If the image is bigger than \a maxSize
 * the generated thumbnail will be scaled down to \a maxSize.
 */
void CntThumbnailCreator::addThumbnailFieldL(QList<CContactItemField *> *fieldList, const TFileName &filename, const TSize& maxSize)
{
    Cancel();
    m_fieldList = fieldList;
    m_thumbnailSize = maxSize;

    // Lazy instantiation to save resources
    if (m_rfs.Handle() == KNullHandle)
        m_rfs.Connect();
    if(!m_activeSchedulerWait)
        m_activeSchedulerWait = new (ELeave) CActiveSchedulerWait;

    DecodeImageL(filename);

    // Synchronize asynchronous operations (wait loop is finished in RunL when
    // image operations are done, or in RunError if an error occurs)
    m_activeSchedulerWait->Start();
    User::LeaveIfError(m_err);
}

void CntThumbnailCreator::addThumbnailFieldL(QList<CContactItemField *> *fieldList, CFbsBitmap *bitmap, const TSize& maxSize)
{
    Cancel();
    m_fieldList = fieldList;
    m_thumbnailSize = maxSize;
    delete m_bitmap;
    m_bitmap = bitmap;

    // Lazy instantiation to save resources
    if (m_rfs.Handle() == KNullHandle)
        m_rfs.Connect();
    if(!m_activeSchedulerWait)
        m_activeSchedulerWait = new (ELeave) CActiveSchedulerWait;

    m_state = EStateEncodeImage;
    EncodeImageL();

    // Synchronize asynchronous operations (wait loop is finished in RunL when
    // image operations are done, or in RunError if an error occurs)
    m_activeSchedulerWait->Start();
    User::LeaveIfError(m_err);
}


void CntThumbnailCreator::RunL()
{
    __ASSERT_DEBUG(m_activeSchedulerWait, User::Panic(KPanicCategory, KPanicUnitialized));

    User::LeaveIfError(iStatus.Int());
    switch (m_state) {
    case EStateDecodeImage:
    {
        m_state = EStateScaleImage;
        ScaleImageL();
        break;
    }
    case EStateScaleImage:
    {
        m_state = EStateEncodeImage;
        EncodeImageL();
        break;
    }
    case EStateEncodeImage:
    {
        m_state = EStateFinal;
        CreateContactFieldL();
        break;
    }
    case EStateFinal:
    {
        // We are done with the image, back to the caller of convertL
        m_activeSchedulerWait->AsyncStop();
        break;
    }
    default:
        User::Leave(KErrCompletion);
    }
}

void CntThumbnailCreator::DoCancel()
{
    __ASSERT_DEBUG(m_activeSchedulerWait, User::Panic(KPanicCategory, KPanicUnitialized));

    m_err = KErrCancel;
    m_activeSchedulerWait->AsyncStop();
}

TInt CntThumbnailCreator::RunError(TInt aError)
{
    __ASSERT_DEBUG(m_activeSchedulerWait, User::Panic(KPanicCategory, KPanicUnitialized));

    m_err = aError;
    m_activeSchedulerWait->AsyncStop();
    return KErrNone;
}

void CntThumbnailCreator::DecodeImageL(const TFileName &filename)
{
    if (m_rfs.IsValidName(filename) && BaflUtils::FileExists(m_rfs, filename)) {
        m_state = EStateDecodeImage;

        // Create image decoder
        delete m_decoder;
        m_decoder = 0;
        m_decoder = CImageDecoder::FileNewL(m_rfs, filename);
        TFrameInfo frameInfo = m_decoder->FrameInfo();

        // Lazy instantiation
        if(!m_bitmap)
            m_bitmap = new (ELeave) CFbsBitmap;
        User::LeaveIfError(m_bitmap->Create(frameInfo.iOverallSizeInPixels, frameInfo.iFrameDisplayMode));

        m_decoder->Convert(&iStatus, *m_bitmap);
    } else {
        m_state = EStateFinal;

        // Ignore "file does not exist" error because the image has probably
        // been removed (leaves thumbnail field empty for the contact).
        TRequestStatus *status = &iStatus;
        User::RequestComplete(status, KErrNone);
    }
    SetActive();
}

void CntThumbnailCreator::ScaleImageL()
{
    __ASSERT_DEBUG(m_decoder, User::Panic(KPanicCategory, KPanicUnitialized));
    __ASSERT_DEBUG(m_thumbnailSize != KThumbnailSizeUninitialized, User::Panic(KPanicCategory, KPanicUnitialized));

    TFrameInfo frameInfo = m_decoder->FrameInfo();

    // Scale down if the image is too big
    if ( frameInfo.iOverallSizeInPixels.iWidth > m_thumbnailSize.iWidth
            || frameInfo.iOverallSizeInPixels.iHeight > m_thumbnailSize.iHeight) {
        // Lazy instantiation
        if(!m_bitmapScaler)
            m_bitmapScaler = CBitmapScaler::NewL();
        m_bitmapScaler->Scale(&iStatus, *m_bitmap, m_thumbnailSize);
    } else {
        // No need to scale, complete request immediately
        TRequestStatus *status = &iStatus;
        User::RequestComplete(status, KErrNone);
    }
    SetActive();
}

void CntThumbnailCreator::EncodeImageL()
{
    delete m_imageData;
    m_imageData = 0;
    delete m_encoder;
    m_encoder = 0;
    m_encoder = CImageEncoder::DataNewL(m_imageData, CImageEncoder::EOptionNone, KImageTypeJPGUid);
    m_encoder->Convert(&iStatus, *m_bitmap);
    SetActive();
}

void CntThumbnailCreator::CreateContactFieldL()
{
    __ASSERT_DEBUG(m_imageData, User::Panic(KPanicCategory, KPanicUnitialized));

    initializeThumbnailFieldL();
    CContactItemField *thumbnailField = CContactItemField::NewLC(*m_thumbnailFieldFromTemplate);

    thumbnailField->StoreStorage()->SetThingL(*m_imageData);
    QT_TRYCATCH_LEAVING(m_fieldList->append(thumbnailField));
    CleanupStack::Pop(thumbnailField);

    // Complete request to proceed to the final state
    TRequestStatus *status = &iStatus;
    User::RequestComplete(status, KErrNone);
    SetActive();
}

void CntThumbnailCreator::initializeThumbnailFieldL()
{
    // Assume the golden template is not changed run-time and fetch it only
    // when initializeThumbnailFieldL is called for the first time during the
    // life-time of the CntThumbnailCreator object (requires the instance to
    // live longer than one thumbnail create operation to be effective,
    // otherwise we would end up opening contact database and reading the
    // system template every time a thumbnail is stored for a contact).
    if(!m_thumbnailFieldFromTemplate) {
        CContactDatabase *contactDatabase = CContactDatabase::OpenL();
        CleanupStack::PushL(contactDatabase);
        CContactItem *goldenTemplate = contactDatabase->ReadContactLC(KGoldenTemplateId);
        const CContactItemFieldSet& cardFields = goldenTemplate->CardFields();

        // Check if thumbnail field type is KUidContactFieldPictureValue
        TInt pictureFieldIndex = cardFields.Find(KUidContactFieldPicture, KUidContactFieldVCardMapPHOTO);

        // Check if thumbnail field type is KUidContactFieldVCardMapJPEG
        if(pictureFieldIndex == KErrNotFound) {
            pictureFieldIndex = cardFields.Find(KUidContactFieldVCardMapJPEG, KUidContactFieldVCardMapPHOTO);
        }

        if(pictureFieldIndex == KErrNotFound) {
            // Either KUidContactFieldPictureValue or KUidContactFieldVCardMapJPEG
            // thumbnail field types should be in the template
            User::Leave(KErrNotFound);
        }

        m_thumbnailFieldFromTemplate = CContactItemField::NewL(cardFields[pictureFieldIndex]);
        CleanupStack::PopAndDestroy(goldenTemplate);
        CleanupStack::PopAndDestroy(contactDatabase);
    }
}
