/*****************************************************************************/
/* Copyright (c) 2009 NXP B.V.                                               */
/*                                                                           */
/* This library is free software; you can redistribute it and/or             */
/* modify it under the terms of the GNU Lesser General Public                */
/* License as published by the Free Software Foundation, using               */
/* version 2.1 of the License.                                               */
/*                                                                           */
/* This library is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          */
/* Lesser General Public License for more details.                           */
/*                                                                           */
/* You should have received a copy of the GNU Lesser General Public          */
/* License along with this library; if not, write to the Free Software       */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA   */
/*                                                                           */
/*****************************************************************************/
/**
    @file   phStbDFB_FBDevGfxLayer.c
    @brief  FB Device graphics layer specific part of graphics driver for DirectFB.

@b  Component:  phStbDFB.

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20050823    charrett    Re-organisation of code
  2 20050826    charrett    Fix bug with layers when using multiple applications
  3 20050826    laird       Adding NTSC changes
  4 20050831    laird       Changing Macro name.
  5 20050912    charrett    Allow double buffering.
  6 20050913    laird       Add support for which Screen has the Framebuffer
  7 20051005    laird       Store CoreSurface and CoreLayer
  8 20051124    laird       Actually store CoreSurface.
  9 20060727    laird       Removing Asserts.
----------------------------STB225----------------------------------------------
  9 20070112    laird       Moving to STB225
 10 20070116    laird       Crosscheck Rework.
 11 20070308    neill       Changes to support multiple frame buffers
 12 20070619    neill       Create LCD layer separately
 13 20070704    neill       Fix set level and getting layer information
 14 20070808    neill       Add support for Opacity
 15 20070815    neill       Add dummy test region function
 16 20070921    neill       Add support for HD framebuffers
 17 20071017    burningh    Fix QA C coverage errors
 18 20071029    burningh    Improve QA C confidence factor
 19 20071207    neill       Add support for flicker filtering
 20 20080703    laird       Remove a hack put in some time ago - MPC data noted it.
 21 20080709    neill       Add force param to blank output
 22 20090120    batelaan    Update file copyright/license text
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

/***********************************************
* INCLUDE FILES                                *
************************************************/

#include <phStbDFB_Local.h>
#include <core/core.h>
#include <core/layer_context.h>
#include <core/layer_control.h>
#include <core/windowstack.h>
#include <fusion/object.h>

/***********************************************
* LOCAL MACROS                                 *
* recommendation only: <MODULE><_WORD>+        *
************************************************/

/******************************************************************
* LOCAL TYPEDEFS                                                  *
* recommendation only:                [p]phStbTemplate_<Word>+_t  *
*******************************************************************/

/******************************************************************
* STATIC FUNCTION PROTOTYPES                  <Module>_<Word>+    *
*******************************************************************/
static DFBResult phStbDFB_FBDevGfxLayerInit(CoreLayer                  *layer,
                                            void                       *driver_data,
                                            void                       *layer_data,
                                            DFBDisplayLayerDescription *description,
                                            DFBDisplayLayerConfig      *config,
                                            DFBColorAdjustment         *adjustment );

static DFBResult phStbDFB_FBDevGfxAllocateSurface(CoreLayer              *layer,
                                                  void                   *driver_data,
                                                  void                   *layer_data,
                                                  void                   *region_data,
                                                  CoreLayerRegionConfig  *config,
                                                  CoreSurface           **ret_surface);

static DFBResult phStbDFB_FBDevGfxSetRegion(CoreLayer                  *layer,
                                            void                       *driver_data,
                                            void                       *layer_data,
                                            void                       *region_data,
                                            CoreLayerRegionConfig      *config,
                                            CoreLayerRegionConfigFlags  updated,
                                            CoreSurface                *surface,
                                            CorePalette                *palette );

static DFBResult phStbDFB_FBDevGfxTestRegion(CoreLayer                  *layer,
                                             void                       *driver_data,
                                             void                       *layer_data,
                                             CoreLayerRegionConfig      *config,
                                             CoreLayerRegionConfigFlags *failed,
                                             CoreSurface                *surface);

