/*
* Copyright (c) 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:  Implementation of Camera image decoder
*
*/


#include <imageconversion.h>
#include <ExifRead.h>
#include <ecam.h> // MCameraBuffer

#include <eikenv.h>
#include <SvgCodecImageConstants.hrh>
#include <SVGEngineInterfaceImpl.h>

#include "camlogging.h"
#include "camfilesaveutility.h"
#include "cambuffershare.h"

#include "camimagedecoder.h"

// ===========================================================================
// Local constants

namespace NCamCameraController
  {
  // Decoding flags
  static const TUint32 KConversionOptions = 
    ( CImageDecoder::EOptionNone
    | CImageDecoder::EOptionAlwaysThread
    | CImageDecoder::EPreferFastDecode
    );
  
  // EXIF reader flags
  //   Only need the thumbnail, so no need to parse the
  //   main (jpeg) image.
  static const TUint KExifReaderFlags = CExifRead::ENoJpeg;
  
  // Other constants
  static const TInt KMaxRetries = 10;
  static const TInt KPriority = CActive::EPriorityHigh;
  }

using namespace NCamCameraController;

// ===========================================================================
// public constructor and destructor

CCamImageDecoder* 
CCamImageDecoder::NewL( MCamImageDecoderObserver& aObserver )
  {
  CCamImageDecoder* self = 
      new (ELeave) CCamImageDecoder( aObserver );

  CleanupStack::PushL( self );
  self->ConstructL();
  CleanupStack::Pop( self );

  return self;  
  }


CCamImageDecoder::~CCamImageDecoder()
  {
  PRINT( _L("Camera => ~CCamImageDecoder") );
  Cancel();

  delete iDecoder;

  SetImageData( NULL );
  if(iDecodedBitmap)
  	{
  	delete iDecodedBitmap;
  	iDecodedBitmap = NULL;
  	}
  if ( iDecodedMask )
     {
     delete iDecodedMask;
     iDecodedMask = NULL;
     }

  delete iSvgEngine;
  iFs.Close();
  PRINT( _L("Camera <= ~CCamImageDecoder") );
  }

// ===========================================================================
// public methods
void
CCamImageDecoder::StartConversionL( CCamBufferShare* aBuffer )
  {
  PRINT( _L("Camera => CCamImageDecoder::StartConversionL") );

  // Data for CImageDecoder must be available throughout the conversion.
  // Need to stop any outstanding operation before deleting the descriptor.
  Cancel();

  PRINT( _L("Camera <> CCamImageDecoder: Copying image data..") );
  
  SetImageData( aBuffer );

  // GetImageDataL leaves if no data available.
  TDesC8* imageData = GetImageDataL();

  PRINT( _L("Camera <> CCamImageDecoder: Creating decoder..") );

  delete iDecoder;
  iDecoder = NULL;
  iDecoder = CImageDecoder::DataNewL( iFs, *imageData, (CImageDecoder::TOptions)KConversionOptions );

  if( iDecoder->FrameCount() > 0 )
    {
    const TFrameInfo& info( iDecoder->FrameInfo() );
    
#ifdef _DEBUG   
    TSize size = info.iOverallSizeInPixels;
    PRINT2( _L("Camera <> CCamImageDecoder: Bmp size(%d,%d)"), size.iWidth, size.iHeight );
    PRINT1( _L("Camera <> CCamImageDecoder: Bmp dispmode(%d)"), info.iFrameDisplayMode );
#endif

    PRINT( _L("Camera <> CCamImageDecoder: Create bitmap for snapshot..") );
    if( !iDecodedBitmap ) iDecodedBitmap = new (ELeave) CFbsBitmap;
    else                  iDecodedBitmap->Reset();
    
    TInt createError = iDecodedBitmap->Create( info.iOverallSizeInPixels, 
                                               info.iFrameDisplayMode );
    if( KErrNone != createError )
      {
      delete iDecodedBitmap;
      iDecodedBitmap = NULL;
      User::Leave( createError );
      }

    PRINT( _L("Camera <> CCamImageDecoder: start conversion..") );
    iRetryCounter = 0;
    iDecoder->Convert( &iStatus, *iDecodedBitmap, 0 );
    SetActive();
    }
  else
    {
    PRINT( _L("Camera <> CCamImageDecoder: No frame provided, leave..") );
    User::Leave( KErrNotFound );    
    }

  PRINT( _L("Camera <= CCamImageDecoder::StartConversionL") );
  }

