/*
* 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 <apmstd.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 <uniimageprocessor.h>
#include <unimsventry.h>

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

#include <msgtextutils.h>

#include <mmssettingsdefs.h>

#include <mmsversion.h>

#include "UniClientMtm.h"
#include "UniEditorEnum.h"
#include "UniEditorDocument.h"
#include "UniEditorProcessImageOperation.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" );

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

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::NewL
//
// Factory method.
// ---------------------------------------------------------
//
CUniEditorProcessImageOperation* CUniEditorProcessImageOperation::NewL( 
        MUniEditorOperationObserver& aObserver,
        CUniEditorDocument& aDocument,
        RFs& aFs )
    {
    CUniEditorProcessImageOperation* self = new ( ELeave ) CUniEditorProcessImageOperation(
            aObserver,
            aDocument,
            aFs );
            
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    
    return self;
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::CUniEditorProcessImageOperation.
// ---------------------------------------------------------
//
CUniEditorProcessImageOperation::CUniEditorProcessImageOperation(
        MUniEditorOperationObserver& aObserver,
        CUniEditorDocument& aDocument,
        RFs& aFs ) :
    CUniEditorOperation( aObserver, aDocument, aFs, EUniEditorOperationProcessImage ),
    iNewAttaId( KMsvNullIndexEntryId )
    {
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::ConstructL
//
// 2nd phase constructor.
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::ConstructL()
    {
    BaseConstructL();

    TInt featureBitmask( 0 );
    
    CRepository* repository = CRepository::NewL( KCRUidMuiuVariation );
    repository->Get( KMuiuMmsFeatures, featureBitmask );
    
    iExactImageScaling = ( featureBitmask & KMmsFeatureIdExactImageScaling );
    
    delete repository;
    }

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

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::Process
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::Process( CMsgImageInfo* aImageInfo,
                                               TMsvAttachmentId aAttachmentId,
                                               TInt aMessageSize )
    {
    ResetErrors();
    
    iImageInfo = aImageInfo;
    iAttachmentId = aAttachmentId;
    iMessageSize = aMessageSize;
    iOperationState = EUniProcessImgCheck;
    
    CompleteSelf( KErrNone );
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DetachAttachmentId
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DetachAttachmentId( TMsvAttachmentId& aAttachmentId )
    {
    // iNewImageInfo may be already detached in DetachImageInfo
    aAttachmentId = iNewAttaId;
    iNewAttaId = KMsvNullIndexEntryId;
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::RunL
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::RunL()
    {
    PrintOperationAndState();
    if ( iStatus.Int() != KErrNone )
        {
        SetError( iStatus.Int() );
        iOperationState = EUniProcessImgError;
        }
    
    switch ( iOperationState )
        {
        case EUniProcessImgCheck:
            {
            DoStartCheck();
            break;
            }
        case EUniProcessImgProcess:
            {
            DoStartProcessL();
            break;
            }
        case EUniProcessImgResolve:
            {
            DoStartResolveL();
            break;
            }
        case EUniProcessImgReady:
            {
            iObserver.EditorOperationEvent( EUniEditorOperationProcessImage,
                                            EUniEditorOperationComplete );
            break;
            }
        case EUniProcessImgError:
            {
            DoErrorWithoutStateChange();
            iObserver.EditorOperationEvent( EUniEditorOperationProcessImage,
                                            EUniEditorOperationError );
            break;
            }
        default:
            {
            iObserver.EditorOperationEvent( EUniEditorOperationProcessImage,
                                            EUniEditorOperationError );
            break;
            }
        }    
    }

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

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DoStartCheck
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DoStartCheck()
    {
    if ( !CheckNeedToProcess() )
        {
        iOperationState = EUniProcessImgError;
        }
    else if ( iProcessMethod == EUniProcessImgMethodNone )
        {
        iOperationState = EUniProcessImgReady;
        }
    else
        {
        iOperationState = EUniProcessImgProcess;
        }
    CompleteSelf( KErrNone );
    }
  
// ---------------------------------------------------------
// 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
                                    iDocument.MaxMessageSize() - KUniCompressionMargin );
    SetPending();
                   
    CleanupStack::PopAndDestroy(); // sourceFile;
    }
  
// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DoStartResolveL
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DoStartResolveL()
    {
    iNewImageInfo = static_cast<CMsgImageInfo*>(iDocument.DataModel()->MediaResolver().CreateMediaInfoL( iNewImageFile ) );
    iDocument.DataModel()->MediaResolver().ParseInfoDetailsL( iNewImageInfo, iNewImageFile );

    iOperationState = EUniProcessImgReady;

    iNewImageFile.Close();
    
    __ASSERT_DEBUG( iEditStore, Panic( EUniNullPointer ) );
    
    iEditStore->CommitL();
    delete iEditStore;
    iEditStore = NULL;

    CompleteSelf( KErrNone );
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DoErrorWithoutStateChange
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::DoErrorWithoutStateChange()
    {
    iNewImageFile.Close();
    delete iNewImageInfo;
    iNewImageInfo = NULL;
    
    delete iEditStore;
    iEditStore = 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 > ( iDocument.MaxMessageSize() - KUniCompressionMargin ) ) ) ) )
        {
        err = KErrGeneral;
        }
        
    switch ( err )
        {
        case KErrNone:
            {
            iOperationState = EUniProcessImgResolve;
            if ( aCompressed )
                {
                SetError( EUniProcessImgCompressSuccessful );
                }
            break;
            }
        case KErrNoMemory:
            {
            iOperationState = EUniProcessImgError;
            SetError( EUniProcessImgOutOfMemory );
            break;
            }
        case KErrDiskFull:
            {
            iOperationState = EUniProcessImgError;
            SetError( EUniProcessImgOutOfDisk );
            break;
            }
        case KErrNotFound:
            {
            iOperationState = EUniProcessImgError;
            SetError( EUniProcessImgNotFound );
            break;
            }
        default:
            {
            iOperationState = EUniProcessImgError;
            if ( aCompressed )
                {
                SetError( EUniProcessImgCompressFailed );
                }
            else
                {
                SetError( EUniProcessImgScalingFailed );
                }
            break;
            }
        }
    
    if ( err == KErrCancel )
        {
        CompleteOperation( KErrCancel );
        }
    else
        {
        CompleteOperation( KErrNone );
        }
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::CheckNeedToProcess
// 
// Checks if scaling/converting/compression is needed.
// ---------------------------------------------------------
//
TBool CUniEditorProcessImageOperation::CheckNeedToProcess()
    {
    iProcessMethod = EUniProcessImgMethodNone;
    
    TMmsConformance conformance = 
        iDocument.DataModel()->MmsConformance().MediaConformance( *iImageInfo );
    
    if ( conformance.iCanAdapt == EFalse )
        {
        return ETrue;
        }
        
    TSize origSize = iImageInfo->Dimensions();
    
    if ( origSize.iWidth == 0 || origSize.iHeight == 0 )
        {    
        // Cannot get size -> corrupted
        SetError( EUniProcessImgCorrupted );
        return ETrue;
        }
    
    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() + iMessageSize ) > iDocument.MaxMessageSize() &&
         iImageInfo->MimeType().CompareF( KMsgMimeImageJpeg ) == 0 &&
         iMessageSize < 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;
        }
    
    TBool largeImageQuery = EFalse;
    TInt conformanceWidth = KImageRichWidth;
    TInt conformanceHeight = KImageRichHeight;
    
    if (iDocument.DataModel()->MmsConformance().ConformanceVersion()> KMmsVersion12)
        {
        conformanceWidth = KImageMegapixelWidth;
        conformanceHeight = KImageMegapixelHeight;    
        }
    
    if ( iProcessMethod == EUniProcessImgMethodNone )
        {
        // Image won't be processed
        if ( ( origSize.iWidth > conformanceWidth ||
               origSize.iHeight > conformanceHeight ) &&
             ( iImageInfo->FileSize() + iMessageSize ) < iDocument.MaxMessageSize() )
            {
            // 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 > conformanceWidth ||
             scaleSize.iHeight > conformanceHeight )
            {
            // Processed image is "non-conformant" after processing.
            largeImageQuery = ETrue;
            }
        }

    if ( largeImageQuery &&  iDocument.CreationMode() == EMmsCreationModeWarning )
        {
        // Observer will make the final decision whether it will show the query or not.
        if ( !iObserver.EditorOperationQuery( EUniEditorOperationProcessImage,
                                              EMEOQueryGuidedInsertLarge ) )
            {
            SetError( EUniProcessImgUserAbort );
            return EFalse; // Abort
            }
        }
        
    iScaleSize = scaleSize;
    return ETrue;
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::CreateEmptyAttachmentL
// ---------------------------------------------------------
//
void CUniEditorProcessImageOperation::CreateEmptyAttachmentL()
    {
    iNewAttaId = KMsvNullIndexEntryId;

    // 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 );
        }

    TFileName newFileName( parser.Name() );
    newFileName.Append( ext );

    iEditStore = iDocument.Mtm().Entry().EditStoreL();
    MMsvAttachmentManagerSync& managerSync = iEditStore->AttachmentManagerExtensionsL();
    CMsvAttachment* attachment = CMsvAttachment::NewL( CMsvAttachment::EMsvFile );
    CleanupStack::PushL( attachment );
    
    attachment->SetMimeTypeL( iTargetType.Des8() );

    managerSync.CreateAttachmentL( newFileName, iNewImageFile, attachment );
    CleanupStack::Pop( attachment ); // ownership transferred
    
    iNewAttaId = attachment->Id();
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::ImageSendSize
// ---------------------------------------------------------
//
TSize CUniEditorProcessImageOperation::ImageSendSize() const
    {
    TSize size;
    if( TUniMsvEntry::IsMmsUpload( iDocument.Entry() ) )
        {
        size.iWidth = KUniMmsUploadImageWidth;
        size.iHeight = KUniMmsUploadImageHeight;
        }
    else
        {
        size =  iDocument.MaxImageSize();
        }
    return size;
    }

// ---------------------------------------------------------
// CUniEditorProcessImageOperation::OpenFileForReadingL
// ---------------------------------------------------------
//
RFile CUniEditorProcessImageOperation::OpenFileForReadingL()
    {
    RFile sourceFile;
    if ( iAttachmentId != KMsvNullIndexEntryId )
        {
        __ASSERT_DEBUG( iEditStore, Panic( EUniNullPointer ) );
        sourceFile = iEditStore->AttachmentManagerL().GetAttachmentFileL( iAttachmentId );
        }
    else
        {
        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 == KErrNotFound )
        {
        aError = EUniProcessImgNotFound;
        }
    else if ( aError == KErrNoMemory )
        {
        aError = EUniProcessImgOutOfMemory;
        }
    else if ( aError == KErrDiskFull )
        {
        aError = EUniProcessImgOutOfDisk;
        }
    else if ( aError == KErrCorrupt )
        {
        aError = EUniProcessImgCorrupted;
        }
        
    return CUniEditorOperation::RunError( aError );
    }
    
// ---------------------------------------------------------
// CUniEditorProcessImageOperation::DetachImageInfo
// ---------------------------------------------------------
//
CMsgImageInfo* CUniEditorProcessImageOperation::DetachImageInfo()
    {
    // ownership transferred
    CMsgImageInfo* tempInfo = iNewImageInfo;
    iNewImageInfo = NULL;
    return tempInfo;
    }
    
// End of file
