/*
* Copyright (c) 2006 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:   Loads textures.
*
*/



#include <e32std.h>
#include <imageconversion.h>
#include <eikenv.h>
#include <eikappui.h>
#include <eikapp.h>

#include "alf/alftexturemanager.h"
#include "alf/alftextureprocessor.h"
#include "alf/alftexture.h"
#include "alf/alfenv.h"
#include "alfclient.h"
#include "alf/alfutil.h"
#include "alf/alfbitmapprovider.h"
#include "alflogger.h"

const TInt KFirstAutoGeneratedTextureId = 0x10000000;
const TInt KAlfDefaultSizeDimension = 4;

/**
 * Flag to indicate to use the provided bitmap directly for the texture, without conversion or copying. The bitmaps
 * should be in the correct format and they shall not be compressed or have duplicated handle.
 * If the direct bitmap cannot be used, this flag is ignored internally.
 *
 * NOTE: This flag is not a public since we cannot guarantee that user does not compress
 * bitmap or otherwise temper with it after providing it to toolkit. 
 * But internally we can use this in some cases to speed up the texture upload.
 *
 */
enum
    {
    EAlfTextureFlagAllowDirectBitmapUsage = 0x20        
    };

struct TTextureEntry 
    {
    public:
    	~TTextureEntry()
        {
        }
    	TTextureEntry()                      
                      : iId(0), iTexture(NULL), iRefCount(-1)
        {                            
        }
    	
    	TTextureEntry(TInt aId,
                      CAlfTexture* aTexture)                      
                      : iId(aId), iTexture(aTexture),iRefCount(-1)
        {
        }

    /** The texture id. Set to zero for no id. */
    TInt iId;

    /** The texture entry. */
    CAlfTexture* iTexture;
    
    /** Reference count. */
    TInt iRefCount;
    };

struct TLoadQueueEntry
    {
    /** The texture entry that is being loaded. */
    TTextureEntry iLoading;

    /** The image being loaded has an alpha channel. */
    TBool iHasAlpha;

    /** The original, non-downscaled size of the image. */
    TSize iOriginalSize;

    /** Image decoder to load bitmap images. */
    CImageDecoder* iDecoder;

    /** True, if the texture was already unloaded before it finished
        loading. */
    TBool iUnloaded;
    };


// Private structure
struct CAlfTextureManager::TPrivateData
    {
    CAlfEnv* iEnv;              // Not owned.  	
    
    /**
     * Registry of all textures within this toolkit.
     * Accessed by texture ids (iId). 
     */
    RArray<TTextureEntry> iTextures;

    /** Queue of loading tasks. */
    RArray<TLoadQueueEntry> iLoadQueue;

    /** Queue of loading observers. */
    RPointerArray<MAlfTextureLoadingCompletedObserver> iLoadObserverQueue;

    /** Queue of state observers. */
    RPointerArray<MAlfTextureManagerStateChangedObserver> iStateObserverQueue;

    /** Blank texture */
    CAlfTexture* iBlankTexture;

    /** Path where image files are loaded from. */
    HBufC* iImagePath;
    
    /** State of the texture manager. */
    TState iState;

    /** Bitmap for loading asynchronously into. */
    CFbsBitmap* iBitmap;

    /** Mask bitmap for loading alpha channels. */
    CFbsBitmap* iMaskBitmap;

    /** Open file server session for image loading. */
    RFs iFs;
    
    /** Id of this texture manager. */
    TUid iManagerId;

    /** Texture processor */
    CAlfTextureProcessor* iProcessor;
    
    /** Texture id auto generation */
    TInt iAutoGeneratedTextureId;
    TInt iLowestAutoGeneratedTextureId;
    TInt iHighestAutoGeneratedTextureId;
    
    RPointerArray<MAlfTextureAutoSizeObserver> iAutoSizeObserverQueue;
    
    // DEBUG
    TBool iTextureAutoCounterEnabled;
    TInt iTextureAutoRecreateCounter;
    TInt iTextureAutoReloadCounter;

    //...add other member variables...
    CIdle* iDoomBringer;
    };



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

// ---------------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------------
//
CAlfTextureManager::CAlfTextureManager() : CActive(EPriorityHigh)
	{
		
	}
	
// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CAlfTextureManager::~CAlfTextureManager()
	{
	Cancel();
	if ( iData )
	    {
        delete iData->iImagePath;
	    
	    delete iData->iBlankTexture;
	    
	    RArray<TTextureEntry>& textures = (iData->iTextures);	
        // Remove all texture entries (from last to first -order)
        while (textures.Count())
            {
            TTextureEntry te = textures[textures.Count()-1];
            
            delete te.iTexture; 
            te.iTexture = NULL;                
            }

        textures.Close();   

	    iData->iLoadQueue.Close();	    
    
	    iData->iLoadObserverQueue.Close();	

	    iData->iStateObserverQueue.Close();	
	    
	    iData->iAutoSizeObserverQueue.Close();

        delete iData->iBitmap;
        delete iData->iMaskBitmap;

        iData->iFs.Close();
        
        delete iData->iProcessor;        
        if (iData->iDoomBringer)
            iData->iDoomBringer->Cancel();
        delete iData->iDoomBringer;
	    }	

	delete iData;
	}
// ---------------------------------------------------------------------------
// NewL
// ---------------------------------------------------------------------------
//
CAlfTextureManager* CAlfTextureManager::NewL(CAlfEnv& aEnv, TUid aUid)
	{
	CAlfTextureManager* self = CAlfTextureManager::NewLC(aEnv, aUid);        
    CleanupStack::Pop( self );
    return self;		
	}
	

// ---------------------------------------------------------------------------
// NewLC
// ---------------------------------------------------------------------------
//
CAlfTextureManager* CAlfTextureManager::NewLC(CAlfEnv& aEnv, TUid aUid)
	{
    CAlfTextureManager* self = new( ELeave ) CAlfTextureManager();
    CleanupStack::PushL( self );
    self->ConstructL(aEnv, aUid);
    return self;	
	}

// ---------------------------------------------------------------------------
// 2nd phase constructor
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::ConstructL(CAlfEnv& aEnv, TUid aUid)
	{
    iData = new (ELeave) TPrivateData;
    
    // Zero all data
    iData->iEnv = NULL;  	
    iData->iTextures.Reset();
    iData->iLoadQueue.Reset();
    iData->iLoadObserverQueue.Reset();
    iData->iAutoSizeObserverQueue.Reset();
    iData->iBlankTexture = NULL;
    iData->iImagePath = NULL;
    iData->iState = EIdle;
    iData->iBitmap = NULL;
    iData->iMaskBitmap = NULL;
    iData->iManagerId = TUid::Uid(0);
    iData->iProcessor = NULL;

    // Fill data
    iData->iAutoGeneratedTextureId = KFirstAutoGeneratedTextureId;
    iData->iLowestAutoGeneratedTextureId = KFirstAutoGeneratedTextureId;
    iData->iHighestAutoGeneratedTextureId = KMaxTInt;
    iData->iEnv = &aEnv;    
    iData->iManagerId = aUid;
    iData->iBlankTexture = CAlfTexture::NewL(aEnv, aUid);
    iData->iBitmap = new (ELeave) CFbsBitmap();
    User::LeaveIfError(iData->iBitmap->Create(
        TSize(KAlfDefaultSizeDimension, KAlfDefaultSizeDimension), EColor64K));
    iData->iMaskBitmap = new (ELeave) CFbsBitmap();
    User::LeaveIfError(iData->iMaskBitmap->Create(
        TSize(KAlfDefaultSizeDimension, KAlfDefaultSizeDimension), EGray256));	
    iData->iProcessor = CAlfTextureProcessor::NewL(aEnv); 
    User::LeaveIfError(iData->iFs.Connect());
    iData->iDoomBringer = CIdle::NewL(CActive::EPriorityIdle);
    // DEBUG 
    iData->iTextureAutoCounterEnabled = EFalse;
    iData->iTextureAutoRecreateCounter = 0;
    iData->iTextureAutoReloadCounter = 0;
    
    SetImagePathL(_L(""));
	CActiveScheduler::Add(this);
	}