// ---------------------------------------------------------------------------
// CCamImageDecoder::StartIconConversionL
// ---------------------------------------------------------------------------
//
void CCamImageDecoder::StartIconConversionL( TDesC* aFilePath, TSize& aSize )
    {
    PRINT3( _L("Camera => CCamImageDecoder::StartIconConversionL, file:[%S], size:(%d,%d)"),
              &(*aFilePath), aSize.iWidth, aSize.iHeight );

    // Delete any previous bitmaps, if any
    delete iDecodedBitmap,
    iDecodedBitmap = NULL;
    delete iDecodedMask;
    iDecodedMask = NULL;
    
    // Create bitmap for use while decoding 
    CFbsBitmap* frameBuffer = new (ELeave) CFbsBitmap;
    CleanupStack::PushL( frameBuffer );

    TFontSpec spec;
    if ( !iSvgEngine )
        {
        iSvgEngine = CSvgEngineInterfaceImpl::NewL( frameBuffer, NULL, spec );
        }
    
    TInt domHandle = KErrNotFound;
    MSvgError* serr = iSvgEngine->PrepareDom( *aFilePath, domHandle );
    PRINT3( _L("Camera <> prepare svg dom reader, warning:%d, err code:%d, description:[%S]"), 
                serr->IsWarning(), serr->ErrorCode(), &(serr->Description()) );
    if ( serr->HasError() && !serr->IsWarning() )
        {
        PRINT1( _L("Camera <> leaving with error:%d"), serr->SystemErrorCode() );
        User::Leave( serr->SystemErrorCode() );
        }

    // create image bitmap
    PRINT( _L("Camera <> CCamImageDecoder: Create bitmap for snapshot..") );
    if( !iDecodedBitmap ) iDecodedBitmap = new (ELeave) CFbsBitmap;
    else                  iDecodedBitmap->Reset();

    if( !iDecodedMask ) iDecodedMask = new (ELeave) CFbsBitmap;
    else                iDecodedMask->Reset();

    TRAPD ( createError, 
        {
        iDecodedBitmap->Create( aSize, EColor64K );
        iDecodedMask->Create( aSize, EGray256 );
        } );
    if( createError )
      {
      PRINT1( _L("Camera <> Error while creating bitmaps:%d"), createError );
      delete iDecodedBitmap;
      iDecodedBitmap = NULL;
      delete iDecodedMask;
      iDecodedMask = NULL;
      User::Leave( createError );
      }

    // create soft mask
    iSvgEngine->SetViewportHeight((CSvgDocumentImpl *)domHandle, aSize.iHeight);
    iSvgEngine->SetViewportWidth((CSvgDocumentImpl *)domHandle, aSize.iWidth);

    // render svg image
    serr = iSvgEngine->RenderDom( domHandle, iDecodedBitmap, iDecodedMask );
    PRINT3( _L("Camera <> render svg, warning:%d, err code:%d, description:[%S]"), 
                serr->IsWarning(), serr->ErrorCode(), &(serr->Description()) );
    if ( serr->HasError() && !serr->IsWarning() )
        {
        PRINT1( _L("Camera <> leaving with error:%d"), serr->SystemErrorCode() );
        User::Leave( serr->SystemErrorCode() );
        }

    CleanupStack::PopAndDestroy( frameBuffer );
    if ( !IsActive() )
        {
        SetActive();
        }

    PRINT( _L("Camera <= CCamImageDecoder::StartIconConversionL") );
    }


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
TDesC8*
CCamImageDecoder::GetImageDataL()
  {
  PRINT( _L("Camera => CCamImageDecoder::GetImageDataL") );
  
  if( !iSharedImageData ||
      !iSharedImageData->SharedBuffer() )
    {
    User::Leave( KErrNotFound );
    }

  TDesC8* data = iSharedImageData->SharedBuffer()->DataL( 0 );

  if( !data )
    User::Leave( KErrNotFound );

  delete iThumbnailData;
  iThumbnailData = ReadExifThumbNail( *data ); // Returns NULL on errors

#ifdef CAMERAAPP_CREATE_TESTIMAGE
  TRAPD( saveStatus1, SaveImageDataToFileL( *data, _L("testimagefull.jpg") ) );
  PRINT1( _L("Camera <> CCamImageDecoder: Save full image to file status:%d"), saveStatus1 );
  if( iThumbnailData )
    {
    TRAPD( saveStatus2, SaveImageDataToFileL( *iThumbnailData, _L("testimagethumb.jpg") ) );
    PRINT1( _L("Camera <> CCamImageDecoder: Save thumbnail to file status:%d"), saveStatus2 );
    }
#endif


  if( iThumbnailData )
    {
    data = iThumbnailData;
    }  

  PRINT1( _L("Camera <= CCamImageDecoder::GetImageDataL, data size:%d"), data->Size() );
  return data;
  }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
