/*
* Copyright (c) 2008 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:   
*
*/



#include "HuiFxVisualLayer.h"
#include "Matrix.h"
#include "HuiFxParameterManager.h"
#include <uiacceltk/HuiVisual.h>
#include "HuiFxConstants.h"
#include <e32math.h>
#include "HuiCmdBufferBrush.h"

EXPORT_C CHuiFxVisualLayer* CHuiFxVisualLayer::NewL(MHuiEffectable* aVisual)
    {
    CHuiFxVisualLayer* e = new (ELeave) CHuiFxVisualLayer();
    CleanupStack::PushL(e);
    e->ConstructL(aVisual);
    CleanupStack::Pop(e);
    return e;
    }

EXPORT_C void CHuiFxVisualLayer::ConstructL(MHuiEffectable* aVisual)
    {
#ifdef HUIFX_TRACE    
    RDebug::Print(_L("CHuiFxVisualLayer::ConstructL - 0x%x "), this);
#endif
    CHuiFxLayer::ConstructL( ELayerTypeVisual );
    iParameterManager = CHuiFxParameterManager::NewL();
    iVisual = aVisual;
    iSrcType = EVisualSrcVisual;
    iExtBitmapFile = NULL;
    
    iTranslationX = 0.0f;
    iTranslationY = 0.0f;
    iTranslationZ = 0.0f;
    
    iScaleX = 1.0f;
    iScaleY = 1.0f;
    iScaleZ = 1.0f;
    iScaleOriginX = 0.0f;
    iScaleOriginY = 0.0f;
    iScaleOriginZ = 0.0f;
    
    iRotationOriginX = 0.0f;
    iRotationOriginY = 0.0f;
    iRotationOriginZ = 0.0f;
    iRotationAngle = 0.0f;
    iRotationAxisX = 0.0f;
    iRotationAxisY = 0.0f;
    iRotationAxisZ = 1.0f;
    
    iSkewAngleX = 0.0f;
    iSkewAngleY = 0.0f;
    iSkewAngleZ = 0.0f;
    iSkewOriginX = 0.0f;
    iSkewOriginY = 0.0f;
    iSkewOriginZ = 0.0f;
    
    // We use the visual's own target opacity if we haven't set something explicitly    
    if (iVisual)
        {
        //iOpacity = iVisual->iOpacity.Target();
        iOpacity = iVisual->EffectOpacityTarget();
        }
    else
        {
        iOpacity = 1.0;
        }
    RegisterParameterL(KLitTranslationX, &iTranslationX);
    RegisterParameterL(KLitTranslationY, &iTranslationY);
    RegisterParameterL(KLitTranslationZ, &iTranslationZ);
    
    RegisterParameterL(KLitScaleX, &iScaleX);
    RegisterParameterL(KLitScaleY, &iScaleY);
    RegisterParameterL(KLitScaleZ, &iScaleZ);
    RegisterParameterL(KLitScaleOriginX, &iScaleOriginX);
    RegisterParameterL(KLitScaleOriginY, &iScaleOriginY);
    RegisterParameterL(KLitScaleOriginZ, &iScaleOriginZ);
   
    RegisterParameterL(KLitRotationOriginX, &iRotationOriginX);
    RegisterParameterL(KLitRotationOriginY, &iRotationOriginY);
    RegisterParameterL(KLitRotationOriginZ, &iRotationOriginZ);
    RegisterParameterL(KLitRotationAngle, &iRotationAngle);
    RegisterParameterL(KLitRotationAxisX, &iRotationAxisX);
    RegisterParameterL(KLitRotationAxisY, &iRotationAxisY);
    RegisterParameterL(KLitRotationAxisZ, &iRotationAxisZ);
    
    RegisterParameterL(KLitSkewAngleX, &iSkewAngleX);
    RegisterParameterL(KLitSkewAngleY, &iSkewAngleY);
    RegisterParameterL(KLitSkewAngleZ, &iSkewAngleZ);
    RegisterParameterL(KLitSkewOriginX, &iSkewOriginX);
    RegisterParameterL(KLitSkewOriginY, &iSkewOriginY);
    RegisterParameterL(KLitSkewOriginZ, &iSkewOriginZ);
    
    RegisterParameterL(KLitOpacityParameter, &iOpacity);
    }