// ---------------------------------------------------------------------------
// Returns env
// ---------------------------------------------------------------------------
//
EXPORT_C CAlfEnv& CAlfTextureManager::Env()
    {
    return *iData->iEnv;
    }

// ---------------------------------------------------------------------------
// Returns texture with given id
// ---------------------------------------------------------------------------
//
EXPORT_C const CAlfTexture* CAlfTextureManager::Texture(TInt aId) const
    {
    TInt index = CheckTexture(aId);
    if(index == KErrNotFound)
        {
        return &BlankTexture();
        }
        
    RArray<TTextureEntry>& textures = (iData->iTextures);	
    CAlfTexture* texture = textures[index].iTexture;
    if(texture)
        {
        return texture;
        }
    else
        {
        return &BlankTexture();
        }    
    }

// ---------------------------------------------------------------------------
// Returns texture with given id
// ---------------------------------------------------------------------------
//
EXPORT_C CAlfTexture* CAlfTextureManager::TextureL(TInt aId)
    {
    // try to get the index for the texture id
    TInt index = CheckTexture(aId);
    if(index != KErrNotFound)
        {
        // texture found
        RArray<TTextureEntry>& textures = (iData->iTextures);	
        CAlfTexture* texture = textures[index].iTexture;
        if(texture == NULL)
            {
            User::Leave(KErrNotFound);
            return NULL;                
            }
        return texture;
        }

    User::Leave(KErrNotFound);
    return NULL;
    }

// ---------------------------------------------------------------------------
// Sets image path
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::SetImagePathL(const TDesC& aPath)
    {
    delete iData->iImagePath;
    iData->iImagePath = NULL;

    TParsePtrC parse(aPath);
    CEikonEnv* coe = CEikonEnv::Static();
    if (aPath.Length() && !parse.DrivePresent() && coe && coe->EikAppUi() && coe->EikAppUi()->Application())
        {
        iData->iImagePath = HBufC::NewL(aPath.Size()+2); // two extra characters for drive
        TPtr ptr = iData->iImagePath->Des();
        ptr.Append(coe->EikAppUi()->Application()->AppFullName().Left(2));
        ptr.Append(aPath);
        }
    else
        {
        iData->iImagePath = aPath.AllocL();        
        }
    }
    
// ---------------------------------------------------------------------------
// Gets image path
// ---------------------------------------------------------------------------
//
EXPORT_C const TDesC& CAlfTextureManager::ImagePath() const
    {
    return *iData->iImagePath;        
    }


// ---------------------------------------------------------------------------
// Returns blank texture.
// ---------------------------------------------------------------------------
//
EXPORT_C const CAlfTexture& CAlfTextureManager::BlankTexture() const
    {
    return *iData->iBlankTexture;            
    }

// ---------------------------------------------------------------------------
// Returns blank texture.
// ---------------------------------------------------------------------------
//
EXPORT_C CAlfTexture& CAlfTextureManager::BlankTexture()
    {
    return *iData->iBlankTexture;            
    }

// ---------------------------------------------------------------------------
// DEPRECATED! Load the animated texture based on the Skin name
// ---------------------------------------------------------------------------
//

EXPORT_C CAlfTexture& CAlfTextureManager::LoadAnimatedTextureL(const TDesC& /*aSkinAnimName*/,
                                       TSize /*aTextureMaxSize*/,
                                       TAlfTextureFlags /*aFlags*/,
                                       TInt /*aId*/)
    {
    __ALFLOGSTRING( "CAlfTextureManager::LoadAnimatedTextureL FUNCTIONALITY REMOVED. REFER TO ANTRIKSH PROJECT" );
    CAlfTexture* tex = NULL;
    return *tex;
    }

// Loads texture.
// ---------------------------------------------------------------------------
//
EXPORT_C CAlfTexture& CAlfTextureManager::LoadTextureL(const TDesC& aImageName,
                                       TAlfTextureFlags aFlags,
                                       TInt aId)
    {
    return LoadTextureL(aImageName, TSize(0, 0), aFlags, aId);        
    }

// ---------------------------------------------------------------------------
// Loads texture.
// ---------------------------------------------------------------------------
//
EXPORT_C CAlfTexture& CAlfTextureManager::LoadTextureL(const TInt aId,
                                       TSize aTextureMaxSize,
                                       TAlfTextureFlags aFlags)
    {
    return LoadTextureL(_L(""), aTextureMaxSize, aFlags, aId);
    }

// ---------------------------------------------------------------------------
// Loads texture.
// ---------------------------------------------------------------------------
//
EXPORT_C CAlfTexture& CAlfTextureManager::LoadTextureL(const TDesC& aImageName,
                                       TSize aTextureMaxSize,
                                       TAlfTextureFlags aFlags,
                                       TInt aId)
    {    
    // Clients are not allowed to let toolkit use bitmaps directly. 
    // We could use it internally if we had separate bitmaps
    // for each entry.
    RemoveFlags(aFlags, EAlfTextureFlagAllowDirectBitmapUsage);
        
    if (aId == KAlfAutoGeneratedTextureId)
        {
        aId = GenerateTextureId();    
        }    
    
    CAlfTexture* tex = NULL;
    TFileName fileName;

    // Provide an already created texture if such exists.
    TRAPD(err, tex = TextureL(aId));
    if(err == KErrNone && tex->HasContent())
        {
        return *tex;
        }

    // If shared texture manager, then check the existense of the texture first.
    if (IsShared())
        {
        // AutoSize is not supported (yet) for shared textures
        RemoveFlags(aFlags, EAlfTextureFlagAutoSize);

        TBool sharedTextureAlreadyExists = EFalse;
        iData->iEnv->Client().TextureHasContent(sharedTextureAlreadyExists, 
            aId, 
            iData->iManagerId.iUid);
        
        // If shared texture already exists, we don't even start to load it.
        if (sharedTextureAlreadyExists)
            {
            TInt bitmapHandle = 0;
            TInt maskHandle = 0;
        	
        	if (iData->iBitmap)
        		{
        		bitmapHandle = iData->iBitmap->Handle();
        		}
        	
        	if (iData->iMaskBitmap)	
        		{
        		maskHandle = iData->iMaskBitmap->Handle();	
        		}
            
            // Create CAlfTexture instance.
        	tex = CAlfTexture::NewL( Env(), 
        	    iData->iManagerId,
        	    aId, 
        	    bitmapHandle, 
        	    maskHandle, 
        	    aFlags);             
            
            if(aImageName.Length() > 0)
                {
                // assume relative pathname and prepend the image path to get full filename
                fileName = aImageName;
                PrependImagePath(fileName);
                tex->SetFileNameL(fileName);
                }
            
            tex->SetMaxTextureSize(aTextureMaxSize);

            return *tex;    
            }            
        }

    // If the name is invalid, and there was no filename available
    // based on id, return a dummy texture.
    if((aImageName.Length() == 0) && (aId == 0))
        {
        return BlankTexture();
        }

    // add path to filename if filename has been passed
    if(aImageName.Length() > 0)
        {
        // assume relative pathname and prepend the image path to get full filename
        fileName = aImageName;
        PrependImagePath(fileName);
        }

    // if no name has been passed as a parameter but
    // there's and nonzero id has been passed, we
    // can assume that there's a predefined name available
    RArray<TTextureEntry>& textures = (iData->iTextures);	
    RArray<TLoadQueueEntry>& loadqueue = (iData->iLoadQueue);	

    if((aImageName.Length() == 0) && (aId != 0))
       {
        // search for a texture filename based on the id.
        TInt index = CheckTexture(aId);
        if(index >=0)
            {
            tex = textures[index].iTexture;
            fileName = *tex->FileName();
            }
        else
            {
            User::Leave(KErrNotFound);
            }
       }

    // Reuse pre-existing entries:
    // try first finding an entry based on id
    if(!tex && aId > 0)
        {
        TInt previouslyLoadedIndex = CheckTexture(aId);
        if (previouslyLoadedIndex >= 0)
            {
            tex = textures[previouslyLoadedIndex].iTexture;
            }
        }

    if (!tex)
        {
        TInt bitmapHandle = 0;
        TInt maskHandle = 0;

        // If LoadAnimAsImage flag is set, we don't check the framecount with ImageDecoder!
        //
        // This is useful especially when loading multiple large image files
        // as textures that don't have more then 1 frame.
        //
        // Some test results creating the ImageDecoder: 
        // - Large Leafs.mbm (2,7MB) took apporx. 280ms
        // - Big bmp (~1,8MB) took ~130ms 
        // - Normal sized (100-800kb) jpgs and gifs took approx. 25-40ms
        TInt frameCount = 1;
        TSize animFrameSize = TSize(0,0);
        if ( !(aFlags & EAlfTextureFlagLoadAnimAsImage) )
            {
            CImageDecoder* decoder = CImageDecoder::FileNewL(iData->iFs, fileName);
            CleanupStack::PushL(decoder);
            frameCount = decoder->FrameCount();
            if (frameCount > 1)
                {
                const TFrameInfo& fInfo =  decoder->FrameInfo(0);
                animFrameSize = fInfo.iOverallSizeInPixels;
                }
            CleanupStack::PopAndDestroy();
            }
        
        if (frameCount > 1)
            {
            tex = CAlfTexture::NewL(Env(), iData->iManagerId, aId, bitmapHandle, maskHandle, aFlags, ETrue);        
            tex->SetMaxTextureSize(aTextureMaxSize);
            tex->SetSize(animFrameSize);
            tex->SetFileNameL(fileName);
            return *tex;
            }
        else
            {
        	if (iData->iBitmap)
        		{
    	    	bitmapHandle = iData->iBitmap->Handle();
    		    }
    	
        	if (iData->iMaskBitmap)	
    		    {
    		    maskHandle = iData->iMaskBitmap->Handle();	
    		    }
        	tex = CAlfTexture::NewL(Env(), iData->iManagerId, aId, bitmapHandle, maskHandle, aFlags);        
            }
        }
    else
        {
        // we have a pre-existing texture which is non-null,
        // but check that the texture is loaded ok, we can also
        // return unloaded textures that are in the load queue
        if (tex->HasContent() || tex->IsAnimated() || IsInLoadQueue(tex))
            {
            return *(tex);
            }
        }

    // replace filename
    // File names are relative to the image path.
    tex->SetFileNameL(fileName);
    tex->SetMaxTextureSize(aTextureMaxSize);

    // Set not released 
    tex->SetReleaseFlags(CAlfTexture::EReleaseNone);

    // If autosize used and size is set to zero, we don't yet load at all.
    if (aFlags & EAlfTextureFlagAutoSize)
        {            
        if (aTextureMaxSize.iHeight == 0 && aTextureMaxSize.iWidth == 0)
            {
            return *tex;    
            }        
        }
        
    // Prepare for loading by creating a load queue entry.
    TTextureEntry entry;
    entry.iTexture = tex;
    entry.iId = aId;

    TLoadQueueEntry loadqentry;
    loadqentry.iUnloaded = EFalse;
    loadqentry.iLoading = entry;
    loadqentry.iDecoder = NULL;

    // Textures are loaded one at a time, in the order they were requested.
    loadqueue.AppendL(loadqentry);

    // Start the texture load active object if we're not loading
    if(iData->iState == EIdle)
        {
        // Start loading images..
        StartLoading();
        // Notify observers.
        NotifyStateChange();
        }

    return *tex;
    }


