/*
* Copyright (c) 2006,2007 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:   Provides CUniEditorProcessImageOperation methods.
*
*/



// ========== INCLUDE FILES ================================

#include <BASCHED.H>
#include <icl/imagedata.h>

#include <centralrepository.h>

#include <messagingvariant.hrh>
#include <messaginginternalcrkeys.h> // for Central Repository keys

#include <MsgMediaResolver.h>
#include <MsgImageInfo.h>
#include <MmsConformance.h>
#include <MsgMimeTypes.h>

#include <msgunieditorimageprocessor.h>


#include <mmsvattachmentmanager.h>
#include <mmsvattachmentmanagersync.h>
#include <cmsvattachment.h>

#include <msgtextutils.h>

#include <msvids.h>
#include <MmsEngineDomainCRKeys.h>
#include <mmssettingsdefs.h>
#include <HbMessageBox>
#include <HbAction>
#include <mmsconst.h>

#include "msgmonitor.h"
#include "msgunieditorprocessimageoperation.h"

// ========== CONSTANTS ====================================

// Leave some space after compression so that text can be inserted to the 
// message.
const TInt KUniCompressionMargin = 10 * 1024; // 10 kB

//const TInt KUniMmsUploadImageWidth = 1600;
//const TInt KUniMmsUploadImageHeight = 1200;


_LIT8( KUniExtImageJpeg_8, ".jpg" );
_LIT8( KUniExtImageGif_8,  ".gif" );

_LIT(KTempFilePath,"c:\\temp\\unieditor\\");

#define LOC_LARGE_IMAGE_NOTE QString("The receiver may not support image this large. Continue?")