/******************************************************************
* STATIC DATA                  g[k|p|kp|pk|kpk]<Module>_<Word>+   *
*******************************************************************/
static DisplayLayerFuncs  oldFbDevGfxPrimaryLayerFuncs;
static void              *oldFbDevGfxPrimaryLayerDriverData;

/**
 * Function lookup table for the FBdev GFX layer.
 */
static DisplayLayerFuncs gphStbDFB_FBDevGfxLayerFuncs = {
    /*.LayerDataSize         = */ phStbDFB_LayerDataSize,
    /*.RegionDataSize        = */ 0,
    /*.InitLayer             = */ phStbDFB_FBDevGfxLayerInit,
    /*.InitSource            = */ 0,
    /*.GetCurrentOutputField = */ 0,
    /*.GetLevel              = */ phStbDFB_GetLevel,
    /*.SetLevel              = */ phStbDFB_SetLevel,
    /*.SetColorAdjustment    = */ 0,
    /*.TestRegion            = */ phStbDFB_FBDevGfxTestRegion,
    /*.AddRegion             = */ 0,
    /*.SetRegion             = */ phStbDFB_FBDevGfxSetRegion,
    /*.RemoveRegion          = */ 0,
    /*.FlipRegion            = */ 0,
    /*.UpdateRegion          = */ 0,
    /*.SetInputField         = */ 0,
    /*.AllocateSurface       = */ phStbDFB_FBDevGfxAllocateSurface,
    /*.ReallocateSurface     = */ 0,
    /*.DeallocateSurface     = */ 0
};

static DisplayLayerFuncs gphStbDFB_FBDevAdditionalGfxLayerFuncs;

/*********************************************************(((((((**********
* EXPORTED DATA                     g[k|p|kp|pk|kpk]phStbTemplate_<Word>+ *
***************************************************************************/

/*******************************************************************************
* FUNCTION IMPLEMENTATION  <Module>[_<Word>+] for static functions             *
*                          tm[<layer>]<Module>[_<Word>+] for exported functions*
********************************************************************************/

/******************************************************************************/
void phStbDFB_FBDevGfxLayerRegister(GraphicsDevice *device)
{
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_INTERNAL_ENTER, "%s: Hooking primary layer", __FUNCTION__);

    gpphStbDFB_DeviceData->layers[PH_STBDFB_SCREEN_MAIN_GRAPHICS1].layerId = PH_STBDFB_SCREEN_MAIN_GRAPHICS1;
    gpphStbDFB_DeviceData->layers[PH_STBDFB_SCREEN_MAIN_GRAPHICS2].layerId = PH_STBDFB_SCREEN_MAIN_GRAPHICS2;

    /*
     * Replace the InitLayer function in the oldFbDevGfxPrimaryLayerFuncs with my
     * InitLayer and the same for Allocate Surface I do not need to register the
     * layer as this function calls it indirectly anyway
     */
    dfb_layers_hook_primary( device,
                             &gpphStbDFB_DeviceData->layers[PH_STBDFB_SCREEN_MAIN_GRAPHICS1],
                             &gphStbDFB_FBDevGfxLayerFuncs,
                             &oldFbDevGfxPrimaryLayerFuncs,
                             &oldFbDevGfxPrimaryLayerDriverData);

    memcpy((void*)&gphStbDFB_FBDevAdditionalGfxLayerFuncs, (void*)&oldFbDevGfxPrimaryLayerFuncs, sizeof(DisplayLayerFuncs));
    gphStbDFB_FBDevAdditionalGfxLayerFuncs.LayerDataSize   = phStbDFB_LayerDataSize;
    gphStbDFB_FBDevAdditionalGfxLayerFuncs.InitLayer       = phStbDFB_FBDevGfxLayerInit;
    gphStbDFB_FBDevAdditionalGfxLayerFuncs.AllocateSurface = phStbDFB_FBDevGfxAllocateSurface;
    gphStbDFB_FBDevAdditionalGfxLayerFuncs.GetLevel        = phStbDFB_GetLevel;
    gphStbDFB_FBDevAdditionalGfxLayerFuncs.SetLevel        = phStbDFB_SetLevel;
    gphStbDFB_FBDevAdditionalGfxLayerFuncs.TestRegion      = phStbDFB_FBDevGfxTestRegion;
    gphStbDFB_FBDevAdditionalGfxLayerFuncs.SetRegion       = phStbDFB_FBDevGfxSetRegion;

    dfb_layers_register( dfb_screens_at(PH_STBDFB_SCREEN_MAIN),
                         &gpphStbDFB_DeviceData->layers[PH_STBDFB_SCREEN_MAIN_GRAPHICS2],
                         &gphStbDFB_FBDevAdditionalGfxLayerFuncs);

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_INTERNAL_LEAVE, "%s: Succeeded", __FUNCTION__);
}