// ---------------------------------------------------------------------------
// Creates texture.
// ---------------------------------------------------------------------------
//
EXPORT_C CAlfTexture& CAlfTextureManager::CreateTextureL(TInt aId,
                                   MAlfBitmapProvider* aBitmapProvider,
                                   TAlfTextureFlags aFlags)
    {    
    // Clients are not allowed to let toolkit use bitmaps directly. It is used only internally.
    RemoveFlags(aFlags, EAlfTextureFlagAllowDirectBitmapUsage);

    CAlfTexture* tex = NULL;
    
    if (aId == KAlfAutoGeneratedTextureId)
        {
        aId = GenerateTextureId();    
        }    

    CFbsBitmap* bitmap = NULL;
    CFbsBitmap* maskBitmap = NULL;

    if (aId==0)
        {
        User::Leave(KErrArgument);
        }

    // Provide an already created texture if such exists.
    TRAPD(err, tex = TextureL(aId));
    if(err == KErrNone && tex->HasContent())
        {
        return *tex;
        }

    // Call the bitmapProvider method to load the bitmaps
	if (aBitmapProvider)
		{
	    aBitmapProvider->ProvideBitmapL(aId, bitmap, maskBitmap);		
		}
					
	if (!bitmap)
	    { 
	    // We leave here, otherwise serverside would panic. 
	    User::Leave(KErrArgument);    
	    // Bitmap size is checked and handled in serverside, 
	    // so that we can create empty textures (size = 0,0)
	    }
	    	  
	// "bitmap" pointer should be valid from now on
    CleanupStack::PushL(bitmap);
	TInt bitmapHandle = bitmap->Handle();
	
	TInt maskHandle = 0;
	if (maskBitmap)	
		{
		maskHandle = maskBitmap->Handle();	
	    CleanupStack::PushL(maskBitmap);
		}
    
    // Create CAlfTexture instance, this duplicates the bitmaps to server.
	if (!tex)
	    {
    	tex = CAlfTexture::NewL( Env(), 
    	                         iData->iManagerId,
    	                         aId, 
    	                         bitmapHandle, 
    	                         maskHandle, 
    	                         aFlags);	        
	    }
	else
	    {
	    // texture already exists but it does not have content, this creates content.
      	iData->iEnv->Client().TextureCreateL( aId, bitmapHandle, 
        	maskHandle, aFlags, iData->iManagerId.iUid );    	            	        
	    }    
		
	tex->SetBitmapProvider(aBitmapProvider);	
    tex->SetSize(bitmap->SizeInPixels());

    // Set not released 
    tex->SetReleaseFlags(CAlfTexture::EReleaseNone);
	
	// CAlfTexture has at this point duplicated bitmaps, can be deleted here.        
	if (maskBitmap)	
		{
	    CleanupStack::PopAndDestroy( maskBitmap );
	    maskBitmap = NULL;		
		}
    CleanupStack::PopAndDestroy( bitmap ); 
	bitmap = NULL;		

    // Notify observers
    NotifyTextureLoaded(*tex, aId, KErrNone);

    return *tex;
    }

// ---------------------------------------------------------------------------
//  Unloads texture
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::UnloadTexture(const TDesC& aImageName)
    {
    TInt index = CheckTexture(aImageName);
    if (index >= 0)
        {
        RArray<TTextureEntry>& textures = (iData->iTextures);	
        TTextureEntry entry = textures[index];        
        CancelLoadingOfTexture(*entry.iTexture);
        if (!entry.iTexture->IsAnimated())
            {
            iData->iEnv->Client().TextureUnload(entry.iId, iData->iManagerId.iUid);
            }
        else
            {
            entry.iTexture->StopAnimation();      
            }
        
        TInt releaseFlags = entry.iTexture->ReleaseFlags();
        releaseFlags |= CAlfTexture::EReleaseFromUnload;
        entry.iTexture->SetReleaseFlags(releaseFlags);
        }
    }

// ---------------------------------------------------------------------------
//  Unloads texture
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::UnloadTexture(TInt aId)
    {
    TInt index = CheckTexture(aId);
    if (index >= 0)
        {
        RArray<TTextureEntry>& textures = (iData->iTextures);	
        TTextureEntry entry = textures[index];        
		CancelLoadingOfTexture(*entry.iTexture);
        if (!entry.iTexture->IsAnimated())
            {
            iData->iEnv->Client().TextureUnload( aId, iData->iManagerId.iUid );    	                
            }
        else
            {
            entry.iTexture->StopAnimation();        
            }           
        
        TInt releaseFlags = entry.iTexture->ReleaseFlags();
        releaseFlags |= CAlfTexture::EReleaseFromUnload;
        entry.iTexture->SetReleaseFlags(releaseFlags);
        }
    }
 