EXPORT_C CHuiFxVisualLayer *CHuiFxVisualLayer::CloneL() const
{
    CHuiFxVisualLayer *layer = new (ELeave) CHuiFxVisualLayer;
    layer->CHuiFxLayer::ConstructL( ELayerTypeVisual );
    layer->CHuiFxLayer::CopyFromL(this);
    layer->iVisual = iVisual; // visual will be set later...
    layer->iParameterManager = iParameterManager->CloneL();
    layer->iParameterManager->CopyParameterL(KLitTranslationX, &layer->iTranslationX, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitTranslationY, &layer->iTranslationY, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitTranslationZ, &layer->iTranslationZ, iParameterManager);
    
    layer->iParameterManager->CopyParameterL(KLitScaleX, &layer->iScaleX, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitScaleY, &layer->iScaleY, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitScaleZ, &layer->iScaleZ, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitScaleOriginX, &layer->iScaleOriginX, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitScaleOriginY, &layer->iScaleOriginY, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitScaleOriginZ, &layer->iScaleOriginZ, iParameterManager);
   
    layer->iParameterManager->CopyParameterL(KLitRotationOriginX, &layer->iRotationOriginX, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitRotationOriginY, &layer->iRotationOriginY, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitRotationOriginZ, &layer->iRotationOriginZ, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitRotationAngle, &layer->iRotationAngle, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitRotationAxisX, &layer->iRotationAxisX, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitRotationAxisY, &layer->iRotationAxisY, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitRotationAxisZ, &layer->iRotationAxisZ, iParameterManager);
    
    layer->iParameterManager->CopyParameterL(KLitSkewAngleX, &layer->iSkewAngleX, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitSkewAngleY, &layer->iSkewAngleY, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitSkewAngleZ, &layer->iSkewAngleZ, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitSkewOriginX, &layer->iSkewOriginX, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitSkewOriginY, &layer->iSkewOriginY, iParameterManager);
    layer->iParameterManager->CopyParameterL(KLitSkewOriginZ, &layer->iSkewOriginZ, iParameterManager);
    
    layer->iParameterManager->CopyParameterL(KLitOpacityParameter, &layer->iOpacity, iParameterManager);

    layer->iSrcType = iSrcType;
    layer->iTranslationX = iTranslationX;
    layer->iTranslationY = iTranslationY;
    layer->iTranslationZ = iTranslationZ;
    layer->iScaleX = iScaleX;    
    layer->iScaleY = iScaleY;    
    layer->iScaleZ = iScaleZ;    
    layer->iScaleOriginX = iScaleOriginX;    
    layer->iScaleOriginY = iScaleOriginY;    
    layer->iScaleOriginZ = iScaleOriginZ;    
    layer->iRotationOriginX = iRotationOriginX;    
    layer->iRotationOriginY = iRotationOriginY;    
    layer->iRotationOriginZ = iRotationOriginZ;    
    layer->iRotationAngle = iRotationAngle;
    layer->iRotationAxisX = iRotationAxisX;
    layer->iRotationAxisY = iRotationAxisY;
    layer->iRotationAxisZ = iRotationAxisZ;
    layer->iSkewAngleX = iSkewAngleX;
    layer->iSkewAngleY = iSkewAngleY;
    layer->iSkewAngleZ = iSkewAngleZ;
    
    layer->iSkewOriginX = iSkewOriginX;
    layer->iSkewOriginY = iSkewOriginY;
    layer->iSkewOriginZ = iSkewOriginZ;
    if (iExtBitmapFile)
        layer->iExtBitmapFile = iExtBitmapFile->AllocL();
    else
        layer->iExtBitmapFile = NULL;
    layer->iOpacity = iOpacity;
    layer->iOpaqueHint = iOpaqueHint;
    return layer;
}
EXPORT_C void CHuiFxVisualLayer::SetExtRect( TRect * /*aExtRect*/ )
    {
    // not needed..
    }
