/* Copyright (c) 2009 The Khronos Group Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

/*! \ingroup wfc
 *  \file wfccontext.c
 *
 *  \brief SI Context handling
 */

#include <string.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <math.h>

#include <WF/wfc.h>
#include <EGL/egl.h>

#include "wfccontext.h"
#include "wfcpipeline.h"

#include "owfscreen.h"
#include "owfdisplaycontextgeneral.h"
#include "owfnotifications.h"


/*! maximum number of elements per scene */
#define MAX_ELEMENTS            512
/*! maximum number of scenes per context */
#define MAX_SCENES              3


#define CONTEXT_SCENE_POOL_SIZE     MAX_SCENES
#define CONTEXT_ELEMENT_POOL_SIZE   MAX_SCENES * MAX_ELEMENTS
#define CONTEXT_NODE_POOL_SIZE      2 * CONTEXT_ELEMENT_POOL_SIZE

/*! almost 2^31 */
#define MAX_DELAY               2100000000

/*! 15ms */
#define AUTO_COMPOSE_DELAY      15000
#define FIRST_CONTEXT_HANDLE    2000

#define WAIT_FOREVER            -1

#ifdef __cplusplus
extern "C" {
#endif

static WFCHandle                nextContextHandle = FIRST_CONTEXT_HANDLE;

typedef enum
{
    WFC_MESSAGE_NONE,
    WFC_MESSAGE_QUIT,
    WFC_MESSAGE_COMPOSE,
    WFC_MESSAGE_COMMIT,
    WFC_MESSAGE_FENCE_1_DISPLAY,
    WFC_MESSAGE_FENCE_2_SYNCOBJECT,
    WFC_MESSAGE_ACTIVATE,
    WFC_MESSAGE_DEACTIVATE,
    WFC_MESSAGE_START_COUNTDOWN,
    WFC_MESSAGE_CANCEL
} WFC_MESSAGES;

static void*
WFC_Context_ComposerThread(void* data);


/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
void WFC_CONTEXT_Ctor(void* self)
{
    self = self;
}

/*---------------------------------------------------------------------------*/
static WFCboolean
WFC_Context_CreateState(WFC_CONTEXT* context)
    {   /* Must be called late enough that scratch buffers can be mapped and hardware rotation capability queried */
    OWF_IMAGE_FORMAT        fInt,
                            fExt;
    WFCint                  stride = 0;
    OWF_ASSERT(context);

    DPRINT(("WFC_Context_CreateContextState"));

    owfNativeStreamGetHeader(context->stream,
                             NULL, NULL, &stride, &fExt, NULL);
    /* internal image used as intermediate target */
    fInt.pixelFormat    = OWF_IMAGE_ARGB_INTERNAL;
    fInt.linear         = fExt.linear;
    fInt.premultiplied  = fExt.premultiplied;
    fInt.rowPadding     = 1;
    
     
    if (context->type == WFC_CONTEXT_TYPE_ON_SCREEN)
        {
        /* The unrotated target buffer: Can't get real address without locking for writing!  NO STRIDE */
        context->state.unrotatedTargetImage=OWF_Image_Create(context->targetWidth,context->targetHeight,&fExt,context->scratchBuffer[2],0);
        /* The rotated version of the target buffer for hardware rotation
         * or a de-rotated version of the internal buffer into another scratch buffer for software rotation
         */ 
        if (OWF_Screen_Rotation_Supported(context->screenNumber))
            {   /* The rotated version of the target buffer for hardware rotation */
            context->state.rotatedTargetImage=OWF_Image_Create(context->targetHeight,context->targetWidth,&fExt,context->scratchBuffer[2],0);
            }
        else
            {   /* a de-rotated version of the internal buffer into another scratch buffer for software rotation */
            context->state.rotatedTargetImage=OWF_Image_Create(context->targetWidth,context->targetHeight,&fInt,context->scratchBuffer[1],0);
            }
        }
    else
        {
        /* The unrotated target buffer: Can't get real address without locking for writing!  STRIDE HONOURED */
        context->state.unrotatedTargetImage=OWF_Image_Create(context->targetWidth,context->targetHeight,&fExt,context->scratchBuffer[2],stride);
        /* a de-rotated version of the internal buffer into another scratch buffer for software rotation */
        context->state.rotatedTargetImage=OWF_Image_Create(context->targetWidth,context->targetHeight,&fInt,context->scratchBuffer[1],0);
        }
    /* The internal target buffer composed to for 0 and 180 degree rotation */
    context->state.unrotatedInternalTargetImage=OWF_Image_Create(context->targetWidth,context->targetHeight,&fInt,context->scratchBuffer[0],stride);
    /* The internal target buffer composed to for 90 and 270 degree rotation */
    context->state.rotatedInternalTargetImage=OWF_Image_Create(context->targetHeight,context->targetWidth,&fInt,context->scratchBuffer[0],stride);

    if (context->state.unrotatedTargetImage && context->state.rotatedTargetImage && context->state.unrotatedInternalTargetImage && context->state.rotatedInternalTargetImage)
        {
        return WFC_TRUE;
        }
    else
        {
        return WFC_FALSE;
        }
    }
/*---------------------------------------------------------------------------*/
static void
WFC_Context_DestroyState(WFC_CONTEXT* context)
    {
    /* The unrotated target buffer */ 
    OWF_Image_Destroy(context->state.unrotatedTargetImage);
    /* The rotated version of the target buffer for hardware rotation, 
     * or a de-rotated version of the internal buffer into another scratch buffer for software rotation
     */ 
    OWF_Image_Destroy(context->state.rotatedTargetImage);
    /* The internal target buffer composed to for 0 and 180 degree rotation */
    OWF_Image_Destroy(context->state.unrotatedInternalTargetImage);
    /* The internal target buffer composed to for 90 and 270 degree rotation */
    OWF_Image_Destroy(context->state.rotatedInternalTargetImage);
    
    }
/*---------------------------------------------------------------------------
 * Should only be accessed indirectly by calls to WFC_Device_DestroyContext or
 * WFC_Device_DestroyContexts
 *----------------------------------------------------------------------------*/
void WFC_CONTEXT_Dtor(void* self)
{
    OWFint                  ii = 0;
    WFC_CONTEXT*            context = NULL;

    OWF_ASSERT(self);
    DPRINT(("WFC_CONTEXT_Dtor(%p)", self));

    context = CONTEXT(self);

    OWF_ASSERT(context);
    
    WFC_Pipeline_DestroyState(context);
    WFC_Context_DestroyState(context);
    
    OWF_MessageQueue_Destroy(&context->composerQueue);

    /* make the stream destroyable */
    if (context->stream != OWF_INVALID_HANDLE)
        {
        owfNativeStreamSetProtectionFlag(context->stream, OWF_FALSE);
        owfNativeStreamDestroy(context->stream);
        }
    context->stream = OWF_INVALID_HANDLE;

    OWF_AttributeList_Destroy(&context->attributes);

    for (ii = 0; ii < SCRATCH_BUFFER_COUNT; ii++)
    {
        OWF_Image_FreeData(context->displayContext, &context->scratchBuffer[ii]);
        context->scratchBuffer[ii] = 0;
    }

    OWF_DisplayContext_Destroy(context->screenNumber, context->displayContext);
    context->displayContext = NULL;    

    OWF_Pool_Destroy(context->scenePool);
    OWF_Pool_Destroy(context->elementPool);
    OWF_Pool_Destroy(context->nodePool);

    OWF_Semaphore_Destroy(&context->compositionSemaphore);
    OWF_Semaphore_Destroy(&context->commitSemaphore);
    OWF_Mutex_Destroy(&context->updateFlagMutex);
    OWF_Mutex_Destroy(&context->sceneMutex);
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL void
WFC_Context_Shutdown(WFC_CONTEXT* context)
{
    OWF_ASSERT(context);
    DPRINT(("WFC_Context_Shutdown(context = %d)", context->handle));

    DPRINT(("Waiting for composer thread termination"));
    OWF_Message_Send(&context->composerQueue, WFC_MESSAGE_QUIT, 0);
    OWF_Thread_Join(context->composerThread, NULL);
    OWF_Thread_Destroy(context->composerThread);
    context->composerThread = NULL;

    if (context->device)
    {
        /* #4604: added guard condition */
        WFC_Device_DestroyContextElements(context->device, context);
        WFC_Device_DestroyContextImageProviders(context->device, context);
    }

    WFC_Scene_Destroy(context->workScene);
    WFC_Scene_Destroy(context->snapshotScene);
    WFC_Scene_Destroy(context->committedScene);
    context->workScene = NULL;
    context->snapshotScene = NULL;
    context->committedScene = NULL;    
}

/*---------------------------------------------------------------------------
 *  Initialize context attributes
 *----------------------------------------------------------------------------*/
OWF_API_CALL OWF_ATTRIBUTE_LIST_STATUS
WFC_Context_InitializeAttributes(WFC_CONTEXT* context,
                                 WFCContextType type)
{
    OWF_ATTRIBUTE_LIST_STATUS attribError=ATTR_ERROR_NONE;
    OWF_ASSERT(context);
    /* initialize attributes/properties */
    if (context->stream)
    {
        owfNativeStreamGetHeader(context->stream,
                                 &context->targetWidth,
                                 &context->targetHeight,
                                 NULL, NULL, NULL);
    }
    context->type               = type;
    context->rotation           = WFC_ROTATION_0;
    context->backgroundColor    = 0x000000FF;
    context->lowestElement      = WFC_INVALID_HANDLE;


    OWF_AttributeList_Create(&context->attributes,
                             WFC_CONTEXT_TYPE,
                             WFC_CONTEXT_BG_COLOR);
    attribError=OWF_AttributeList_GetError(&context->attributes);
    if (attribError!=ATTR_ERROR_NONE)
        {
        OWF_ASSERT(attribError==ATTR_ERROR_NO_MEMORY);
        return attribError;
        }

  
    /* The composition code reads the member variables directly, 
     * not via the attribute engine.
     */
    OWF_Attribute_Initi(&context->attributes,
                        WFC_CONTEXT_TYPE,
                        (WFCint*) &context->type,
                        OWF_TRUE);

    OWF_Attribute_Initi(&context->attributes,
                        WFC_CONTEXT_TARGET_WIDTH,
                        &context->targetWidth,
                        OWF_TRUE);

    OWF_Attribute_Initi(&context->attributes,
                        WFC_CONTEXT_TARGET_HEIGHT,
                        &context->targetHeight,
                        OWF_TRUE);

    OWF_Attribute_Initi(&context->attributes,
                        WFC_CONTEXT_ROTATION,
                        (WFCint*) &context->rotation,
                        OWF_FALSE);

    OWF_Attribute_Initi(&context->attributes,
                        WFC_CONTEXT_BG_COLOR,
                        (WFCint*) &context->backgroundColor,
                        OWF_FALSE);

    OWF_Attribute_Initi(&context->attributes,
                        WFC_CONTEXT_LOWEST_ELEMENT,
                        (OWFint*) &context->lowestElement,
                        OWF_TRUE);
    attribError=OWF_AttributeList_GetError(&context->attributes);
	
	/* After commit to working, writable attribute abstracted variables
	must not be written to directly. */
    OWF_AttributeList_Commit(&context->attributes,
                             WFC_CONTEXT_TYPE,
                             WFC_CONTEXT_BG_COLOR,
		             WORKING_ATTR_VALUE_INDEX );
	
    return attribError;
}


/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
static WFC_CONTEXT*
WFC_Context_Initialize(WFC_CONTEXT* context,
                       WFC_DEVICE* device,
                       WFCNativeStreamType stream,
                       WFCContextType type,
                       WFCint screenNum)
{
    void*                   scratch[SCRATCH_BUFFER_COUNT];
    OWFint                  err2    = 0;
    OWFint                  ii      = 0,
                            nbufs   = 0;
    OWFint                  fail    = 0;
    OWF_ATTRIBUTE_LIST_STATUS attribStatus = ATTR_ERROR_NONE;
    OWF_ASSERT(context);
    OWF_ASSERT(device);

    DPRINT(("WFC_Context_Initialize(%p,%p,%d,%d)", context, device, type, screenNum));

    context->type               = type;
    context->device             = device;
    context->handle             = nextContextHandle;
    context->screenNumber       = screenNum;
    context->activationState    = WFC_CONTEXT_STATE_PASSIVE;
    context->sourceUpdateCount  = 0;
    context->clientElementCount = 0;
    ++nextContextHandle;

    context->displayContext = OWF_DisplayContext_Create(screenNum);
    if (context->displayContext == NULL)
        {
        DPRINT(("WFC_Context_Initialize(): Could not create display Context"));
        return NULL;
        }
    
    context->eglPrivateSignalSync = (TFPtrEglPrivateSignalSyncNok) eglGetProcAddress("egl_Private_SignalSyncNOK");
    if (! context->eglPrivateSignalSync)
        {
        DPRINT(("WFC_Context_Initialize(): Missing EGL extension - egl_Private_SignalSyncNOK extension"));   
        return NULL;
        }

     /*the following section of the code could be pushed to adaptation in future*/
    if (type == WFC_CONTEXT_TYPE_ON_SCREEN)
    {
        OWF_IMAGE_FORMAT        imageFormat;
        OWF_SCREEN              screen;
        WFCint width = 0;
        WFCint height = 0;
        WFCint normalSize = 0;
        WFCint flippedSize = 0;
        WFCNativeStreamType stream;
    
        /* Set up stream for sending data to screen */
        
        if (!OWF_Screen_GetHeader(screenNum, &screen))
        {
            DPRINT(("WFC_Context_Initialize(): Could not retrieve the screen parameters"));
            return NULL;
        }

        /* Set on-screen pixel format */
        imageFormat.pixelFormat     = OWF_SURFACE_PIXEL_FORMAT;
        imageFormat.premultiplied   = OWF_SURFACE_PREMULTIPLIED;
        imageFormat.linear          = OWF_SURFACE_LINEAR;
        imageFormat.rowPadding      = OWF_SURFACE_ROWPADDING;

        width = screen.normal.width;
        height = screen.normal.height;
        
        normalSize = screen.normal.height * screen.normal.stride;
        flippedSize = screen.flipped.height * screen.flipped.stride;
        
        if (flippedSize > normalSize)
            {
            width = screen.flipped.width;
            height = screen.flipped.height;
            }
        
        stream = owfNativeStreamCreateImageStream(width,
                                                  height,
                                                  &imageFormat,
                                                  1);

        if (stream)
        {
            WFC_Context_SetTargetStream(context, stream);
            
            /* At this point the stream's refcount is 2 - we must decrement
             * it by one to ensure that the stream is destroyed when the
             * context (that "owns" it) is destroyed.
             */
            owfNativeStreamRemoveReference(stream);
        }
        else
        {
            DPRINT(("WFC_Context_Initialize(): cannot create internal target stream"));
            return NULL;
        }
    }
    else
    {
        WFC_Context_SetTargetStream(context, stream);
    }
    
    nbufs = SCRATCH_BUFFER_COUNT-1;
    for (ii = 0; ii < nbufs; ii++)
    {
        scratch[ii] = OWF_Image_AllocData(context->displayContext, MAX_SOURCE_WIDTH,
                MAX_SOURCE_HEIGHT,
                OWF_IMAGE_ARGB_INTERNAL);
        fail = fail || (scratch[ii] == NULL);
    }

    /*
     * allocate one-channel buffer for alpha
     * obs! this assumes sizeof(OWFsubpixel) is 4.
     */
    scratch[nbufs] = OWF_Image_AllocData(context->displayContext, MAX_SOURCE_WIDTH,
                                         MAX_SOURCE_HEIGHT,
                                         OWF_IMAGE_L32);
    fail = fail || (scratch[nbufs] == NULL);
    
    err2 = OWF_MessageQueue_Init(&context->composerQueue);
    fail = fail || (err2 != 0);

    if (fail)
    {
        OWF_MessageQueue_Destroy(&context->composerQueue);

        for (ii = 0; ii < SCRATCH_BUFFER_COUNT; ii++)
        {
            OWF_Image_FreeData(context->displayContext, &scratch[ii]);
        }
        return NULL;
    }

    for (ii = 0; ii < SCRATCH_BUFFER_COUNT; ii++)
    {
        context->scratchBuffer[ii] = scratch[ii];
    }

    if (!WFC_Pipeline_CreateState(context) || !WFC_Context_CreateState(context))
         {
         DPRINT(("WFC_Context_Initialize(): Could not create pipeline state object"));
         return NULL;
         }
    if (    OWF_Semaphore_Init(&context->compositionSemaphore, 1)
        ||  OWF_Semaphore_Init(&context->commitSemaphore, 1)
        ||  OWF_Mutex_Init(&context->updateFlagMutex)
        ||  OWF_Mutex_Init(&context->sceneMutex)
        
        )
        {
        DPRINT(("WFC_Context_Initialize(): Could not create mutexes and semaphores!"));
        return NULL;
        }

    attribStatus= WFC_Context_InitializeAttributes(context, type);

    if (attribStatus!=ATTR_ERROR_NONE)
        {
        return NULL;
        }
    
    
    context->scenePool = OWF_Pool_Create(sizeof(WFC_SCENE),
                                         CONTEXT_SCENE_POOL_SIZE);
    context->elementPool = OWF_Pool_Create(sizeof(WFC_ELEMENT),
                                           CONTEXT_ELEMENT_POOL_SIZE);
    context->nodePool = OWF_Pool_Create(sizeof(OWF_NODE),
                                        CONTEXT_NODE_POOL_SIZE);
    if (!( context->scenePool &&
          context->nodePool && context->elementPool))
    {
        /* must call these to remove references to context */
        context->workScene = NULL;
        context->committedScene = NULL;
        return NULL;
    }

    DPRINT(("  Creating scenes"));
    context->workScene = WFC_Scene_Create(context);
    context->committedScene = WFC_Scene_Create(context);
    context->snapshotScene = NULL;

    /* snapshotScene is initialized in InvokeCommit */

    /* context's refcount is now 3 */

    if (!(context->workScene && context->committedScene &&
          context->nodePool && context->elementPool))
    {
        /* must call these to remove references to context */
        WFC_Scene_Destroy(context->workScene);
        WFC_Scene_Destroy(context->committedScene);
        context->workScene = NULL;
        context->committedScene = NULL;
        return NULL;
    }


    context->composerThread = OWF_Thread_Create(WFC_Context_ComposerThread,
                                                context);
    if (!(context->composerThread))
        {
        /* must call these to remove references to context */
        WFC_Scene_Destroy(context->workScene);
        WFC_Scene_Destroy(context->committedScene);
        context->workScene = NULL;
        context->committedScene = NULL;
        return NULL;
        }
    
    return context;
}

/*---------------------------------------------------------------------------
 *  Create new context on device
 *
 *  \param device Device on which the context should be created
 *  \param type Context type (on- or off-screen)
 *
 *  \return New context object or NULL in case of failure
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFC_CONTEXT*
WFC_Context_Create(WFC_DEVICE* device,
                   WFCNativeStreamType stream,
                   WFCContextType type,
                   WFCint screenNum)
{
    WFC_CONTEXT*            context = NULL;

    OWF_ASSERT(device);
   context = CREATE(WFC_CONTEXT);

    if (context)
    {
        if (!WFC_Context_Initialize(context, device, stream, type, screenNum))
        {
            DESTROY(context);
        }
    }
    return context;
}

/*---------------------------------------------------------------------------
 *  Setup context rendering target
 *
 *  \param context Context
 *  \param stream Target stream to use for rendering
 *----------------------------------------------------------------------------*/
OWF_API_CALL void
WFC_Context_SetTargetStream(WFC_CONTEXT* context,
                            OWFNativeStreamType stream)
{
    OWF_ASSERT(context);
    context->stream = stream;

    owfNativeStreamAddReference(stream);

    owfNativeStreamGetHeader(stream,
                             &context->targetWidth, &context->targetHeight,
                             NULL, NULL, NULL);
}
/*---------------------------------------------------------------------------
 * Checks if the given stream would be valid as an off-screen context target.
 * 
 * Checks that the format can be rendered TO.
 * Also checks that the image size is acceptable (within the scratch buffers).
 * This is called before the context is created so is effectively a "static" context method.
 * 
 *  \param stream Target stream to use for rendering
 ---------------------------------------------------------------------------*/

OWF_API_CALL WFCboolean
WFC_Context_IsValidTargetStream(OWFNativeStreamType stream)
{
    OWFint      width,height;
    OWF_IMAGE_FORMAT    format;
    owfNativeStreamGetHeader(stream,
                         &width, &height,
                         NULL, &format, NULL);
    if (width>MAX_SOURCE_WIDTH)
        {
        return WFC_FALSE;
        }
    if (height>MAX_SOURCE_HEIGHT)
        {
        return WFC_FALSE;
        }
    return (WFCboolean)OWF_Image_IsValidDestinationFormat(&format);
}

/*---------------------------------------------------------------------------
 * Checks that the image size is acceptable (within the scratch buffers).
 * This is called before the source is created.
 * 
 *  \param stream source stream to use for rendering.
 ---------------------------------------------------------------------------*/

OWF_API_CALL WFCboolean
WFC_Context_IsValidSourceStream(OWFNativeStreamType stream)
{
    OWFint      width,height;
    owfNativeStreamGetHeader(stream,
                         &width, &height,
                         NULL, NULL, NULL);
    if ((width+2) * (height+2) > MAX_SOURCE_WIDTH * MAX_SOURCE_HEIGHT)
        {
        return WFC_FALSE;
        }
    return WFC_TRUE;
}

/*---------------------------------------------------------------------------
 *  Find element from current scene
 *
 *  \param context Context object
 *  \param element Element to find
 *
 *  \return Element object or NULL if element hasn't been inserted
 *  into current scene.
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFC_ELEMENT*
WFC_Context_FindElement(WFC_CONTEXT* context,
                        WFCElement element)
{
    OWF_ASSERT(context);
    return WFC_Scene_FindElement(context->workScene, element);
}

/*---------------------------------------------------------------------------
 *  Commit context scene graph changes
 *
 *  \param context Context to commit
 *----------------------------------------------------------------------------*/
static void
WFC_Context_DoCommit(WFC_CONTEXT* context)
{
    OWF_ASSERT(context);
    DPRINT(("WFC_Context_DoCommit(context = %p)", context));

    OWF_ASSERT(context->snapshotScene);


    DPRINT(("COMMIT: Committing attribute list changes"));

    DPRINT(("COMMIT: Acquiring mutex"));
    OWF_Mutex_Lock(&context->sceneMutex);

    /* comitting scene attribute changes */
    DPRINT(("COMMIT: Committing scene attribute changes"));
    OWF_AttributeList_Commit(&context->attributes,
                             WFC_CONTEXT_TYPE,
                             WFC_CONTEXT_BG_COLOR,COMMITTED_ATTR_VALUE_INDEX);


    /* resolve sources and masks */
    DPRINT(("COMMIT: Committing scene changes"));
    WFC_Scene_Commit(context->snapshotScene);
    DPRINT(("COMMIT: Destroying old committed scene"));
    WFC_Scene_Destroy(context->committedScene);
    DPRINT(("COMMIT: Setting new snapshot scene as committed one."));
    context->committedScene = context->snapshotScene;
    context->snapshotScene = NULL;

    // reset the visibility flags
    owfSymDeviceResetVisibilityState(context);
    
    DPRINT(("COMMIT: Unlocking mutex"));
    OWF_Mutex_Unlock(&context->sceneMutex);

    DPRINT(("COMMIT: Signaling commit semaphore"));
    /* signal we're ready */
    OWF_Semaphore_Post(&context->commitSemaphore);
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
static void
WFC_Context_LockTargetForWriting(WFC_CONTEXT* context)
{
    OWF_ASSERT(context);

    DPRINT(("WFC_Context_LockTargetForWriting"));

    context->state.targetBuffer =
        owfNativeStreamAcquireWriteBuffer(context->stream);
    context->state.targetPixels =
        owfNativeStreamGetBufferPtr(context->stream,
                                    context->state.targetBuffer);

    if ((WFC_ROTATION_0   == context->rotation || WFC_ROTATION_180 == context->rotation) ||
        !OWF_Screen_Rotation_Supported(context->screenNumber))
    {
        /* final target, in target format */
        context->state.targetImage =context->state.unrotatedTargetImage;
    }
    else
    {
        /* final target, in target format */
        /* fExt stride/padding value is the rotated value */
        context->state.targetImage = context->state.rotatedTargetImage;
    }
    OWF_Image_SetPixelBuffer(context->state.targetImage,context->state.targetPixels);
    
    if (context->state.targetImage==NULL)
        {
        OWF_ASSERT(WFC_FALSE);
        }

    /* take context rotation into account. */
    if (WFC_ROTATION_0   == context->rotation ||
        WFC_ROTATION_180 == context->rotation)
    {
        context->state.internalTargetImage = context->state.unrotatedInternalTargetImage;
    }
    else
    {
        context->state.internalTargetImage = context->state.rotatedInternalTargetImage;
    }

    if (context->state.internalTargetImage==NULL)
        {
        OWF_ASSERT(WFC_FALSE);
        }
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
static void
WFC_Context_UnlockTarget(WFC_CONTEXT* context)
{
    OWFNativeStreamBuffer   frontBuffer = OWF_INVALID_HANDLE;
    void*                   pixelDataPtr = NULL;
    OWF_ROTATION rotation = OWF_ROTATION_0;

    OWF_ASSERT(context);
    DPRINT(("WFC_Context_UnlockTarget"));
    DPRINT(("  Unlocking target stream=%d, buffer=%d",
            context->stream, context->state.targetBuffer));

    owfNativeStreamReleaseWriteBuffer(context->stream,
                                      context->state.targetBuffer,
                                      EGL_NO_DISPLAY,
                                      NULL);

    
    /* Refactor the code that follows so that it is triggered by the above releasewrite */
    
    /* Acquire target stream front buffer and blit to SDL screen */
    frontBuffer = owfNativeStreamAcquireReadBuffer(context->stream);
    DPRINT(("  Locking target stream=%d, buffer=%d",
            context->stream, frontBuffer));

    pixelDataPtr = owfNativeStreamGetBufferPtr(context->stream, frontBuffer);

    switch (context->rotation)
    {
        case WFC_ROTATION_0:
        {
            rotation = OWF_ROTATION_0;
            break;
        }
        case WFC_ROTATION_90:
        {
            rotation = OWF_ROTATION_90;
            break;
        }
        case WFC_ROTATION_180:
        {
            rotation = OWF_ROTATION_180;
            break;
        }
        case WFC_ROTATION_270:
        {
            rotation = OWF_ROTATION_270;
            break;
        }
        default:
        {
            OWF_ASSERT(0);
        }
    }
    
    OWF_Screen_Blit(context->screenNumber, pixelDataPtr, rotation);

    owfNativeStreamReleaseReadBuffer(context->stream, frontBuffer);
    DPRINT(("  Releasing target stream=%d, buffer=%d",
            context->stream, frontBuffer));

}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
static void
WFC_Context_PrepareComposition(WFC_CONTEXT* context)
{
    OWFsubpixel             r, g, b, a;

    OWF_ASSERT(context);

    /* the first thing to do is to lock target stream and fetch
       write buffer to it. fetching blocks until one is available,
       but since only one stream can be target to only one context
       at the time, no stalls should occur */
    WFC_Context_LockTargetForWriting(context);

    /* prepare for composition by "clearing the table" with
       background color.  */

    r = (OWFsubpixel) OWF_RED_MAX_VALUE * ((context->backgroundColor >> 24) &
        0xFF) / OWF_BYTE_MAX_VALUE;
    g = (OWFsubpixel) OWF_GREEN_MAX_VALUE * ((context->backgroundColor >> 16) &
        0xFF) / OWF_BYTE_MAX_VALUE;
    b = (OWFsubpixel) OWF_BLUE_MAX_VALUE * ((context->backgroundColor >> 8) &
        0xFF) / OWF_BYTE_MAX_VALUE;
    a = (OWFsubpixel) OWF_ALPHA_MAX_VALUE * (context->backgroundColor & 0xFF) /
        OWF_BYTE_MAX_VALUE;

    r = r * a / OWF_ALPHA_MAX_VALUE;
    g = g * a / OWF_ALPHA_MAX_VALUE;
    b = b * a / OWF_ALPHA_MAX_VALUE;

    OWF_Image_Clear(context->state.internalTargetImage, r, g, b, a);

    WFC_Scene_LockSourcesAndMasks(context->committedScene);
}



/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
static void
WFC_Context_FinishComposition(WFC_CONTEXT* context)
{
    OWF_ROTATION            rotation = OWF_ROTATION_0;
    OWFint                  screenNumber;
    OWFboolean              screenRotation;

    OWF_ASSERT(context);

    screenNumber = context->screenNumber;
    screenRotation = OWF_Screen_Rotation_Supported(screenNumber);
    /* re-use scratch buffer 1 for context rotation */
    if (WFC_ROTATION_0   == context->rotation || screenRotation)
    {
 
        if (screenRotation)
        {
            if (WFC_ROTATION_90   == context->rotation || WFC_ROTATION_270   == context->rotation)
                {
                owfSetStreamFlipState(context->stream, OWF_TRUE);
                }
            else
                {
                owfSetStreamFlipState(context->stream, OWF_FALSE);
                }
        }
        OWF_Image_DestinationFormatConversion(context->state.targetImage, context->state.internalTargetImage);
    }
    else
    {
        switch (context->rotation)
        {
            case WFC_ROTATION_0:
            {
                rotation = OWF_ROTATION_0;
                break;
            }
            case WFC_ROTATION_90:
            {
                rotation = OWF_ROTATION_90;
                break;
            }
            case WFC_ROTATION_180:
            {
                rotation = OWF_ROTATION_180;
                break;
            }
            case WFC_ROTATION_270:
            {
                rotation = OWF_ROTATION_270;
                break;
            }
            default:
            {
                OWF_ASSERT(0);
            }
        }
     
        /* rotate */
        OWF_Image_Rotate(context->state.rotatedTargetImage,
                         context->state.internalTargetImage,
                         rotation);

        /* Note: support of different target formats  can be put here */

        OWF_Image_DestinationFormatConversion(context->state.targetImage, context->state.rotatedTargetImage);
    }
    WFC_Context_UnlockTarget(context);
    WFC_Scene_UnlockSourcesAndMasks(context->committedScene);
}

/*!---------------------------------------------------------------------------
 * \brief Actual composition routine.
 *  Mainly just calls other functions that executes different stages of
 *  the composition pipeline.
 *  \param context Context to compose.
 *----------------------------------------------------------------------------*/
static void
WFC_Context_DoCompose(WFC_CONTEXT* context)
{
    WFC_SCENE*              scene   = NULL;
    OWF_NODE*               node    = NULL;

    OWF_ASSERT(context);

    OWF_Mutex_Lock(&context->updateFlagMutex);

    OWF_DisplayContext_IncrementSerialNumber(context->displayContext);
    
    OWF_DisplayContext_SetCompositionOngoing(context->displayContext, WFC_TRUE);
    context->sourceUpdateCount = 0;
    OWF_Mutex_Unlock(&context->updateFlagMutex);
    
    WFC_Context_PrepareComposition(context);

    DPRINT(("WFC_Context_Compose"));
    /* Composition always uses the committed version
     * of the scene.
     */

    OWF_Mutex_Lock(&context->sceneMutex);
    
    scene = context->committedScene;
    OWF_ASSERT(scene);

    for (node = scene->elements; NULL != node; node = node->next)
    {
        
        WFC_ELEMENT*            element = NULL;
        WFC_ELEMENT_STATE*      elementState = NULL;
        element = ELEMENT(node->data);

        if (element->skipCompose)
        {
             /* this element is somehow degraded, its source is missing or
              * something else; skip to next element */
            DPRINT(("  *** Skipping element %d", element->handle));
            continue;
        }

        DPRINT(("  Composing element %d", element->handle));

        /* BeginComposition may fail e.g. if the element's destination
         * rectangle is something bizarre, i.e. causes overflows or
         * something.
         */
        if ((elementState=WFC_Pipeline_BeginComposition(context, element))!=NULL)
        {
            owfSymElementNotifications(context, element);
            
            WFC_Pipeline_ExecuteSourceConversionStage(context, elementState);
            WFC_Pipeline_ExecuteCropStage(context, elementState);
            WFC_Pipeline_ExecuteFlipStage(context, elementState);
            WFC_Pipeline_ExecuteRotationStage(context, elementState);
            WFC_Pipeline_ExecuteScalingStage(context, elementState);
            WFC_Pipeline_ExecuteBlendingStage(context, elementState);
                
            WFC_Pipeline_EndComposition(context, element,elementState);
        }
    }

    WFC_Context_FinishComposition(context);
    
    owfSymProcessAllNotifications(context);
    OWF_Mutex_Unlock(&context->sceneMutex);
    
    OWF_Semaphore_Post(&context->compositionSemaphore);
}

/*---------------------------------------------------------------------------
 *  Activate/deactivate auto-composition on context
 *
 *  \param context Context
 *  \param act Auto-composition enable/disable
 *----------------------------------------------------------------------------*/
OWF_API_CALL void
WFC_Context_Activate(WFC_CONTEXT* context,
                     WFCboolean act)
{
    OWF_ASSERT(context);

    DPRINT(("WFC_Context_Activate: %s", (act) ? "activate" : "deactivate"));

    if (act && !WFC_Context_Active(context))
    {
        DPRINT(("WFC_Context_Activate: WFC_CONTEXT_STATE_PASSIVE: activating"));
        context->activationState = WFC_CONTEXT_STATE_ACTIVATING;

        /* moved from composer loop - updates must be allowed
         * immediately after activation
         */
        WFC_Device_EnableContentNotifications(context->device,
                                              context,
                                              WFC_TRUE);

        OWF_Message_Send(&context->composerQueue,
                         WFC_MESSAGE_ACTIVATE,
                         0);
    }
    else if (!act && WFC_Context_Active(context))
    {
        DPRINT(("WFC_Context_Activate: WFC_CONTEXT_STATE_ACTIVE: deactivating"));
        context->activationState = WFC_CONTEXT_STATE_DEACTIVATING;
        OWF_Message_Send(&context->composerQueue,
                         WFC_MESSAGE_DEACTIVATE,
                         0);
    }
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCboolean
WFC_Context_InvokeComposition(WFC_DEVICE* device,
                              WFC_CONTEXT* context,
                              WFCboolean wait)
{
    WFCint              status = 0;

    OWF_ASSERT(context);
    OWF_ASSERT(device);

    device = device; /* suppress the compiler warning */

    status = OWF_Semaphore_TryWait(&context->compositionSemaphore);
    if (status)
    {
        if (!wait)
        {
            /* busy; processing last request */
            return WFC_FALSE;
        }
        /* wait previous frame composition to finish */
        OWF_Semaphore_Wait(&context->compositionSemaphore);
    }

    /* compositionSemaphore is posted/signaled in WFC_Context_Compose()
    after frame has been successfully composed */
    OWF_Message_Send(&context->composerQueue, WFC_MESSAGE_COMPOSE, 0);

    return WFC_TRUE;
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCErrorCode
WFC_Context_InvokeCommit(WFC_DEVICE* device,
                         WFC_CONTEXT* context,
                         WFCboolean wait)
{
    WFCint              status = 0;

    OWF_ASSERT(context);
    OWF_ASSERT(device);

    device = device; /* suppress the compiler warning */

    /* first check if there're inconsistensies in the scene */
    if (WFC_Scene_HasConflicts(context->workScene))
    {
        DPRINT(("WFC_Context_InvokeCommit: scene has inconsistensies"));
        return WFC_ERROR_INCONSISTENCY;
    }

    /* then commit - always asynchronously */
    status = OWF_Semaphore_TryWait(&context->commitSemaphore);
    DPRINT(("COMMIT: Commit semaphore status = %d", status));
    if (status)
    {
        if (!wait)
        {
            DPRINT(("COMMIT: Busy; exiting."));
            /* busy; processing last commit */
            return WFC_ERROR_BUSY;
        }

        DPRINT(("COMMIT: Waiting for previous commit to finish."));
        /* wait previous commit to finish */
        OWF_Semaphore_Wait(&context->commitSemaphore);
    }

    /* comitting scene attribute changes */
    DPRINT(("COMMIT: Cloning scene attribute changes"));
    OWF_AttributeList_Commit(&context->attributes,
                             WFC_CONTEXT_TYPE,
                             WFC_CONTEXT_BG_COLOR,SNAPSHOT_ATTR_VALUE_INDEX);

    DPRINT(("COMMIT: Cloning scene"));
    /* take snapshot of the current working copy - it will
     * be the new committed scene */
    context->snapshotScene = WFC_Scene_Clone(context->workScene);

    DPRINT(("COMMIT: Sending commit request"));
    /* invoke async commit */
    OWF_Message_Send(&context->composerQueue, WFC_MESSAGE_COMMIT, 0);
    return WFC_ERROR_NONE;
}

/*---------------------------------------------------------------------------
 *  \param context
 *  \param dpy
 *  \param sync
 *----------------------------------------------------------------------------*/
OWF_API_CALL void
WFC_Context_InsertFence(WFC_CONTEXT* context,
                        WFCEGLDisplay dpy,
                        WFCEGLSync sync)
{
    OWF_ASSERT(context);
    OWF_ASSERT(sync);

    DPRINT(("WFC_Context_InsertFence: Sending fence sync: 0x%08x", sync));
    
    OWF_Message_Send(&context->composerQueue, WFC_MESSAGE_FENCE_1_DISPLAY, (void*) dpy);
    OWF_Message_Send(&context->composerQueue, WFC_MESSAGE_FENCE_2_SYNCOBJECT, sync);
}

/*---------------------------------------------------------------------------
 *  Insert element into context's scene
 *
 *  \param context
 *  \param element
 *  \param subordinate
 *
 *  \return WFCErrorCode
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCErrorCode
WFC_Context_InsertElement(WFC_CONTEXT* context,
                          WFCElement element,
                          WFCElement subordinate)
{
    WFC_ELEMENT*            object = NULL;
    WFCErrorCode            result = WFC_ERROR_BAD_HANDLE;

    OWF_ASSERT(context);

    object = WFC_Device_FindElement(context->device, element);

    if (NULL != object && CONTEXT(object->context) == context)
    {
        /* set the sharing flag as the element will be
         * shared between the device and working copy scene.
         * this is to tell the scene that it must not destroy
         * this element.
         */
        object->shared = WFC_TRUE;
        result = WFC_Scene_InsertElement(context->workScene,
                                         object,
                                         subordinate);

        context->lowestElement = WFC_Scene_LowestElement(context->workScene);
    }
    return result;
}

/*---------------------------------------------------------------------------
 *  Remove element from context's scene
 *
 *  \param context
 *  \param element
 *
 *  \return WFCErrorCode
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCErrorCode
WFC_Context_RemoveElement(WFC_CONTEXT* context,
                          WFCElement element)
{
    WFCErrorCode            err = WFC_ERROR_BAD_HANDLE;
    WFC_ELEMENT*            elemento = NULL;

    OWF_ASSERT(context);

    elemento = WFC_Context_FindElement(context, element);

    if (elemento)
    {
        WFC_Scene_RemoveElement(context->workScene, element);
        /* the element is no longer shared, as it only resides
         * in device from this point on
         */
        elemento->shared = WFC_FALSE;
        context->lowestElement = WFC_Scene_LowestElement(context->workScene);

        err = WFC_ERROR_NONE;
    }

    return err;
}

/*!
 *  \brief IncreaseClientElementCount
 *
 *  \param context
 *  \return positive increased current element count; negative or zero indicating the count can't be increased
 */
OWF_API_CALL WFCint
WFC_Context_IncreaseClientElementCount(WFC_CONTEXT* context)
    {
    /* This implementation simply caps the number of elements allocated to the client
     * to 1/3 of the total elements.
     * A cleverer strategy would allow the client to create more elements 
     * so long as the number added to the scene *2 did not exceed the spare pool size.
     * These failure points are also a good place to consider increasing the pool size
     */
    if (context->clientElementCount>=MAX_ELEMENTS)
        {
        return -context->clientElementCount;
        }
    else
        {
        return ++context->clientElementCount;
        }
    }

/*!
 *  \brief DecreaseClientElementCount
 *
 *  \param context
 *  \return positive or zero decreased current element count; negative indicating the count can't be decreased - p0robably already zero
 */
OWF_API_CALL WFCint
WFC_Context_DecreaseClientElementCount(WFC_CONTEXT* context)
    {
    /* note that a negative return indicates that decrements are not matched with increments */ 
    return --context->clientElementCount;
    }

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCErrorCode
WFC_Context_GetElementAbove(WFC_CONTEXT* context,
                            WFCElement element,
                            WFCElement* result)
{
    WFC_ELEMENT*            object = NULL;
    WFCErrorCode            error = WFC_ERROR_ILLEGAL_ARGUMENT;

    OWF_ASSERT(context);
    OWF_ASSERT(result);

    object = WFC_Context_FindElement(context, element);

    if (object)
    {
        WFCElement          temp;

        temp = WFC_Scene_GetNeighbourElement(context->workScene, element, 1);
        *result = temp;
        error = WFC_ERROR_NONE;
    }
    return error;
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCErrorCode
WFC_Context_GetElementBelow(WFC_CONTEXT* context,
                            WFCElement element,
                            WFCElement* result)
{
    WFC_ELEMENT*            object = NULL;
    WFCErrorCode            error = WFC_ERROR_ILLEGAL_ARGUMENT;

    OWF_ASSERT(context);
    OWF_ASSERT(result);

    object = WFC_Context_FindElement(context, element);
    if (object)
    {
        WFCElement          temp;

        temp = WFC_Scene_GetNeighbourElement(context->workScene, element, -1);
        *result = temp;
        error = WFC_ERROR_NONE;
    }
    return error;
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCErrorCode
WFC_Context_GetAttribi(WFC_CONTEXT* context,
                       WFCContextAttrib attrib,
                       WFCint* value)
{
    WFCint                  temp = 0;
    OWF_ATTRIBUTE_LIST_STATUS   err;
    WFCErrorCode            result = WFC_ERROR_NONE;

    OWF_ASSERT(context);
    OWF_ASSERT(value);

    temp    = OWF_Attribute_GetValuei(&context->attributes, attrib);
    err     = OWF_AttributeList_GetError(&context->attributes);

    if (err != ATTR_ERROR_NONE)
    {
        result = WFC_ERROR_BAD_ATTRIBUTE;
    }
    else
    {
        *value = temp;
    }
    return result;
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCErrorCode
WFC_Context_SetAttribi(WFC_CONTEXT* context,
                       WFCContextAttrib attrib,
                       WFCint value)
{
    WFCErrorCode                result = WFC_ERROR_NONE;

    OWF_ASSERT(context);

    /* check value */
    switch (attrib)
    {
        case WFC_CONTEXT_BG_COLOR:
        {
            OWFint              alpha;

            /*
             *  Color format is RGBA NOT RGBA.
             */
            alpha = value & 0xFF;

            if (WFC_CONTEXT_TYPE_ON_SCREEN == context->type)
            {
                /* the only allowed value for on-screen context
                 * background alpha is 255 */
                if (alpha != 255)
                {
                    result = WFC_ERROR_ILLEGAL_ARGUMENT;
                }
            }
            break;
        }

        case WFC_CONTEXT_ROTATION:
        {
            if (!(WFC_ROTATION_0 == value ||
                  WFC_ROTATION_90 == value ||
                  WFC_ROTATION_180 == value ||
                  WFC_ROTATION_270 == value))
            {
               result = WFC_ERROR_ILLEGAL_ARGUMENT;
            }
            break;
        }

        case WFC_CONTEXT_TYPE:
        case WFC_CONTEXT_TARGET_HEIGHT:
        case WFC_CONTEXT_TARGET_WIDTH:
        case WFC_CONTEXT_LOWEST_ELEMENT:
        {
            result = WFC_ERROR_BAD_ATTRIBUTE;
            break;
        }

        case WFC_CONTEXT_FORCE_32BIT:
        default:
        {
            result = WFC_ERROR_BAD_ATTRIBUTE;
            break;
        }
    }

    if (WFC_ERROR_NONE == result)
    {
        OWF_ATTRIBUTE_LIST_STATUS   error;

        /* try changing the value */
        OWF_Attribute_SetValuei(&context->attributes, attrib, value);
        error = OWF_AttributeList_GetError(&context->attributes);

        /* transform errors */
        switch (error) {
            case ATTR_ERROR_ACCESS_DENIED:
            case ATTR_ERROR_INVALID_ATTRIBUTE:
            {
                result = WFC_ERROR_BAD_ATTRIBUTE;
                break;
            }
            default:
            {
                break;
            }
        }
    }

    return result;
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCErrorCode
WFC_Context_GetAttribfv(WFC_CONTEXT* context,
                        WFCContextAttrib attrib,
                        WFCint count,
                        WFCfloat* values)
{
    WFCErrorCode            result = WFC_ERROR_NONE;

    OWF_ASSERT(context);
    OWF_ASSERT(values);

    switch (attrib)
    {
        case WFC_CONTEXT_BG_COLOR:
        {
            if (4 != count)
            {
                result = WFC_ERROR_ILLEGAL_ARGUMENT;
            }
            else
            {
                OWFuint32      color;
                OWF_ATTRIBUTE_LIST_STATUS   err;

                color = OWF_Attribute_GetValuei(&context->attributes, attrib);
                err = OWF_AttributeList_GetError(&context->attributes);

                if (err != ATTR_ERROR_NONE)
                {
                    result = WFC_ERROR_BAD_ATTRIBUTE;
                    break;
                }

                /* extract color channels and convert to float */
                values[0] = (WFCfloat) (color >> 24) /
                            (WFCfloat) OWF_BYTE_MAX_VALUE;
                values[1] = (WFCfloat) ((color >> 16) & 0xFF) /
                            (WFCfloat) OWF_BYTE_MAX_VALUE;
                values[2] = (WFCfloat) ((color >> 8) & 0xFF) /
                            (WFCfloat) OWF_BYTE_MAX_VALUE;
                values[3] = (WFCfloat) (color & 0xFF) /
                            (WFCfloat) OWF_BYTE_MAX_VALUE;
            }
            break;
        }

        default:
        {
            result = WFC_ERROR_BAD_ATTRIBUTE;
            break;
        }
    }

    return result;
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCErrorCode
WFC_Context_SetAttribfv(WFC_CONTEXT* context,
                        WFCContextAttrib attrib,
                        WFCint count,
                        const WFCfloat* values)
{
    WFCErrorCode            result = WFC_ERROR_NONE;

    OWF_ASSERT(context);
    OWF_ASSERT(values);

    switch (attrib)
    {
        case WFC_CONTEXT_BG_COLOR:
        {
            if (4 != count)
            {
                result = WFC_ERROR_ILLEGAL_ARGUMENT;
            }
            else
            {
                OWFuint32    color;

                /* Every color component value must fall within range [0, 1] */
                if (INRANGE(values[0], 0.0f, 1.0f) &&
                    INRANGE(values[1], 0.0f, 1.0f) &&
                    INRANGE(values[2], 0.0f, 1.0f) &&
                    INRANGE(values[3], 0.0f, 1.0f))
                {
                    color = (((OWFuint32) floor(values[0] * 255)) << 24) |
                            (((OWFuint32) floor(values[1] * 255)) << 16) |
                            (((OWFuint32) floor(values[2] * 255)) << 8) |
                            ((OWFuint32) floor(values[3] * 255));

                    /* delegate to integer accessor - it'll check the
                     * the rest of the value and update it eventually  */
                    result = WFC_Context_SetAttribi(context, attrib, color);
                }
                else
                {
                    result = WFC_ERROR_ILLEGAL_ARGUMENT;
                }
            }
            break;
        }

        default:
        {
            result = WFC_ERROR_BAD_ATTRIBUTE;
            break;
        }
    }

    return result;
}


static void
WFC_Context_AutoComposer(WFC_CONTEXT* context)
{
    OWF_Mutex_Lock(&context->updateFlagMutex);
    if (context->sourceUpdateCount > 0)
    {
        DPRINT(("WFC_Context_ComposerThread: %d sources were updated, "
                "invoking composition\n",
                context->sourceUpdateCount));

        /* reset update counter */

        OWF_Mutex_Unlock(&context->updateFlagMutex);
        WFC_Context_DoCompose(context);
    }
    else
    {
        OWF_Mutex_Unlock(&context->updateFlagMutex);
    }
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
static void*
WFC_Context_ComposerThread(void* data)
{
    WFC_CONTEXT*            context = (WFC_CONTEXT*) data;
    OWF_MESSAGE             msg;


    OWF_ASSERT(context);
    DPRINT(("WFC_Context_ComposerThread starting"));

    memset(&msg, 0, sizeof(OWF_MESSAGE));

    while (context->device && msg.id != WFC_MESSAGE_QUIT)
    {
        OWFint              err = -1;

        if (WFC_CONTEXT_STATE_ACTIVE == context->activationState)
        {
            err = OWF_Message_Wait(&context->composerQueue,
                                   &msg,
                                   AUTO_COMPOSE_DELAY);

            WFC_Context_AutoComposer(context);
        }
        else
        {
            DPRINT(("  ComposerThread waiting for message"));
            err = OWF_Message_Wait(&context->composerQueue, &msg, WAIT_FOREVER);
        }

        if (0 == err)
        {
            switch (msg.id)
            {
                case WFC_MESSAGE_ACTIVATE:
                {
                    DPRINT(("****** ENABLING AUTO-COMPOSITION ******"));
                    context->activationState = WFC_CONTEXT_STATE_ACTIVE;
                    break;
                }

                case WFC_MESSAGE_DEACTIVATE:
                {
                    /* cancel possible countdown so that update won't occur
                     * after deactivation */
                    DPRINT(("****** DISABLING AUTO-COMPOSITION ******"));
                    WFC_Device_EnableContentNotifications(context->device,
                                                          context,
                                                          WFC_FALSE);
                    context->activationState = WFC_CONTEXT_STATE_PASSIVE;
                    break;
                }

                case WFC_MESSAGE_COMMIT:
                {
                    DPRINT(("****** COMMITTING SCENE CHANGES ******"));

                    DPRINT(("COMMIT: Invoking DoCommit"));
                    WFC_Context_DoCommit(context);

                    if (!WFC_Context_Active(context))
                    {
                        DPRINT(("COMMIT: Context is inactive, composition "
                                "not needed.", context->handle));
                        break;
                    }
                    else
                    {
                        /* context is active; compose immediately after
                         * commit has completed */

                        DPRINT(("COMMIT: Invoking composition after commit"));
                    }
                    /* FLOW THROUGH */
                }

                case WFC_MESSAGE_COMPOSE:
                {
                    DPRINT(("****** COMPOSING SCENE ******"));

                    WFC_Context_DoCompose(context);

                    break;
                }
                
                case WFC_MESSAGE_FENCE_1_DISPLAY:
                {
                    DPRINT(("****** STORING EGLDISPLAY (%p) ******", msg.data));

                    context->nextSyncObjectDisplay = (WFCEGLDisplay)msg.data;
                    break;
                }

                case WFC_MESSAGE_FENCE_2_SYNCOBJECT:
                {
                    DPRINT(("****** BREAKING FENCE (%p) ******", msg.data));
                    OWF_ASSERT(context->eglPrivateSignalSync);

                    context->eglPrivateSignalSync(context->nextSyncObjectDisplay,
                                     (WFCEGLSync) msg.data,
                                     EGL_SIGNALED_KHR);
                    break;
                }
            }
        }
    }

    /* Release any use of EGL by this thread. */
    eglReleaseThread();

    DPRINT(("WFC_Context_ComposerThread terminating"));
    OWF_Thread_Exit(NULL);
    return NULL;
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/

OWF_API_CALL void
WFC_Context_SourceStreamUpdated(OWFNativeStreamType stream,
                                OWFint event,
                                void* data,
                                void* returnParam)
{
    (void)returnParam;
    OWF_ASSERT(data);

    DPRINT(("WFC_Context_SourceStreamUpdated(%p, %x, %p)",
            stream, event, data));
    stream = stream; /* suppress compiler warning */
 
    switch (event)
    {
        case OWF_OBSERVER_RETURN_DEFAULT_EVENT:
        if (returnParam)
        {
            OWF_DEFAULT_EVENT_PARAM* parameter = (OWF_DEFAULT_EVENT_PARAM*) returnParam;
            if ((parameter->length) == sizeof(OWF_DEFAULT_EVENT_PARAM))
                {
                parameter->event = OWF_STREAM_UPDATED;
                }
        }
        break;
    
        case OWF_STREAM_UPDATED:
            {
                WFC_CONTEXT*            context = NULL;
                context = CONTEXT(data);
                OWF_ASSERT(context);
                OWF_Mutex_Lock(&context->updateFlagMutex);
                
                if (WFC_Context_Active(context))
                {
                    ++context->sourceUpdateCount;
                }
                OWF_Mutex_Unlock(&context->updateFlagMutex);
            }
            break;
        default:
            break;
    }
}

/*---------------------------------------------------------------------------
 *
 *----------------------------------------------------------------------------*/
OWF_API_CALL WFCboolean
WFC_Context_Active(WFC_CONTEXT* context)
{
    OWF_ASSERT(context);

    return (WFC_CONTEXT_STATE_ACTIVE == context->activationState ||
           WFC_CONTEXT_STATE_ACTIVATING == context->activationState) ? WFC_TRUE : WFC_FALSE;
}

#ifdef __cplusplus
}
#endif