// ---------------------------------------------------------------------------
//  Updates texture content
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::UpdateTextureFromFileL(TInt aId, const TDesC* aFileName)
    {
   	RArray<TTextureEntry>& textureEntries = (iData->iTextures);	
    TInt index = CheckTexture(aId);
    if (index >= 0)
        {
    	TTextureEntry entry = textureEntries[index];
    	
    	TFileName fileName;
    	if (aFileName != NULL)
    		{
    		fileName = *aFileName;
    		}
    	else if (entry.iTexture->FileName() == NULL)
    		{
    		User::Leave(KErrArgument);
    		}	
        entry.iTexture->SetBitmapProvider(NULL);
        
        // Set non released
        entry.iTexture->SetReleaseFlags(CAlfTexture::EReleaseNone);

        if (!entry.iTexture->IsAnimated())
            {
           	ReloadTextureL(fileName, entry.iTexture->MaxTextureSize(), entry.iTexture->Flags(), 
               	entry.iTexture->Id());
            }
        else
            {
            entry.iTexture->StartAnimation();    
            }    
        } 
    else
    	{
    	User::Leave(KErrNotFound);
    	}           
    }
    
// ---------------------------------------------------------------------------
//  Updates texture content
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::UpdateTextureFromBitmapL(TInt aId, MAlfBitmapProvider* aBitmapProvider)
    {
   	RArray<TTextureEntry>& textureEntries = (iData->iTextures);	
    TInt index = CheckTexture(aId);
    if (index >= 0)
        {
    	TTextureEntry entry = textureEntries[index];
    	
    	if (!aBitmapProvider)
    		{
    		aBitmapProvider = entry.iTexture->BitmapProvider();
    		}
    	TBuf<1> empty;	
        entry.iTexture->SetFileNameL(empty);

        // Set non released
        entry.iTexture->SetReleaseFlags(CAlfTexture::EReleaseNone);

        if (!entry.iTexture->IsAnimated())
            {
           	RecreateTextureL(entry.iTexture->Id(), aBitmapProvider, entry.iTexture->Flags());
            }
        else
            {
            entry.iTexture->StartAnimation();     
            }    
        }        
    else
    	{
    	User::Leave(KErrNotFound);
    	}           
    } 
    
    
// ---------------------------------------------------------------------------
//  Defines filename for given id.
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::DefineFileNameL(TInt aId, const TDesC& aFileName)
    {
    if (aId==0)
        {
        User::Leave(KErrArgument); // can't specify filename for "no id"
        }

   	RArray<TTextureEntry>& textures = (iData->iTextures);	

    // Look for an existing entry for the id.
    for(TInt i = 0; i < textures.Count(); ++i)
        {
        if(textures[i].iId == aId)
            {
            textures[i].iTexture->SetFileNameL(aFileName);
            return;
            }
        }

    // Otherwise just append to the texture list
	CAlfTexture* tex = CAlfTexture::NewL( Env(), iData->iManagerId, aId );  
	tex->SetFileNameL(aFileName);
    }
    
// ---------------------------------------------------------------------------
// Prepends image path
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::PrependImagePath(TDes& aFileName)
    {
    TFileName buf;

    //allow app to load textures from different drive with complete path
    TParse p1;
    p1.Set(aFileName,0,0);

    if (p1.DrivePresent())
        {
        return;
        }


    if(aFileName.Find(*iData->iImagePath) == KErrNotFound)
        {
        buf = *iData->iImagePath;
        buf.Append(aFileName);
        aFileName = buf;
        }
    }



// ---------------------------------------------------------------------------
// CheckTexture
// ---------------------------------------------------------------------------
//
TInt CAlfTextureManager::CheckTexture(const TDesC& aImageName) const
    {
    if(aImageName.Length()==0)
        {
        // name empty, can't check
        return KErrNotFound;
        }

    RArray<TTextureEntry>& textures = (iData->iTextures);	
    for(TInt i = 0; i < textures.Count(); ++i)
        {
        TTextureEntry te = textures[i];
        // compare against texture manager entry filename (iFileName)
        if((te.iTexture->FileName() != NULL)
            && (aImageName.Compare(*(te.iTexture->FileName())) == 0))
            {
            return i;
            }
        }
    // not found
    return KErrNotFound;
    }

	
// ---------------------------------------------------------------------------
// CheckTexture
// ---------------------------------------------------------------------------
//
TInt CAlfTextureManager::CheckTexture(TInt aId) const
    {
    RArray<TTextureEntry>& textures = (iData->iTextures);	
    if(aId <= 0)
        {
        // id not defined, can't search
        return KErrNotFound;
        }
    for(TInt i = 0; i < textures.Count(); i++)
        {
        if(textures[i].iId == aId)
            {
            return i;
            }
        }
    // not found:
    return KErrNotFound;
    }
    
    

// ---------------------------------------------------------------------------
// RunError from CActive
// ---------------------------------------------------------------------------
//
TInt CAlfTextureManager::RunError(TInt /*aError*/)
    {
    return KErrNone;
    // Should never be called, RunL is responsible for handling all error cases
    }


// ---------------------------------------------------------------------------
// RunL from CActive
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::RunL()
    {
    // remove the loaded entry from the queue
    TLoadQueueEntry entry = PopLoadedQueueEntry();

    // Image loading has been completed.
    iData->iState = EIdle;

    // check status
    if(iStatus == KErrNone)
        {
        // ok, we have a loaded image, but
        // we still need to do texture uploads
        // and possibly some image conversions
        TRAPD( err, ImageLoadingCompleteL(entry) );

        if(err != KErrNone)
            {
            // Notify observers about the image upload error
            NotifyTextureLoaded(*entry.iLoading.iTexture, entry.iLoading.iId, err);
            }
        }
    else
        {
        // notify sb of failed image load!
        NotifyTextureLoaded(*entry.iLoading.iTexture, entry.iLoading.iId, iStatus.Int());
        }

    StartLoading();
    }

// ---------------------------------------------------------------------------
// Cancels asyncronous loading
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::DoCancel()
    {
    RArray<TLoadQueueEntry>& loadqueue = (iData->iLoadQueue);	
    if(loadqueue.Count() > 0)
        {
        loadqueue[0].iDecoder->Cancel();
        }
    iData->iState = EIdle;
    }

// ---------------------------------------------------------------------------
// Starts asyncronous loading
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::StartLoading()
    {
    // loop that finds next entry to load
    while (1)
        {
        // try to schedule next image for loading..
        TRAPD( err, DoLoadNextL() );

        // ok?            
        if(err != KErrNone)
            {            
            // remove the entry from the queue
            TLoadQueueEntry entry = PopLoadedQueueEntry();
            // Notify observers about the image loading error
            NotifyTextureLoaded(*entry.iLoading.iTexture, entry.iLoading.iId, err);            
            // Image loading has been completed.
            iData->iState = EIdle;
            continue;
            }
        // leave the loop if we had no trouble scheduling the next
        // image decode
        break;
        }
    }