HBufC8*
CCamImageDecoder::ReadExifThumbNail( const TDesC8& aExifData )
  {
  PRINT( _L("Camera => CCamImageDecoder::ReadExifThumbNailL") );
  HBufC8* thumb = NULL;
  TRAP_IGNORE( 
    {
    CExifRead* reader = CExifRead::NewL( aExifData, KExifReaderFlags );
    CleanupStack::PushL( reader );
    thumb = reader->GetThumbnailL();
    CleanupStack::PopAndDestroy( reader );
    });

  PRINT( _L("Camera <= CCamImageDecoder::ReadExifThumbNailL") );
  return thumb;
  }

// ===========================================================================
// from CActive

// ---------------------------------------------------------------------------
// virtual 
// ---------------------------------------------------------------------------
//
void 
CCamImageDecoder::DoCancel()
  {
  PRINT( _L("Camera => CCamImageDecoder::DoCancel") );

  if( iDecoder )
    {
    iDecoder->Cancel();
    delete iDecoder;
    iDecoder = NULL;    
    }

  SetImageData( NULL );

  PRINT( _L("Camera <= CCamImageDecoder::DoCancel") );
  }


// ---------------------------------------------------------------------------
// virtual 
// ---------------------------------------------------------------------------
//
void 
CCamImageDecoder::RunL()
  {
  PRINT1( _L("Camera => CCamImageDecoder::RunL, iStatus:%d"), iStatus.Int() );

  switch( iStatus.Int() )
    {
    case KErrNone :
      {
      // CImageDecoder has finished using the data,
      // so we are able to free it.
      if ( iSvgEngine )
          {
          iSvgEngine->Destroy();
          delete iSvgEngine;
          iSvgEngine = NULL;
          }
      SetImageData( NULL );
      iObserver.ImageDecodedL( iStatus.Int(), iDecodedBitmap, iDecodedMask );
      break;
      }
    case KErrUnderflow :
      {
      // Decoder did not have enough data to convert.
      // CImageDecoder documentation recommends calling
      // repeatedly ContinueConvert.
      if( iRetryCounter++ < KMaxRetries )
        {
        iStatus = KErrNone;
        iDecoder->ContinueConvert( &iStatus );
        SetActive();
        }
      else
        {
        // Handled in RunError
        User::Leave( KErrUnderflow );
        }
      break;
      }
    case KErrCancel :
    default :
      {
      User::Leave( iStatus.Int() );
      break;
      }
    }

  PRINT( _L("Camera <= CCamImageDecoder::RunL") );
  }


// ---------------------------------------------------------------------------
// virtual 
// ---------------------------------------------------------------------------
//
TInt 
CCamImageDecoder::RunError( TInt aError )
  {
  PRINT1( _L("Camera => CCamImageDecoder::RunError(%d)"), aError );

  SetImageData( NULL );
  // Leave has occurred in RunL.
  // Notify observer with error.
  TRAP_IGNORE(iObserver.ImageDecodedL( aError, NULL, NULL ));

  PRINT( _L("Camera <= CCamImageDecoder::RunError") );
  return KErrNone;
  }


// ===========================================================================
void
CCamImageDecoder::SetImageData( CCamBufferShare* aBuffer )
  {
  if( iSharedImageData )
    {
    iSharedImageData->Release();
    iSharedImageData = NULL;
    }

  iSharedImageData = aBuffer;

  if( iSharedImageData )
    iSharedImageData->Reserve();
  }

// ===========================================================================
// private constructors


void
CCamImageDecoder::ConstructL()
  {
  User::LeaveIfError( iFs.Connect() );

  CActiveScheduler::Add( this );
  }



CCamImageDecoder::CCamImageDecoder( MCamImageDecoderObserver& aObserver )
  : CActive( KPriority  ),
    iObserver( aObserver )
  {
  }

// end of file