EXPORT_C void CHuiFxVisualLayer::SetVisual( CHuiVisual * aVisual )
    {
    iVisual = aVisual->Effectable();
    
    if (Math::IsInfinite(iOpacity))
        {
        iOpacity = aVisual->iOpacity.Target();
        }
    }
EXPORT_C void CHuiFxVisualLayer::SetVisual( MHuiEffectable *aVisual )
    {
    iVisual = aVisual;
    
    if (Math::IsInfinite(iOpacity))
        {
        //iOpacity = iVisual->iOpacity.Target();
        iOpacity = iVisual->EffectOpacityTarget();
        }    
    }



EXPORT_C CHuiFxVisualLayer::~CHuiFxVisualLayer()
    {
#ifdef HUIFX_TRACE    
    RDebug::Print(_L("CHuiFxVisualLayer::~CHuiFxVisualLayer - 0x%x "), this);
#endif
    delete iExtBitmapFile;
    delete iParameterManager;
    iParameterManager = NULL;
    }

EXPORT_C TBool CHuiFxVisualLayer::Changed() const
    {
    TBool ret = iParameterManager->HasAnimatedParameters();
#ifdef HUIFX_TRACE    
    RDebug::Print(_L("CHuiFxVisualLayer::Changed - 0x%x , ret %d"), this, ret);    
#endif
    return ret;
    }

EXPORT_C void CHuiFxVisualLayer::AdvanceTime(TReal32 aElapsedTime)
    {
    iParameterManager->AdvanceTime(aElapsedTime);
    }

EXPORT_C TBool CHuiFxVisualLayer::PrepareDrawL(CHuiFxEngine& aEngine)
    {
    iParameterManager->Update(aEngine, VisualRect());
    return ETrue;
    }

EXPORT_C void CHuiFxVisualLayer::Draw(CHuiFxEngine& aEngine, CHuiGc& aGc, CHuiFxRenderbuffer &aTarget, CHuiFxRenderbuffer& /*aSource*/)
    {
#ifdef HUIFX_TRACE    
    RDebug::Print(_L("CHuiFxVisualLayer::Draw - 0x%x "), this);
#endif
    aTarget.BindAsRenderTarget();
    aGc.Push(EHuiGcMatrixModel);
    
    THuiFxEngineType engineType = aEngine.EngineType();
    TRenderBufferType bufferType = aTarget.BufferType();
    if(engineType == EHuiFxEngineGles20)
        {
        if(bufferType == EBufferTypeOffscreen)
            {
            aGc.Enable(CHuiGc::EFeatureBlending, ETrue);
            aGc.SetBlendMode(CHuiGc::EBlendReplace);
            }
        TRect viewPort(TPoint(0,0), aTarget.Size());
        aGc.SetProjection(CHuiGc::EProjectionOrthographic, viewPort);
        }
    
    aGc.Translate(EHuiGcMatrixModel, VisualRect().iTl.iX, VisualRect().iTl.iY, 0.0f);
    ApplyTransformations(aGc);
    aGc.Translate(EHuiGcMatrixModel, -VisualRect().iTl.iX, -VisualRect().iTl.iY, 0.0f);
    
    aGc.SetBlendMode( CHuiGc::EBlendNormal );
    //iVisual->iOpacity.Set( iOpacity );
    iVisual->EffectSetOpacity( iOpacity );   
    
    // Set new clipping region which does not clip anything. 
    // We want always draw visual fully to the aTarget. 
    aGc.PushClip();
    TRect bufferArea = TRect(TPoint(0,0), aTarget.Size()); 
    aGc.SetClip(bufferArea); // this does not transform bufferArea which is fine for us    
    
    // Draw visual content to aTarget
    //iVisual->DrawSelf(aGc, TargetRect());
    iVisual->EffectSetSource(iSrcType==EVisualSrcInput1);
    iVisual->EffectDrawSelf( aGc, TargetRect() );
    iVisual->EffectSetSource(EFalse);
    
    // Restore original clipping
    aGc.PopClip();
    
    if(engineType == EHuiFxEngineGles20)
        {
        if(bufferType == EBufferTypeOffscreen)
            {
            aGc.SetBlendMode(CHuiGc::EBlendNormal);
            }
        aGc.SetProjection(CHuiGc::EProjectionOrthographic);
        }
    
    aGc.Pop(EHuiGcMatrixModel);
    aTarget.UnbindAsRenderTarget();
    }