// ---------------------------------------------------------------------------
// Loads texture entry from queue. Mostly this code is copied from 
// CAlfTextureManager::DoLoadNextL but some adjustmets has been made to support
// better client server paradigm.
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::DoLoadNextL()
    {
    RArray<TLoadQueueEntry>& loadqueue = (iData->iLoadQueue);	
    // Any loading tasks left?
    if(loadqueue.Count() == 0)
        {
        // No? Notify observers and leave.
        NotifyStateChange();
        return; // nothing else to be loaded..
        }

    // Manager is now busy.
    iData->iState = ELoading;

    // Fetch a load queue entry
    TLoadQueueEntry& entry = loadqueue[0];
    CAlfTexture* texture = entry.iLoading.iTexture;

    // Create a new image decoder for loading the image.
    TFileName imageFileName = *texture->FileName(); // does not include image path
    PrependImagePath(imageFileName);
    entry.iDecoder = 0;
    // We use fast decode because it seems to be a lot faster with jpegs.
    TRAPD( err,  entry.iDecoder = CImageDecoder::FileNewL(iData->iFs, imageFileName, CImageDecoder::EPreferFastDecode ));

    // check for errors..
    if(err != KErrNone)
        {
        // Try to cancel the decoding (if possible)
        if(entry.iDecoder)
            {
            entry.iDecoder->Cancel();
            }
        User::Leave(err); // re-leave with the error
        }
        
    // from decoder's frame info retrieve the framesize
    TFrameInfo frameInfo(entry.iDecoder->FrameInfo(0));

    // check for alpha channel 
    if(frameInfo.iFlags & TFrameInfo::ETransparencyPossible )
        {
        entry.iHasAlpha = ETrue;
        }
    else
        {
        entry.iHasAlpha = EFalse;
        }

    // Get the image original size
    TRect bitmapSize = frameInfo.iFrameCoordsInPixels;
    entry.iOriginalSize = bitmapSize.Size();

    // target resolution for texture, initially equal to bitmap size
    TInt widthResolutionTarget = bitmapSize.Size().iWidth;
    TInt heightResolutionTarget = bitmapSize.Size().iHeight;

    TSize maxTexSize = entry.iLoading.iTexture->MaxTextureSize();

    // Assign new texture resolution target dimensions
    // if we have explicitly requested them
    if( (entry.iLoading.iTexture->Flags() & EAlfTextureFlagDoNotRetainResolution)
            && maxTexSize.iWidth > 0
            && maxTexSize.iHeight > 0)
        {
        // assign new target resolution for decoder-based scaling
        if(maxTexSize.iWidth < widthResolutionTarget)
            {
            widthResolutionTarget = maxTexSize.iWidth;
            }
        if(maxTexSize.iHeight < heightResolutionTarget)
            {
            heightResolutionTarget = maxTexSize.iHeight;
            }
        }

    // we need to do some downscaling, but can we do arbitrary
    // scaling as well?
    if(frameInfo.iFlags & TFrameInfo::EFullyScaleable)
        {
        // .. yes, arbitrary scaling is possible
        // just assign the new size to the bitmap
        // so that it will be scaled accordingly during
        // conversion
        }
    else
        {
        // all decoders should be able to do 1/2, 1/4, 1/8 DCT Scaling
        // calculate nearest half size for the decoder-downscaled bitmap
        // halve image width&height to size which is closest larger match
        // of the bitmap size
        TInt halvedWidth = bitmapSize.Size().iWidth;
        TInt halvedHeight = bitmapSize.Size().iHeight;
        TInt halveFactor = 1; // this limits the halving to 1/8 max
        while ( ((halvedWidth >> 1) > widthResolutionTarget) &&
                ((halvedHeight >> 1) > heightResolutionTarget)
                && (halveFactor << 1) <= 8)
            {
            halveFactor <<= 1;
            }
        halvedWidth = halvedWidth / halveFactor;
        halvedHeight = halvedHeight / halveFactor;
        // .. the bitmap will be downscaled further to the correct
        // dimensions by the toolkit after the bitmap has been decoded
        widthResolutionTarget = halvedWidth;
        heightResolutionTarget = halvedHeight;
        }


    if (!(entry.iLoading.iTexture->Flags() & EAlfTextureFlagRetainColorDepth) && 
        iData->iBitmap->DisplayMode()!=EColor64K)
        {
        // (Re)Create the bitmap in EColor64K (16bit) mode to save memory
        iData->iBitmap->Create(TSize(widthResolutionTarget, heightResolutionTarget), EColor64K);
        }
    else if ((entry.iLoading.iTexture->Flags() & EAlfTextureFlagRetainColorDepth) && 
        iData->iBitmap->DisplayMode()==EColor64K)
        {
        // (Re)Create the bitmap in EColor16MU (24bit) mode retain the color information
        iData->iBitmap->Create(TSize(widthResolutionTarget, heightResolutionTarget), EColor16MU);
        }
    else
        {
        // no need to recreate the bitmap, but assign the new size!
        iData->iBitmap->Resize(TSize(widthResolutionTarget, heightResolutionTarget));
        }

    if (iData->iBitmap->DisplayMode()==EColor64K)
        {
        }
    // Decode ( and rescale ) to bitmap.
    if(entry.iHasAlpha)
        {
        // set the alpha channel bitmap to the same size than the color bitmap
        iData->iMaskBitmap->Resize(TSize(widthResolutionTarget, heightResolutionTarget));
        entry.iDecoder->Convert(&iStatus, *iData->iBitmap, *iData->iMaskBitmap, 0);
        }
    else
        {
        // We cannot do resize mask to smaller until serverside works differently
        iData->iMaskBitmap->Resize(TSize(widthResolutionTarget, heightResolutionTarget));
        entry.iDecoder->Convert(&iStatus, *iData->iBitmap, 0);
        }
    // Wait for completion.
    SetActive();

    }


// ---------------------------------------------------------------------------
// Pops entry from load queue
// ---------------------------------------------------------------------------
//
TLoadQueueEntry CAlfTextureManager::PopLoadedQueueEntry()
    {
    RArray<TLoadQueueEntry>& loadqueue = (iData->iLoadQueue);	
    TLoadQueueEntry entry = loadqueue[0];
    loadqueue.Remove(0);
    // Delete the decoder.
    delete entry.iDecoder;
    entry.iDecoder = 0;
    return entry;
    }