/******************************************************************************/
void phStbDFB_FBDevLCDLayerRegister(GraphicsDevice *device)
{
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_INTERNAL_ENTER, "%s: Hooking LCD layer", __FUNCTION__);

    gpphStbDFB_DeviceData->layers[PH_STBDFB_SCREEN_MAIN_LCD].layerId = PH_STBDFB_SCREEN_MAIN_LCD;

    dfb_layers_register( dfb_screens_at(PH_STBDFB_SCREEN_LCD),
                         &gpphStbDFB_DeviceData->layers[PH_STBDFB_SCREEN_MAIN_LCD],
                         &gphStbDFB_FBDevAdditionalGfxLayerFuncs);

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_INTERNAL_LEAVE, "%s: Succeeded", __FUNCTION__);
}

/******************************************************************************/
DFBResult phStbDFB_FBDevGfxLayerInit(CoreLayer                  *layer,
                                     void                       *driver_data,
                                     void                       *layer_data,
                                     DFBDisplayLayerDescription *description,
                                     DFBDisplayLayerConfig      *config,
                                     DFBColorAdjustment         *adjustment )
{
    DFBResult     result = DFB_OK;
    tmErrorCode_t error = TM_OK;
    phStbVideoTypes_PixelFormat_t pixelFormat;
    phStbDFB_LayerData_t       *pLayerData  = (phStbDFB_LayerData_t*)driver_data;

    DBG_PRINT((gphStbDFB_DbgUnit, DBG_INTERNAL_ENTER, "%s: layerId=%u", __FUNCTION__, pLayerData->layerId));

    switch(pLayerData->layerId)
    {
        case(PH_STBDFB_SCREEN_MAIN_GRAPHICS1):
            snprintf(description->name, DFB_DISPLAY_LAYER_DESC_NAME_LENGTH, "FBDev Primary Layer [OSD]");
            break;
        case(PH_STBDFB_SCREEN_MAIN_GRAPHICS2):
            snprintf(description->name, DFB_DISPLAY_LAYER_DESC_NAME_LENGTH, "FBDev Primary Layer [Images]");
            break;
        case(PH_STBDFB_SCREEN_MAIN_LCD):
            snprintf(description->name, DFB_DISPLAY_LAYER_DESC_NAME_LENGTH, "FBDev Primary Layer [LCD]");
            break;
        default:
            break;
    }

    /*Call the old initLayer function*/
    result = oldFbDevGfxPrimaryLayerFuncs.InitLayer(layer,
                                                    oldFbDevGfxPrimaryLayerDriverData,
                                                    layer_data,
                                                    description,
                                                    config,
                                                    adjustment);

#if 0
    /*TODO Do this when Video Renderer supports this.*/
    TM_TO_DFB_RETURN_ON_ERR(phStbVideoRenderer_Layer_GetPixelFormat(pLayerData->pLayer, &pixelFormat),
                            "Error calling phStbVideoRenderer_Layer_GetPixelFormat Layer(%d) 0x%08x\n",
                            pLayerData->layerId, err);
#endif
    pixelFormat = phStbVideoTypes_PixelFormatARGB8888;
    /*set the pixel format*/
    config->pixelformat = CONVERT_TO_DFB_PIXELFORMAT(pixelFormat);

    config->buffermode  = DLBM_BACKVIDEO;

    /*This surface is the primary surface*/
    config->surface_caps = DSCAPS_PRIMARY | DSCAPS_DOUBLE | DSCAPS_VIDEOONLY | DSCAPS_FLIPPING;

    /*Need to override the defaults and support size*/
    description->caps |= DLCAPS_SCREEN_SIZE | DLCAPS_LEVELS;

    switch(pLayerData->layerId)
    {
        case(PH_STBDFB_SCREEN_MAIN_GRAPHICS1):
            error = phStbVideoRenderer_GetVideoLayer( phStbVideoRenderer_Layers_Graphics1,
                                                      &pLayerData->pLayer,
                                                      pLayerData->name);
            break;
        case(PH_STBDFB_SCREEN_MAIN_GRAPHICS2):
            error = phStbVideoRenderer_GetVideoLayer( phStbVideoRenderer_Layers_Graphics2,
                                                      &pLayerData->pLayer,
                                                      pLayerData->name);
            break;
        case(PH_STBDFB_SCREEN_MAIN_LCD):
            break;
        default:
            break;
    }

    if (error != TM_OK)
    {
        result = DFB_FAILURE;
    }

    DBG_PRINT((gphStbDFB_DbgUnit, DBG_INTERNAL_LEAVE, "%s: => 0x%x", __FUNCTION__, result));
    return result;
}
/******************************************************************************/
DFBResult phStbDFB_FBDevGfxAllocateSurface(CoreLayer              *layer,
                                           void                   *driver_data,
                                           void                   *layer_data,
                                           void                   *region_data,
                                           CoreLayerRegionConfig  *config,
                                           CoreSurface           **ret_surface)
{
    DFBResult             result = DFB_OK;
    CoreLayerContext      *context;
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_INTERNAL_ENTER, "%s", __FUNCTION__);
    /*
     * Call the old allocate surface function as this will work for this surface as
     * we are the GFX layer
     */

    result = oldFbDevGfxPrimaryLayerFuncs.AllocateSurface(layer,
                                                          oldFbDevGfxPrimaryLayerDriverData,
                                                          layer_data,
                                                          region_data,
                                                          config,
                                                          ret_surface);

    if (result == DFB_OK)
    {
        /*
         * Call set region, this calls dfb_fbdev_set_mode which in turn fills
         * in all the settings for the buffer descriptions etc which is needed
         * to stop it erroring, say that the format has changed by sending CLRCF_FORMAT
         */
        result = oldFbDevGfxPrimaryLayerFuncs.SetRegion(layer,
                                                        oldFbDevGfxPrimaryLayerDriverData,
                                                        layer_data,
                                                        region_data,
                                                        config,
                                                        CLRCF_FORMAT,
                                                        *ret_surface,
                                                        NULL);
    }

    DBG_PRINT((gphStbDFB_DbgUnit, DBG_INTERNAL_LEAVE, "%s: => 0x%x surface=%p", __FUNCTION__, result, *ret_surface));
    return result;
}