void CHuiFxVisualLayer::ApplyTransformations(CHuiGc& aGc)
    {
#ifdef HUIFX_TRACE    
    RDebug::Print(_L("CHuiFxVisualLayer::ApplyTransformations - 0x%x "), this);
#endif
    // Matrix multiplication is generally not commutative.
    // Therefore the order of the transformations matters.
    // In order to prevent the scaling ang skewing from affecting the translation,
    // if seems wiser to do the translation first, otherwise the results seem to be unpredictable.
    
    // Translation
	TReal32 translationX = 0.0f;
	TReal32 translationY = 0.0f;
	TReal32 translationZ = 0.0f;

    if (iTranslationX != 0.0f)
		{
		translationX = iTranslationX - iVisual->EffectDisplayRect().iTl.iX;
		}
	if (iTranslationY != 0.0f)
        {
        translationY = iTranslationY - iVisual->EffectDisplayRect().iTl.iY;
        }    

	if (iTranslationZ != 0.0f)
        {
		// Z translation not supported by the model
        translationZ = iTranslationZ;
        }    

	  if (translationX != 0.0f || translationY != 0.0f || translationZ != 0.0f)
	  	{
        aGc.Translate(EHuiGcMatrixModel, translationX, translationY, translationZ);
		}
        
    // Scaling
    if (iScaleX != 1.0f || iScaleY != 1.0f || iScaleZ != 1.0f)
        {
        if ( iScaleOriginX != 0 || iScaleOriginY != 0 || iScaleOriginZ != 0 )
            {
            aGc.Translate(EHuiGcMatrixModel, iScaleOriginX, iScaleOriginY, iScaleOriginZ);
            aGc.Scale(EHuiGcMatrixModel, iScaleX, iScaleY, iScaleZ); 
            aGc.Translate(EHuiGcMatrixModel, -iScaleOriginX, -iScaleOriginY, -iScaleOriginZ);
            }
        else
            {
            aGc.Scale(EHuiGcMatrixModel, iScaleX, iScaleY, iScaleZ); 
            }
        }
    
    // Skewing
    if (iSkewAngleX != 0.0f || iSkewAngleY != 0.0f || iSkewAngleZ != 0.0f)
        {
        const TReal32 radsPerDeg = 2.0f * (float)M_PI / 360.0f;
        TReal32 skewX = tan(iSkewAngleX * radsPerDeg);
        TReal32 skewY = tan(iSkewAngleY * radsPerDeg);
        TReal32 skewZ = tan(iSkewAngleZ * radsPerDeg);
        if ( iSkewOriginX != 0 || iSkewOriginY != 0 || iSkewOriginZ != 0 )
            {
            aGc.Translate(EHuiGcMatrixModel, iSkewOriginX, iSkewOriginY, iSkewOriginZ);
            aGc.Shear(EHuiGcMatrixModel, skewX, skewY, skewZ); 
            aGc.Translate(EHuiGcMatrixModel, -iSkewOriginX, -iSkewOriginY, -iSkewOriginZ);
            }
        else
            {
            aGc.Shear(EHuiGcMatrixModel, skewX, skewY, skewZ); 
            }
        }

    // Rotation
    if (iRotationAngle != 0.0f)
        {
        if ( iRotationOriginX != 0 || iRotationOriginY != 0 || iRotationOriginZ != 0 )
            {
            aGc.Translate(EHuiGcMatrixModel, iRotationOriginX, iRotationOriginY, iRotationOriginZ);
            aGc.Rotate(EHuiGcMatrixModel, iRotationAngle, iRotationAxisX, iRotationAxisY, iRotationAxisZ); 
            aGc.Translate(EHuiGcMatrixModel, -iRotationOriginX, -iRotationOriginY, -iRotationOriginZ);
            }
        else
            {
            aGc.Rotate(EHuiGcMatrixModel, iRotationAngle, iRotationAxisX, iRotationAxisY, iRotationAxisZ); 
            }
        }

    // Translation
/*    
    if (iTranslationX != 0.0f || iTranslationY != 0.0f || iTranslationZ != 0.0f)
        {
        aGc.Translate(EHuiGcMatrixModel, iTranslationX, iTranslationY, iTranslationZ);
        }    
*/        
    }