// ========== MEMBER FUNCTIONS =============================

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::NewL
//
// Factory method.
// ---------------------------------------------------------
//
CUniEditorProcessImageOperation* CUniEditorProcessImageOperation::NewL(
    MUniEditorProcessImageOperationObserver &aObserver)
    {
    CUniEditorProcessImageOperation* self = new ( ELeave ) 
                    CUniEditorProcessImageOperation(aObserver);
            
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    
    return self;
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::CUniEditorProcessImageOperation.
// ---------------------------------------------------------
//
CUniEditorProcessImageOperation::CUniEditorProcessImageOperation(
    MUniEditorProcessImageOperationObserver &aObserver) 
    :    CActive( EPriorityStandard ),
         iObserver(aObserver)
    {
            CActiveScheduler::Add( this );
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::ConstructL
//
// 2nd phase constructor.
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::ConstructL()
    {
    User::LeaveIfError(iFs.Connect());
    iFs.ShareProtected();            

    TInt featureBitmask( 0 );
    
    CRepository* repository = CRepository::NewL( KCRUidMuiuVariation );
    repository->Get( KMuiuMmsFeatures, featureBitmask );
    
    iExactImageScaling = ( featureBitmask & KMmsFeatureIdExactImageScaling );
    
    delete repository;
    
    // MMS Engine CR
    repository = CRepository::NewL( KCRUidMmsEngine );


    TInt temp = 0;
    if ( repository->Get( KMmsEngineImageWidth, temp ) == KErrNone )
    {
        iMaxImageWidth = temp;
    }
    temp = 0;
    if ( repository->Get( KMmsEngineImageHeight, temp ) == KErrNone )
    {
        iMaxImageHeight = temp;
    }

    // Just to make sure the size is at least "small"
    iMaxImageWidth = Max( KMmsUniImageSmallWidth, iMaxImageWidth );
    iMaxImageHeight = Max( KMmsUniImageSmallHeight, iMaxImageHeight );
    
    temp = 0;
    if ( repository->Get( KMmsEngineCreationMode, temp ) == KErrNone )
    {
        iMmsCreationMode = temp;
    }
    
    delete repository;

    iMaxMmsSize = MsgMonitor::maxMmsSize();
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::~CUniEditorProcessImageOperation
// ---------------------------------------------------------
//
CUniEditorProcessImageOperation::~CUniEditorProcessImageOperation()
    {
    Cancel();    
    delete iNewImageInfo;
    iNewImageInfo = NULL;
    iNewImageFile.Close();
    delete iImageProcessor;

    //Since iFs doesnot have recursive dir deletion use file manager
    TRAP_IGNORE(
        CFileMan *fm = CFileMan::NewL(iFs);
        fm->RmDir(KTempFilePath);
        delete fm;
        );

    iFs.Close();
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::Process
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::Process( CMsgImageInfo* aImageInfo )
    {    
    iImageInfo = aImageInfo;
    iOperationState = EUniProcessImgCheck;
    
    CompleteSelf( KErrNone );
    }



// ---------------------------------------------------------
// CUniEditorProcessImageOperation::RunL
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::RunL()
    {
    if ( iStatus.Int() != KErrNone )
        {        
        iOperationState = EUniProcessImgError;
        }
    TFileName newFileName;
    
    switch ( iOperationState )
        {
        case EUniProcessImgCheck:
            {
            DoStartCheck();
            break;
            }
        case EUniProcessImgProcess:
            {
            DoStartProcessL();
            break;
            }
        case EUniProcessImgResolve:
            {
            DoStartResolveL();
            break;
            }
        case EUniProcessImgReady:
            {
            if(iNewImageInfo) 
            {    
            newFileName.Append(iNewImageInfo->FullFilePath());
            }
            iObserver.EditorOperationEvent( EUniEditorProcessImageOperationComplete,newFileName );
            break;
            }
        case EUniProcessImgError:
            {
            DoErrorWithoutStateChange();            
            iObserver.EditorOperationEvent( EUniEditorProcessImageOperationError,newFileName );
            break;
            }
        default:
            {
            iObserver.EditorOperationEvent( EUniEditorProcessImageOperationError,newFileName );
            break;
            }
        }    
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DoCancelCleanup
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DoCancelCleanup()
    {
    if ( iImageProcessor )
        {
        iImageProcessor->Cancel();
        }
        
    DoErrorWithoutStateChange();
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DoStartCheck
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DoStartCheck()
    {
    if ( !CheckNeedToProcess() )
        {
        iOperationState = EUniProcessImgError;
        }
    else if ( iProcessMethod == EUniProcessImgMethodNone )
        {
        //Since resizing is not started 
        //we should ensure that original file is picked for insertion
        iOperationState = EUniProcessImgError; 
        }
    else
        {
        iOperationState = EUniProcessImgProcess;
        }
    checkLargeImage();
    }
  
// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DoStartProcessL
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DoStartProcessL()
    {
    CreateEmptyAttachmentL();
    
    if ( !iImageProcessor )
        {
        iImageProcessor = new( ELeave )CUniImageProcessor( this );
        }
        
    RFile sourceFile = OpenFileForReadingL();
    
    CleanupClosePushL( sourceFile );
    
    iImageProcessor->ProcessImageL( sourceFile,
                                    iNewImageFile,
                                    iScaleSize,
                                    iTargetType.Des8(),
                                    ETrue, // Always maintain aspect ratio
                                    iMaxMmsSize - KUniCompressionMargin );
    SetPending();
                   
    CleanupStack::PopAndDestroy(); // sourceFile;
    }
  
// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DoStartResolveL
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DoStartResolveL()
    {
    CMsgMediaResolver* mediaResolver = CMsgMediaResolver::NewL();
    mediaResolver->SetCharacterSetRecognition( EFalse );    
    CleanupStack::PushL(mediaResolver);
    
    //Delete the previous object if present
    delete iNewImageInfo;
    iNewImageInfo = NULL;
    iNewImageInfo = static_cast<CMsgImageInfo*>(mediaResolver->CreateMediaInfoL( iNewImageFile ) );
    
    mediaResolver->ParseInfoDetailsL( iNewImageInfo, iNewImageFile );

    iOperationState = EUniProcessImgReady;

    iNewImageFile.Close();
    
    CleanupStack::PopAndDestroy(mediaResolver);
    CompleteSelf( KErrNone );
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DoErrorWithoutStateChange
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DoErrorWithoutStateChange()
    {
    iNewImageFile.Close();
    delete iNewImageInfo;
    iNewImageInfo = NULL;
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::ImageProcessingReady
//
// Image Compressor callback implementation.
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::ImageProcessingReady( TSize aBitmapSize, 
    TInt aFileSize, 
    TBool aCompressed )
    {
    TInt err = iImageProcessor->Error();
    
    if ( err == KErrNone &&
         ( aBitmapSize.iWidth == 0 || aBitmapSize.iHeight == 0 ||
           ( aCompressed &&
            ( aFileSize == 0 ||
                    aFileSize > ( iMaxMmsSize - KUniCompressionMargin ) ) ) ) )
        {
        err = KErrGeneral;
        }
        
    switch ( err )
        {
        case KErrNone:
            {
            iOperationState = EUniProcessImgResolve;
            break;
            }
        case KErrNoMemory:
            {
            iOperationState = EUniProcessImgError;

            break;
            }
        case KErrDiskFull:
            {
            iOperationState = EUniProcessImgError;

            break;
            }
        case KErrNotFound:
            {
            iOperationState = EUniProcessImgError;

            break;
            }
        default:
            {
            iOperationState = EUniProcessImgError;
            break;
            }
        }
    
    if ( err == KErrCancel )
        {
        CompleteOperation( KErrCancel );
        }
    else
        {
        CompleteOperation( KErrNone );
        }
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::CheckNeedToProcess
// 
// Checks if scaling/converting/compression is needed.
// ---------------------------------------------------------
//
TBool CUniEditorProcessImageOperation::CheckNeedToProcess()
    {
    iProcessMethod = EUniProcessImgMethodNone;
    
    CMmsConformance* mmsConformance = NULL;
    TRAP_IGNORE(mmsConformance = CMmsConformance::NewL());
    TMmsConformance conformance;
    if(mmsConformance)
    {
        mmsConformance->CheckCharacterSet( EFalse );
        conformance = mmsConformance->MediaConformance( *iImageInfo );
    }
    
    if ( conformance.iCanAdapt == EFalse )
        {
        return EFalse;
        }
        
    TSize origSize = iImageInfo->Dimensions();
    
    if ( origSize.iWidth == 0 || origSize.iHeight == 0 )
        {    
        // Cannot get size -> corrupted
        return EFalse; // Abort
        }
    
    TSize scaleSize = ImageSendSize();
    TSize optimizedSize = origSize;
    
    while ( optimizedSize.iWidth > scaleSize.iWidth ||
            optimizedSize.iHeight > scaleSize.iHeight )
        {
        // Largest possible (1/2)^n size
        optimizedSize.iWidth >>= 1;
        optimizedSize.iHeight >>= 1;
        }
        
    if ( scaleSize.iWidth < origSize.iWidth ||
         scaleSize.iHeight < origSize.iHeight )
        {
        if ( !iExactImageScaling &&
            ( scaleSize.iWidth > KMmsUniImageSmallWidth ||
              scaleSize.iHeight > KMmsUniImageSmallHeight ) )
            {
            // Use optimized (1/2^n) size when scaling
            // to larger than "Small"
            scaleSize = optimizedSize;
            }
        // else -> scale to exact (non-optimized) size
        
        iProcessMethod |= EUniProcessImgMethodScale;
        }
    else
        {
        // Scaling not needed. Check possible conversion need.
        scaleSize = origSize;
        
        if ( conformance.iConfStatus & EMmsConfNokConversionNeeded )
            {
            // Conversion needed.
            iProcessMethod |= EUniProcessImgMethodConvert;
            }
        }

    if ( !( iProcessMethod & EUniProcessImgMethodScale ) &&
         ( iImageInfo->FileSize() + 
           MsgMonitor::messageSize() ) > iMaxMmsSize &&
         iImageInfo->MimeType().CompareF( KMsgMimeImageJpeg ) == 0 &&
         (MsgMonitor::messageSize()) < KUniCompressionMargin )
        {
        // Only compression needed as image is JPEG that is larger than can be fitted
        // into the message and scaling is not performed. Also current message size
        // is under comression margin.
        iProcessMethod |= EUniProcessImgMethodCompress;
        }
    
    largeImageQuery = EFalse;
    
    if ( iProcessMethod == EUniProcessImgMethodNone )
        {
        // Image won't be processed
        if ( ( origSize.iWidth > KImageRichWidth ||
               origSize.iHeight > KImageRichHeight ) &&
             ( iImageInfo->FileSize() + MsgMonitor::messageSize() ) < iMaxMmsSize )
            {
            // Original image width or height is "non-conformant" and original image would 
            // fit to into the message without any processing.
            largeImageQuery = ETrue;
            }
        }
    else
        {
        // Image will be processed
        if ( scaleSize.iWidth > KImageRichWidth ||
             scaleSize.iHeight > KImageRichHeight )
            {
            // Processed image is "non-conformant" after processing.
            largeImageQuery = ETrue;
            }
        }
 
    iScaleSize = scaleSize;
    return ETrue;
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::checkLargeImage
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::checkLargeImage()
{
    //Large image query     
    if( largeImageQuery && iMmsCreationMode == EMmsCreationModeWarning)
    {
        HbMessageBox::question(LOC_LARGE_IMAGE_NOTE, this,
            SLOT(onDialogLargeImage(HbAction*))); 
    }
    else
    {
        CompleteSelf(KErrNone);
    }
        
}
       

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::CreateEmptyAttachmentL
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::CreateEmptyAttachmentL()
    {

    // Get the file name from original full path name.
    TParsePtrC parser( iImageInfo->FullFilePath() );

    TFileName ext( parser.Ext() );
    iTargetType = iImageInfo->MimeType();

    if ( iTargetType.Des8().CompareF( KMsgMimeImagePng ) == 0 )
        {
        //png is non-conformant image type
        //->convert to jpeg
        iTargetType = TDataType( KMsgMimeImageJpeg );
        ext.Zero();
        ext.Copy( KUniExtImageJpeg_8 );
        }
    else if ( iTargetType.Des8().CompareF( KMsgMimeImageWbmp ) == 0 )
        {
        //no wbmp encoder
        //->convert to gif if scaling is needed
        iTargetType = TDataType( KMsgMimeImageGif );
        ext.Zero();
        ext.Copy( KUniExtImageGif_8 );
        }

    //Create dir if doesnot exist, if dir already exits error is returned 
    //which can be ignored
    iFs.MkDirAll(KTempFilePath);
    
    TFileName newFileName( KTempFilePath );
    newFileName.Append(parser.Name());
    newFileName.Append( ext );
    

    iNewImageFile.Replace(iFs,newFileName,EFileWrite | EFileShareAny);

    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::ImageSendSize
// ---------------------------------------------------------
//
TSize CUniEditorProcessImageOperation::ImageSendSize() const
    {
    TSize size;
    size.iWidth = iMaxImageWidth;
    size.iHeight = iMaxImageHeight;
    return size;
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::OpenFileForReadingL
// ---------------------------------------------------------
//
RFile CUniEditorProcessImageOperation::OpenFileForReadingL()
    {
    RFile sourceFile;
    TInt err = sourceFile.Open(
        iFs, iImageInfo->FullFilePath(), EFileRead | EFileShareAny );
    if ( err )
    {
        err = sourceFile.Open(
            iFs, iImageInfo->FullFilePath(), EFileRead | EFileShareReadersOnly );

        User::LeaveIfError( err );
    }
    return sourceFile;
    }
    
// ---------------------------------------------------------
// CUniEditorProcessImageOperation::RunError
// ---------------------------------------------------------
//
TInt CUniEditorProcessImageOperation::RunError( TInt aError )
    {
    delete iImageProcessor;
    iImageProcessor = NULL;    
    
    if ( aError == KLeaveExit )
        {
        return KLeaveExit;
        }
    else
        {
        CompleteSelf( aError );
        return KErrNone;
        }
    }
    
// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DetachImageInfo
// ---------------------------------------------------------
//
CMsgImageInfo* CUniEditorProcessImageOperation::DetachImageInfo()
    {
    // ownership transferred
    CMsgImageInfo* tempInfo = iNewImageInfo;
    iNewImageInfo = NULL;
    return tempInfo;
    }
    
// ---------------------------------------------------------
// CUniEditorProcessImageOperation::CompleteSelf
//
// Completes current step of state machine
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::CompleteSelf( TInt aError )
    {
    iStatus = KRequestPending;
    TRequestStatus* pStatus = &iStatus;
    SetActive();
    User::RequestComplete( pStatus, aError );
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::SetPending
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::SetPending()
    {
    if ( !IsActive() )
        {
        iStatus = KRequestPending;
        SetActive();
        }
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::CompleteOperation
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::CompleteOperation( TInt aError )
    {
    SetPending();
        
    TRequestStatus* pStatus = &iStatus;
    User::RequestComplete( pStatus, aError );
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DoCancel
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DoCancel()
    {
    DoCancelCleanup();
    
    
    if ( iStatus == KRequestPending )
        {
        CompleteOperation( KErrCancel );
        }
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::onDialogLargeImage
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::onDialogLargeImage(HbAction* action)
    {
    HbMessageBox *dlg = qobject_cast<HbMessageBox*> (sender());
    if (action == dlg->actions().at(1)) {
        iOperationState = EUniProcessImgError;
        }
    CompleteSelf(KErrNone);
    }

// End of file