// ---------------------------------------------------------------------------
// This method gets called when image loading has been completed and server
// side texture needs to be created.
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::ImageLoadingCompleteL(TLoadQueueEntry& aEntry)
    {                    
    TSize maxTexSize = aEntry.iLoading.iTexture->MaxTextureSize();    
    
    // Calculate new maxsize keeping the aspect ratio if only one max dimension was
    // specified.
    if ((maxTexSize.iWidth == 0 && maxTexSize.iHeight != 0) ||
        (maxTexSize.iWidth != 0 && maxTexSize.iHeight == 0))
        {
        TSize sizeInPixels = iData->iBitmap->SizeInPixels();
        if (sizeInPixels.iWidth != 0 && sizeInPixels.iHeight != 0)
            {
            if (maxTexSize.iWidth == 0)
                {
                maxTexSize.iWidth = (maxTexSize.iHeight * sizeInPixels.iWidth)/sizeInPixels.iHeight;                    
                }
            else if (maxTexSize.iHeight == 0)
                {
                maxTexSize.iHeight = (maxTexSize.iWidth * sizeInPixels.iHeight)/sizeInPixels.iWidth;   
                }                                
            }            
        }
    
    
    if( (maxTexSize.iWidth != 0) && 
        (maxTexSize.iHeight != 0) && 
        (iData->iBitmap->SizeInPixels() != aEntry.iLoading.iTexture->MaxTextureSize()))
        {
        // The decoder failed to constrain the texture dimensions properly, due to its internal limitations.
        // So we need to scale the texture(s) down further to the correct size.              
        CFbsBitmap* dest = new (ELeave) CFbsBitmap();   
        dest->Create(maxTexSize, iData->iBitmap->DisplayMode());     
        CleanupStack::PushL(dest);
        AlfUtil::ScaleFbsBitmapL(*iData->iBitmap, *dest);
        CleanupStack::Pop(); // dest
        delete iData->iBitmap;
        iData->iBitmap = dest;

        if(aEntry.iHasAlpha)
            {
            // Scale the alpha as well.
            dest = new (ELeave) CFbsBitmap();   
            dest->Create(maxTexSize, iData->iMaskBitmap->DisplayMode());     
            CleanupStack::PushL(dest);
            AlfUtil::ScaleFbsBitmapL(*iData->iMaskBitmap, *dest);
            CleanupStack::Pop(); // dest
            delete iData->iMaskBitmap;
            iData->iMaskBitmap = dest;
            }                                      
        }        

    TInt bitmapHandle = 0;
    TInt maskHandle = 0;
	
	if (iData->iBitmap)
		{
		bitmapHandle = iData->iBitmap->Handle();
		}
	
	if (iData->iMaskBitmap && aEntry.iHasAlpha)	
		{
		maskHandle = iData->iMaskBitmap->Handle();	
		}

    TInt handle = iData->iEnv->Client().TextureLoadL( aEntry.iLoading.iTexture->Id(), 
   	    bitmapHandle, 
   	    maskHandle, 
   	    aEntry.iLoading.iTexture->Flags(), 
   	    iData->iManagerId.iUid);

    aEntry.iLoading.iTexture->SetServerHandle(handle);   	    
    
    if (iData->iBitmap)
        aEntry.iLoading.iTexture->SetSize(iData->iBitmap->SizeInPixels());

    // Notify the observers of loaded texture
    NotifyTextureLoaded(*aEntry.iLoading.iTexture, aEntry.iLoading.iId, KErrNone);

    // Resize bitmaps to save memory
    iData->iBitmap->Reset();
    iData->iMaskBitmap->Reset();
    iData->iBitmap->Create(
        TSize(KAlfDefaultSizeDimension, KAlfDefaultSizeDimension), EColor64K);
    iData->iMaskBitmap->Create(
        TSize(KAlfDefaultSizeDimension, KAlfDefaultSizeDimension), EGray256);

    // DEBUG 
    if (iData->iTextureAutoCounterEnabled)
        {            
        TBuf<256> numBuf;
        numBuf.FillZ();
        numBuf.Append(_L("Reloads:"));
        numBuf.AppendNum(iData->iTextureAutoReloadCounter);
        numBuf.Append(_L(" - Recreates:"));
        numBuf.AppendNum(iData->iTextureAutoRecreateCounter);
        User::InfoPrint(numBuf);
        }

    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::NotifyStateChange() const
    {
    __ALFLOGSTRING( "CAlfTextureManager::NotifyStateChange" )

    RPointerArray<MAlfTextureManagerStateChangedObserver>& observers = 
        (iData->iStateObserverQueue);	
    TInt count = observers.Count();
    for(TInt i = 0; i < count; i++)
     	{
        observers[i]->TextureManagerStateChanged(*this); 								                       
        }        
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::NotifyTextureLoaded(CAlfTexture& aTexture,
                             TInt aTextureId,
                             TInt aErrorCode) const
    {
// NOTE: Commented out because this will prevent alflogging (aTexture.FileName() = NULL => crash)
//    __ALFLOGSTRING3( "CAlfTextureManager::NotifyTextureLoaded -- filename: %S, textureId: %d, error: %d",
//          aTexture.FileName(), aTextureId, aErrorCode )

    RPointerArray<MAlfTextureLoadingCompletedObserver>& observers = 
        (iData->iLoadObserverQueue);	
    TInt count = observers.Count();
    for(TInt i = 0; i < count; i++)
     	{
        observers[i]->TextureLoadingCompleted(aTexture, aTextureId, 
            aErrorCode); 								                       
        }        
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::AddLoadObserverL(
    MAlfTextureLoadingCompletedObserver* aObserver)
    {
    iData->iLoadObserverQueue.Append(aObserver);    
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::RemoveLoadObserver(
MAlfTextureLoadingCompletedObserver* aObserver)
    {
    RPointerArray<MAlfTextureLoadingCompletedObserver>& observers = 
            (iData->iLoadObserverQueue);	

    TInt count = observers.Count();
    for(TInt i = 0; i < count; i++)
      	{
    	if (observers[i] == aObserver)
    		{
    		observers.Remove(i);
    		observers.Compress();
    		break;
    		}								                       
        }        
    }
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TUid CAlfTextureManager::ManagerUid()
    {
    return iData->iManagerId;    
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
EXPORT_C CAlfTextureProcessor& CAlfTextureManager::Processor()
    {
    return *iData->iProcessor;    
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::AppendTextureL(CAlfTexture* aTexture)
    {
    if (CheckTexture(aTexture->Id()) == KErrNotFound)
        {
        // Create a new texture entry
        TTextureEntry entry(aTexture->Id(), aTexture);
        
        // Add the new entry to the list of new textures
        iData->iTextures.AppendL(entry);                    
        }
    else
        {
        User::Leave(KErrAlreadyExists);    
        }    
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::RemoveTexture(CAlfTexture& aTexture)
    {
    // Cancel loading of the texture to be removed.
    CancelLoadingOfTexture(aTexture);
    
    RArray<TTextureEntry>& textures = (iData->iTextures);	
    for(TInt i = 0; i < textures.Count(); i++)
        {
        TTextureEntry te = textures[i];
        if (&aTexture == te.iTexture)
            {
            te.iTexture = NULL;                
            textures.Remove(i);                
            break;
            }
        }        
    }
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool CAlfTextureManager::IsInLoadQueue(const CAlfTexture* texture) const
    {
    TBool retVal = EFalse;
    RArray<TLoadQueueEntry>& loadQueue = iData->iLoadQueue;	
    for(TInt i = 0; i < loadQueue.Count(); i++)
        {
        TLoadQueueEntry loadEntry = loadQueue[i];
        if (loadEntry.iLoading.iTexture == texture)
            {
            retVal = ETrue;
            break;    
            }
        }
    return retVal;
    }
    
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
CAlfTextureManager::TState CAlfTextureManager::State() const
    {
    return iData->iState;    
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::AddStateObserverL(
    MAlfTextureManagerStateChangedObserver* aObserver)
    {
    iData->iStateObserverQueue.Append(aObserver);            
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::RemoveStateObserver(
    MAlfTextureManagerStateChangedObserver* aObserver)
    {
    RPointerArray<MAlfTextureManagerStateChangedObserver>& observers = 
            (iData->iStateObserverQueue);	

    TInt count = observers.Count();
    for(TInt i = 0; i < count; i++)
      	{
    	if (observers[i] == aObserver)
    		{
    		observers.Remove(i);
    		observers.Compress();
    		break;
    		}								                       
        }                
    }
    
EXPORT_C TBool CAlfTextureManager::IsLoaded(const CAlfTexture * texture) const
    {
    if(texture != NULL && texture->HasContent() && !IsInLoadQueue(texture))
        {
        return ETrue;
        }
    return EFalse;
    }


EXPORT_C TBool CAlfTextureManager::IsLoaded(const TDesC& aImageName) const
    {
    TFileName fileName = *iData->iImagePath;
    fileName.Append(aImageName);
    TInt index = CheckTexture(fileName);
    if(index == KErrNotFound)
        {
        return EFalse;
        }
    return IsLoaded(iData->iTextures[index].iTexture);
    }

EXPORT_C TInt CAlfTextureManager::TextureId(const TDesC& aImageName) const
    {
    TFileName fileName = *iData->iImagePath;
    fileName.Append(aImageName);
    TInt index = CheckTexture(fileName);
    
    if(index == KErrNotFound)
        {
        return KErrNotFound;
        }
    return iData->iTextures[index].iId;
    }

EXPORT_C TBool CAlfTextureManager::IsLoaded(TInt aId) const
    {
    ASSERT(aId!=0);
    TInt index = CheckTexture(aId);
    if(index == KErrNotFound)
        {
        return EFalse;
        }
    return IsLoaded(iData->iTextures[index].iTexture);
    }



// ---------------------------------------------------------------------------
// Release all textures with given priority level (or above)
// ---------------------------------------------------------------------------
//
TBool CAlfTextureManager::Release( TInt aReleasePriorityLevel )
    {
    TBool allReleased = ETrue;
    
    RArray<TTextureEntry>& textures = (iData->iTextures);	
    for(TInt i = 0; i < textures.Count(); i++)
        {
        if (textures[i].iTexture->Id())
            {
            if ( textures[i].iTexture->Priority() < aReleasePriorityLevel )
                {
                allReleased = EFalse;
                }
            else
                {
                if (!textures[i].iTexture->IsAnimated())
                    {
                    ReleaseEntry(textures[i]);                    
                    }
                else
                    {
                    textures[i].iTexture->StopAnimation();    
                    }                    
                }
            }
        }
    return allReleased;
    }

// ---------------------------------------------------------------------------
// Release a specific entry
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::ReleaseEntry(const TTextureEntry& aEntry)
    {
    // Id texture has content, it must be released here using unload,
    // otherwise we can just notify serverside.
    if (aEntry.iTexture->HasContent())
        {        
        UnloadTexture(aEntry.iTexture->Id());                        
        }
    else
        {
        iData->iEnv->Client().TextureRelease(aEntry.iTexture->Id(), 
            iData->iManagerId.iUid);            
        }    
    
    TInt releaseFlags = aEntry.iTexture->ReleaseFlags();
    releaseFlags |= CAlfTexture::EReleaseFromEnv;
    aEntry.iTexture->SetReleaseFlags(releaseFlags);
    }

// ---------------------------------------------------------------------------
// Restore all textures with given priority level (or lover)
// ---------------------------------------------------------------------------
//
TBool CAlfTextureManager::RestoreL( TInt aRestorePriorityLevel )
    {
    TBool allRestored = ETrue;
    
    RArray<TTextureEntry>& textures = (iData->iTextures);	
    for(TInt i = 0; i < textures.Count(); i++)
        {
        if ( textures[i].iTexture->Priority() <= aRestorePriorityLevel )
            {
            if (!textures[i].iTexture->IsAnimated())
                {
                RestoreEntryL(textures[i]);    
                }
            else
                {
                textures[i].iTexture->StartAnimation();                                                
                }                
            }
        else
            {
            allRestored = EFalse;
            }
        }    
    
    return allRestored;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::RestoreEntryL(const TTextureEntry& aEntry)
    {
    // Id texture has no content, it must be restored here using create/load
    // otherwise we can just notify serverside.
    if (!aEntry.iTexture->HasContent())
        {
        // Clear release flags 
        TInt releaseFlags = aEntry.iTexture->ReleaseFlags();
        releaseFlags &= ~CAlfTexture::EReleaseFromEnv;    
        releaseFlags &= ~CAlfTexture::EReleaseFromUnload;                   
        aEntry.iTexture->SetReleaseFlags(releaseFlags);

        // If there are other release flags, then do not restore.        
        if (!releaseFlags)
            {                         
            // check for provider-based restore               
            if (aEntry.iTexture->BitmapProvider() != NULL)
                {
                CreateTextureL(aEntry.iTexture->Id(),
                               aEntry.iTexture->BitmapProvider(),
                               aEntry.iTexture->Flags());
                }
            // check for file-based restore
            else if (aEntry.iTexture->FileName() != NULL)
                {
                LoadTextureL(aEntry.iTexture->FileName()->Des(),
                    aEntry.iTexture->MaxTextureSize(), 
                    aEntry.iTexture->Flags(), 
                    aEntry.iTexture->Id());
                }       
            else
                {
                // for PC lint
                }
            }
        }
    else
        {
        iData->iEnv->Client().TextureRestore(aEntry.iTexture->Id(), 
            iData->iManagerId.iUid);    
        }        
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::CancelLoadingOfTexture(CAlfTexture& aTexture)
    {
    RArray<TLoadQueueEntry>& loadQueue = iData->iLoadQueue;    
    for(TInt i = 0; i < loadQueue.Count(); i++)
        {
        if(loadQueue[i].iLoading.iTexture == &aTexture)
            {
            // Texture found from the load queue.
            if(i == 0 && iData->iState == ELoading)
                {
                // Texture is currently loading
                Cancel();
                PopLoadedQueueEntry();
                StartLoading();                
                }
            else
                {
                // Delete the decoder and remove entry.
                delete loadQueue[i].iDecoder;
                loadQueue.Remove(i);
                }
            break;
            }
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TInt CAlfTextureManager::GenerateTextureId()
    {
    TBool done = EFalse;

    TInt start = iData->iAutoGeneratedTextureId;
    while (!done)
        {            
        if(iData->iAutoGeneratedTextureId == iData->iHighestAutoGeneratedTextureId)
            {
            iData->iAutoGeneratedTextureId = iData->iLowestAutoGeneratedTextureId;
            }
        else
            {
            iData->iAutoGeneratedTextureId++;
            }
        
        if (CheckTexture(iData->iAutoGeneratedTextureId) == KErrNotFound)
            {
            done = ETrue;    
            }
        
        if (iData->iAutoGeneratedTextureId == start)
            {
            // Error! Free id was not found, already existing id will be used.
            break;    
            }            
        }

    return iData->iAutoGeneratedTextureId;        
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::SetAutomaticTextureIdRange(TInt aLow, TInt aHigh)
    {
    if (aLow < aHigh)
        {
        iData->iAutoGeneratedTextureId = aLow;
        iData->iLowestAutoGeneratedTextureId = aLow;
        iData->iHighestAutoGeneratedTextureId = aHigh;                    
        }
    }
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::NotifySkinChangedL()
    {
    __ALFLOGSTRING( "CAlfTextureManager::NotifySkinChangedL" )
    
    RArray<TTextureEntry>& textures = (iData->iTextures);	
    for(TInt i = 0; i < textures.Count(); i++)
        {
        if (textures[i].iTexture->Id() &&
            textures[i].iTexture->HasContent() &&
            (textures[i].iTexture->Flags() & EAlfTextureFlagSkinContent))
            {
            iData->iEnv->Client().TextureNotifySkinChanged(textures[i].iTexture->Id(),
                 iData->iManagerId.iUid);    
            
            ReleaseEntry(textures[i]);            
            RestoreEntryL(textures[i]);
            }
        }                        
    }
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool CAlfTextureManager::IsShared()
    {
    return (iData->iManagerId != TUid::Uid(0));        
    }
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::ReportTextureInfoL( 
        TInt aTextureId, const TSize& aTextureRect )
    {
    __ALFLOGSTRING1( "CAlfTextureManager::ReportTextureInfo %d", aTextureId )
    __ALFLOGSTRING2( "texture size = (%d, %d)", aTextureRect.iWidth, aTextureRect.iHeight )

    TBool accepted = EFalse;
    CAlfTexture* texture = TextureL(aTextureId);
    
    RPointerArray<MAlfTextureAutoSizeObserver>& observers = 
        (iData->iAutoSizeObserverQueue);	
    TInt count = observers.Count();
    for(TInt i = 0; i < count; i++)
     	{
        accepted |= observers[i]->PreferredSizeChanged(*texture, aTextureRect);
        }

    // File based textures are updated automatically without observer acceptance
    if (texture->FileName())
        {
        // Update new max size, but leave undefined (zero) values untouched.
        TSize maxTextureSize = texture->MaxTextureSize();
        TSize oldMaxTextureSize = maxTextureSize;
        
        if (maxTextureSize.iWidth != 0 && maxTextureSize.iHeight == 0)
            {
            maxTextureSize.iWidth = aTextureRect.iWidth;    
            }
        else if (maxTextureSize.iHeight != 0 && maxTextureSize.iWidth == 0)
            {
            maxTextureSize.iHeight = aTextureRect.iHeight;      
            }
        else
            {
            maxTextureSize = aTextureRect;   
            }    
            
        texture->SetMaxTextureSize(maxTextureSize);
               
        if (!texture->ReleaseFlags() && (oldMaxTextureSize != maxTextureSize))
            {
            // Update if texture is not released. 
            UpdateTextureL(aTextureId, aTextureRect);             
            }
        }
    else
        {
        if (accepted && !texture->ReleaseFlags())
            {
            // If we did notify someone who accepted it, we assume it may now be able to 
            // provide new size texture. If it is released it should get updated 
            // automatically to new size when restore happens.   
            UpdateTextureL(aTextureId, aTextureRect);             
            }
        else
            {
            // not accepted by any of observers, no use to update then.    
            }    
        }                

    }
    

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::AddAutoSizeObserverL(MAlfTextureAutoSizeObserver* aObserver)
    {
    iData->iAutoSizeObserverQueue.Append(aObserver);     
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
EXPORT_C void CAlfTextureManager::RemoveAutoSizeObserver(MAlfTextureAutoSizeObserver* aObserver)
    {
    RPointerArray<MAlfTextureAutoSizeObserver>& observers = 
            (iData->iAutoSizeObserverQueue);	

    TInt count = observers.Count();
    for(TInt i = 0; i < count; i++)
      	{
    	if (observers[i] == aObserver)
    		{
    		observers.Remove(i);
    		observers.Compress();
    		break;
    		}								                       
        }        
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::UpdateTextureL(TInt aId, TSize /*aSize*/)
    {
    RArray<TTextureEntry>& textures = (iData->iTextures);	
    for(TInt i = 0; i < textures.Count(); i++)
        {
        TTextureEntry te = textures[i];
        if (te.iTexture && te.iTexture->Id() == aId)
            {
            if (te.iTexture->BitmapProvider() != NULL)
                {
                UpdateTextureFromBitmapL(aId);
                break;
                }
            else if (te.iTexture->FileName() != NULL)
                {
                UpdateTextureFromFileL(aId);
                break;
                }       
            else
                {
                // for PC lint
                break;
                }
            }
        }
    }


void CAlfTextureManager::ReloadTextureL(const TDesC& aImageName,
                                       TSize aTextureMaxSize,
                                       TAlfTextureFlags /*aFlags*/,
                                       TInt aId)
    {    
    if (aId == KAlfAutoGeneratedTextureId)
        {
        aId = GenerateTextureId();    
        }    
    
    CAlfTexture* tex = NULL;
    TFileName fileName;

    // Texture must exist.
    tex = TextureL(aId);

    // add path to filename if filename has been passed
    if(aImageName.Length() > 0)
        {
        // assume relative pathname and prepend the image path to get full filename
        fileName = aImageName;
        PrependImagePath(fileName);
        }

    // if no name has been passed as a parameter but
    // there's and nonzero id has been passed, we
    // can assume that there's a predefined name available
    RArray<TTextureEntry>& textures = (iData->iTextures);	
    RArray<TLoadQueueEntry>& loadqueue = (iData->iLoadQueue);	

    if((aImageName.Length() == 0) && (aId != 0))
       {
        // search for a texture filename based on the id.
        TInt index = CheckTexture(aId);
        if(index >=0)
            {
            tex = textures[index].iTexture;
            fileName = *tex->FileName();
            }
        else
            {
            User::Leave(KErrNotFound);
            }
       }

    // return if already in the load queue ??
   if (IsInLoadQueue(tex))
       {
       return;
       }

    // replace filename
    // File names are relative to the image path.
    tex->SetFileNameL(fileName);
    
    TSize maxTextureSize = tex->MaxTextureSize();
    
    // Update new max size, but leave undefined (zero) values untouched    
    if (maxTextureSize.iWidth != 0 && maxTextureSize.iHeight == 0)
        {
        maxTextureSize.iWidth = aTextureMaxSize.iWidth;    
        }
    else if (maxTextureSize.iHeight != 0 && maxTextureSize.iWidth == 0)
        {
        maxTextureSize.iHeight = aTextureMaxSize.iHeight;      
        }
    else
        {
        maxTextureSize = aTextureMaxSize;   
        }    

    tex->SetMaxTextureSize(maxTextureSize);

    // Prepare for loading by creating a load queue entry.
    TTextureEntry entry;
    entry.iTexture = tex;
    entry.iId = aId;

    TLoadQueueEntry loadqentry;
    loadqentry.iUnloaded = EFalse;
    loadqentry.iLoading = entry;
    loadqentry.iDecoder = NULL;

    // Textures are loaded one at a time, in the order they were requested.
    loadqueue.AppendL(loadqentry);

    // DEBUG
    if (iData->iTextureAutoCounterEnabled)
        {            
        iData->iTextureAutoReloadCounter++;
        }

    // Start the texture load active object if we're not loading
    if(iData->iState == EIdle)
        {
        // Start loading images..
        StartLoading();
        // Notify observers.
        NotifyStateChange();
        }
    }

void CAlfTextureManager::RecreateTextureL(TInt aId,
                                          MAlfBitmapProvider* aBitmapProvider,
                                          TAlfTextureFlags aFlags)
    {    
    CAlfTexture* tex = NULL;
    
    if (aId == KAlfAutoGeneratedTextureId)
        {
        User::Leave(KErrArgument); // texture must already exist !
        }    

    CFbsBitmap* bitmap = NULL;
    CFbsBitmap* maskBitmap = NULL;

    // Provide an already created texture if such exists.
    TRAPD(err, tex = TextureL(aId));
    if(err != KErrNone)
        {
        User::Leave(KErrArgument); // texture must already exist !
        }

    // Call the bitmapProvider method to load the bitmaps
	if (aBitmapProvider)
		{
	    aBitmapProvider->ProvideBitmapL(aId, bitmap, maskBitmap);		
		}
					
	if (!bitmap)
	    { 
	    // We leave here, otherwise serverside would panic. 
	    User::Leave(KErrArgument);    
	    // Bitmap size is checked and handled in serverside, 
	    // so that we can create empty textures (size = 0,0)
	    }
	    	  
	// "bitmap" pointer should be valid from now on
    CleanupStack::PushL(bitmap);
	TInt bitmapHandle = bitmap->Handle();
	
	TInt maskHandle = 0;
	if (maskBitmap)	
		{
		maskHandle = maskBitmap->Handle();	
	    CleanupStack::PushL(maskBitmap);
		}
    
    // Create CAlfTexture instance, this duplicates the bitmaps to server.
    // texture already exists but it does not have content, this creates content.
   	iData->iEnv->Client().TextureCreateL( aId, bitmapHandle, 
       	maskHandle, aFlags, iData->iManagerId.iUid );    	            	        
		
	tex->SetBitmapProvider(aBitmapProvider);	
    tex->SetSize(bitmap->SizeInPixels());
	
	// CAlfTexture has at this point duplicated bitmaps, can be deleted here.        
	if (maskBitmap)	
		{
	    CleanupStack::PopAndDestroy( maskBitmap );
	    maskBitmap = NULL;		
		}
    CleanupStack::PopAndDestroy( bitmap ); 
	bitmap = NULL;		
    
    // Notify observers
    NotifyTextureLoaded(*tex, aId, KErrNone);
    
    // DEBUG
    if (iData->iTextureAutoCounterEnabled)
        {            
        iData->iTextureAutoRecreateCounter++;
        TBuf<256> numBuf;
        numBuf.FillZ();
        numBuf.Append(_L("Reloads:"));
        numBuf.AppendNum(iData->iTextureAutoReloadCounter);
        numBuf.Append(_L(" - Recreates:"));
        numBuf.AppendNum(iData->iTextureAutoRecreateCounter);
        User::InfoPrint(numBuf);
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::IncRefcount(TInt aId)
    {
    // do not refcount blanktextures.
    if (!aId)
        {
        return;
        }

    TInt index = CheckTexture(aId);
    ASSERT(index!=KErrNotFound);
    TTextureEntry& te = iData->iTextures[index];
    if (te.iRefCount==-1)
        {
        te.iRefCount = 1;
        }
    else
        {
        te.iRefCount++;        
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::DecRefcount(TInt aId)
    {
    // do not refcount blanktextures.
    if (!aId)
        {
        return;
        }
        
    TInt index = CheckTexture(aId);
    if (index == KErrNotFound)
        {
        return;
        }
    
    TTextureEntry& te = iData->iTextures[index];
    if (te.iRefCount >0)
        {
        te.iRefCount--;        
        }
    if (!te.iRefCount)
        {
        // trigger texture cleanup when 
        // reference count hits zero
        StartGarbageCollect();
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TInt CAlfTextureManager::GarbageCollect(TAny* aPtr)
    {
    CAlfTextureManager* tm = ((CAlfTextureManager*)(aPtr));
	RArray<TTextureEntry>& textures = tm->iData->iTextures;	
    // Cleanup all textures which have refcount 0
    TInt count = textures.Count()-1;
    for (int index = count; index >=0; index--)
        {
        TTextureEntry& te = textures[index];
        // delete only textures that have enabled refcounting
        if (te.iTexture->RefCountingEnabled() && !te.iRefCount)
            {
            TInt action = te.iTexture->RefCountingAction();
            switch (action)
                {
                case CAlfTexture::ERefCountingActionUnload:
                    {
                    // Unload, but mark as released from ref counting 
                    TInt releaseFlags = te.iTexture->ReleaseFlags();
                    tm->UnloadTexture(te.iId);    
                    releaseFlags |= CAlfTexture::EReleaseFromRefCounting;
                    te.iTexture->SetReleaseFlags(releaseFlags);
                    break;
                    }
                case CAlfTexture::ERefCountingActionDelete:
                    {
                    delete te.iTexture;
                    te.iTexture = NULL;
                    break;        
                    }                    
                default:
                    {
                    // Do nothing
                    break;    
                    }                                        
                }            
            }
        }
    return EFalse;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::StartGarbageCollect()
    {
    CIdle* doombringer = iData->iDoomBringer;
    // check if garbagecollecting is already in progress
    // -> if so, just return
    if (doombringer->IsActive())
        {
        return;
        }
    doombringer->Start(TCallBack(GarbageCollect, this));
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::RemoveFlags(TAlfTextureFlags& aFlags, TInt aFlagsToBeRemoved)
    {
    ((TInt&)aFlags) &= ~aFlagsToBeRemoved;      
    }
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CAlfTextureManager::SetFlags(TAlfTextureFlags& aFlags, TInt aFlagsToBeSet)
    {
    ((TInt&)aFlags) |= aFlagsToBeSet;              
    }