EXPORT_C TBool CHuiFxVisualLayer::VisualArea( TRect& aRect )
    {
    // TODO: cache?
    // TODO: transforms
    //aRect = iVisual->DisplayRect();        
    THuiRealRect rect = iVisual->EffectDisplayRect();
    aRect = rect;
    return ETrue;
    }

EXPORT_C TBool CHuiFxVisualLayer::Margin(TMargins &m)
   {
   m.iLeft = iTranslationX < 0.0 ? -iTranslationX : 0.0;
   m.iRight = iTranslationX > 0.0 ? iTranslationX : 0.0;
   m.iTop = iTranslationY < 0.0 ? -iTranslationY : 0.0;
   m.iBottom = iTranslationY > 0.0 ? iTranslationY : 0.0;
   return ETrue;
   }

EXPORT_C MHuiFxParameter* CHuiFxVisualLayer::Parameter(const TDesC& aName)
    {
    return iParameterManager->Parameter(aName);
    }

EXPORT_C void CHuiFxVisualLayer::RegisterParameterL(const TDesC& aName, TReal32* aValue)
    {
    iParameterManager->RegisterParameterL(aName, aValue);
    }

EXPORT_C void CHuiFxVisualLayer::RegisterParameterL(const TDesC& aName, TRgb* aValue)
    {
    iParameterManager->RegisterParameterL(aName, aValue);
    }

void CHuiFxVisualLayer::SetSourceType( const THuiFxVisualSrcType aSrcType )
    {
    iSrcType = aSrcType; 
    }
    
THuiFxVisualSrcType CHuiFxVisualLayer::SourceType() const
    {
    return iSrcType;
    }
    
void CHuiFxVisualLayer::SetExtBitmapFileL( TDesC& aFilename )
    {
#ifdef HUIFX_TRACE    
    RDebug::Print(_L("CHuiFxVisualLayer::SetExtBitmapFileL - 0x%x, %S "), this, &aFilename);
#endif    
    delete iExtBitmapFile;
    iExtBitmapFile = NULL;
    if ( aFilename.Length() == 0 )
        {
        // don't set an empty filename
        return;
        }
    iExtBitmapFile = HBufC::NewL( aFilename.Length() );
    *iExtBitmapFile = aFilename;
    //TODO: the bitmap from this filename should be loaded as texture to the visual.
    }
void CHuiFxVisualLayer::FxmlVisualInputs(RArray<THuiFxVisualSrcType> &aArray)
    {
    aArray.Append(iSrcType);
    }

TBool CHuiFxVisualLayer::FxmlUsesOpaqueHint() const
    {
    return iOpaqueHint;
    }

void  CHuiFxVisualLayer::SetFxmlUsesOpaqueHint(TBool aValue)
    { 
    iOpaqueHint = aValue; 
    }

TBool CHuiFxVisualLayer::IsSemitransparent() const
    {
    if (iOpacity < 1.f)
        {
        return ETrue;        
        }
    else
        {
        return EFalse;        
        }
    }