/******************************************************************************/
DFBResult phStbDFB_FBDevGfxSetRegion(CoreLayer                  *layer,
                                     void                       *driver_data,
                                     void                       *layer_data,
                                     void                       *region_data,
                                     CoreLayerRegionConfig      *config,
                                     CoreLayerRegionConfigFlags  updated,
                                     CoreSurface                *surface,
                                     CorePalette                *palette )
{
    phStbDFB_LayerData_t       *pLayerData = (phStbDFB_LayerData_t*) driver_data;
    phStbVideoRenderer_Layer_t *pLayer     = pLayerData->pLayer;
    tmErrorCode_t err;
    DFBResult result = DFB_OK;

    DBG_PRINT((gphStbDFB_DbgUnit, DBG_INTERNAL_ENTER, "%s: layer='%s'", __FUNCTION__, pLayerData->name));

    /* remember configuration */
    pLayerData->config = *config;

    if (pLayer)
    {
        /* Only deal with specific updates */
        if ((updated != CLRCF_ALL) && (updated & CLRCF_OPACITY))
        {
            /* Check to see if the Framebuffer is created using 2 OSD layers! */
            if (pLayerData->config.width > 960)
            {
                phStbVideoRenderer_Layer_t *pLayer2;
                char                        name[PH_STBVIDEORENDERER_MAX_NAME_LENGTH + 1];

                /* Must modify 2nd OSD layer too! */
                err = phStbVideoRenderer_GetVideoLayer( phStbVideoRenderer_Layers_Graphics2,
                                                        &pLayer2,
                                                        name);

                if (err == TM_OK)
                {
                    /* Show/Hide layer */
                    err = phStbVideoRenderer_Layer_BlankOutput(pLayer2, config->opacity == 0, false);
                    DBG_ASSERT2(err == 0, ("err=0x%x\n", err));

                    err = phStbVideoRenderer_Layer_SetAlphaLevel(pLayer2, config->opacity);
                    if (err != 0)
                    {
                        DBG_ASSERT2(err == 0, ("err=0x%x\n", err));
                        result = DFB_FAILURE;
                    }
                }
                else
                {
                    result = DFB_FAILURE;
                }
            }
            /* Show/Hide layer */
            err = phStbVideoRenderer_Layer_BlankOutput(pLayer, config->opacity == 0, false);
            DBG_ASSERT2(err == 0, ("err=0x%x\n", err));

            err = phStbVideoRenderer_Layer_SetAlphaLevel(pLayer, config->opacity);
            if (err != 0)
            {
                DBG_ASSERT2(err == 0, ("err=0x%x\n", err));
                result = DFB_FAILURE;
            }
        }

        if(updated & CLRCF_OPTIONS)
        {
            /* Check to see if the Framebuffer is created using 2 OSD layers! */
            if (pLayerData->config.width > 960)
            {
                phStbVideoRenderer_Layer_t *pLayer2;
                char                        name[PH_STBVIDEORENDERER_MAX_NAME_LENGTH + 1];

                /* Must modify 2nd OSD layer too! */
                err = phStbVideoRenderer_GetVideoLayer( phStbVideoRenderer_Layers_Graphics2,
                                                        &pLayer2,
                                                        name);

                if (err == TM_OK)
                {
                    if (config->options & DLOP_FLICKER_FILTERING)
                    {
                        err = phStbVideoRenderer_Layer_SetFlickerFiltering(pLayer, true);
                    }
                    else
                    {
                        err = phStbVideoRenderer_Layer_SetFlickerFiltering(pLayer, false);
                    }
                }
                else
                {
                    result = DFB_FAILURE;
                }
            }

            if (config->options & DLOP_FLICKER_FILTERING)
            {
                err = phStbVideoRenderer_Layer_SetFlickerFiltering(pLayer, true);
            }
            else
            {
                err = phStbVideoRenderer_Layer_SetFlickerFiltering(pLayer, false);
            }
            if (err != 0)
            {
                DBG_ASSERT2(err == 0, ("err=0x%x\n", err));
                result = DFB_FAILURE;
            }
        }
    }
    DBG_PRINT((gphStbDFB_DbgUnit, DBG_INTERNAL_LEAVE, "%s: => 0x%x", __FUNCTION__, result));
    return result;
}

/******************************************************************************/
DFBResult phStbDFB_FBDevGfxTestRegion(CoreLayer                  *layer,
                                      void                       *driver_data,
                                      void                       *layer_data,
                                      CoreLayerRegionConfig      *config,
                                      CoreLayerRegionConfigFlags *failed,
                                      CoreSurface                *surface)
{
    return DFB_OK;
}
