/*
   (c) Copyright 2001-2009  The world wide DirectFB Open Source Community (directfb.org)
   (c) Copyright 2000-2004  Convergence (integrated media) GmbH

   All rights reserved.

   Written by Denis Oliver Kropp <dok@directfb.org>,
              Andreas Hundt <andi@fischlustig.de>,
              Sven Neumann <neo@directfb.org>,
              Ville Syrjälä <syrjala@sci.fi> and
              Claudio Ciccani <klan@users.sf.net>.

   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; either
   version 2 of the License, or (at your option) any later version.

   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.
*/

#include <config.h>

#include <pthread.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <string.h>

#include <directfb.h>
#include <directfb_version.h>

#include <misc/conf.h>

#include <core/core.h>
#include <core/coredefs.h>
#include <core/coretypes.h>

#include <core/input.h>
#include <core/layer_context.h>
#include <core/layer_control.h>
#include <core/layers.h>
#include <core/state.h>
#include <core/gfxcard.h>
#include <core/surface.h>
#include <core/windows.h>
#include <core/windowstack.h>
#include <core/wm.h>

#include <gfx/convert.h>

#include <direct/conf.h>
#include <direct/direct.h>
#include <direct/interface.h>
#include <direct/log.h>
#include <direct/mem.h>
#include <direct/messages.h>
#include <direct/util.h>

#include <display/idirectfbsurface.h>

#include <idirectfb.h>

#if DFB_BUILD_FOR_PRJ_OBAMA
#include <MsCommon.h>
#include <drvSYS.h>
#include <drvMMIO.h>
#include "madp.h"
#include "dfb_mmap_ref.h"
#endif

//DFBInfo
#include <dlfcn.h>
#define DFBINFO_PATH    "/config/libdfbinfo.so"
typedef bool (*SetKEYPADCfg_Func)();
typedef bool (*SetExtDFBRcCfg_Func)();

//libapiGOP
#if (!ARCH_X86 && !ARCH_X86_64)
#include <MsCommon.h>
#include  <apiGOP.h>

#define LIBAPIGOP_PATH  "libapiGOP.so"
#define LEAST_SUPPORT_GOP_VERSION   81
typedef E_GOP_API_Result (*GetChipCaps_Func)(EN_GOP_CAPS,u8*,u32);

#endif


#ifdef VISIBILITY_HIDDEN
#pragma GCC visibility push(default)
#endif  //VISIBILITY_HIDDEN


static void*s_dfbinfo_hanle = NULL;


static bool
DirectFBDoDFBInfo_SetKEYPADCfg()
{
    SetKEYPADCfg_Func func = NULL;
    bool ret = false;
    if(NULL == s_dfbinfo_hanle)
        s_dfbinfo_hanle = dlopen(DFBINFO_PATH, RTLD_LAZY);;

    if(!s_dfbinfo_hanle)
    {
        printf("dlopen %s fail !!! \n",DFBINFO_PATH);
        printf("dlerror() : %s\n", dlerror());
        return ret;
    }

    func = (SetKEYPADCfg_Func)dlsym(s_dfbinfo_hanle, "SetKEYPADCfg");
    if(func)
        ret = func();
    else
        printf("get func SetKEYPADCfg fail!\n");

    if(ret == false)
        printf("DirectFBDoDFBInfo_SetKEYPADCfg fail !!! \n");

    //!!! don't close it
//    dlclose(handle);

    return ret;
}

#if (!ARCH_X86 && !ARCH_X86_64)
static bool
DirectFBDoDFBInfo_SetDFBRcCfg()
{

    SetExtDFBRcCfg_Func func = NULL;
    bool ret = false;

    if(NULL == s_dfbinfo_hanle)
        s_dfbinfo_hanle = dlopen(DFBINFO_PATH, RTLD_LAZY);;

    if(!s_dfbinfo_hanle)
    {
        printf("dlopen %s fail !!! \n",DFBINFO_PATH);
        return ret;
    }

    func = (SetExtDFBRcCfg_Func)dlsym(s_dfbinfo_hanle, "SetExtDFBRcCfg");
    if(func)
        ret = func();
    else
        printf("get func SetExtDFBRcCfg fail! \n");

    if(ret == false)
        printf("DirectFBDoDFBInfo_SetDFBRcCfg fail !!! \n");

    return ret;
}
#endif

IDirectFB *idirectfb_singleton = NULL;
pthread_mutex_t  g_dfb_lock_counter_mutex= PTHREAD_MUTEX_INITIALIZER;
unsigned volatile int g_dfb_lock_flag = 0;
static DFBResult CreateRemote( const char *host, int session, IDirectFB **ret_interface );

/*
 * Version checking
 */
const unsigned int directfb_major_version = DIRECTFB_MAJOR_VERSION;
const unsigned int directfb_minor_version = DIRECTFB_MINOR_VERSION;
const unsigned int directfb_micro_version = DIRECTFB_MICRO_VERSION;
const unsigned int directfb_binary_age    = DIRECTFB_BINARY_AGE;
const unsigned int directfb_interface_age = DIRECTFB_INTERFACE_AGE;

const char *
DirectFBCheckVersion( unsigned int required_major,
                      unsigned int required_minor,
                      unsigned int required_micro )
{
     if (required_major > DIRECTFB_MAJOR_VERSION)
          return "DirectFB version too old (major mismatch)";
     if (required_major < DIRECTFB_MAJOR_VERSION)
          return "DirectFB version too new (major mismatch)";
     if (required_minor > DIRECTFB_MINOR_VERSION)
          return "DirectFB version too old (minor mismatch)";
     if (required_minor < DIRECTFB_MINOR_VERSION)
          return "DirectFB version too new (minor mismatch)";
     if (required_micro < DIRECTFB_MICRO_VERSION - DIRECTFB_BINARY_AGE)
          return "DirectFB version too new (micro mismatch)";
     if (required_micro > DIRECTFB_MICRO_VERSION)
          return "DirectFB version too old (micro mismatch)";

     return NULL;
}

const char *
DirectFBUsageString( void )
{
     return dfb_config_usage();
}

#if (!ARCH_X86 && !ARCH_X86_64)
DFBResult
DirectFB_CheckUtopiaVersion()
{
     //Fix converity  
    //void* handle = dlopen(LIBAPIGOP_PATH, RTLD_LAZY);
    void* handle = NULL; 
    handle = dlopen(LIBAPIGOP_PATH, RTLD_LAZY);
    //Fix converity END
    GetChipCaps_Func func = NULL;
    E_GOP_API_Result ret = GOP_API_FAIL;
    GOP_LIB_VER GOPLib;

    if(!handle)
    {
        printf("dlopen %s fail !!! \n",LIBAPIGOP_PATH);
        return ret;
    }

    func = (GetChipCaps_Func)dlsym(handle, "MApi_GOP_GetChipCaps");
    if(func)
    {
        ret = func(E_GOP_CAP_VERSION,(MS_U32*)(&GOPLib),sizeof(GOP_LIB_VER));
    }
    else
        printf("get func MApi_GOP_GetChipCaps fail!\n");

    if(ret == GOP_API_SUCCESS)
    {
        printf("[GOP Version]: \33[0;32m %04ld \33[m  | BuildNum: \33[0;32m %04ld \33[m | ChangeList : \33[0;32m %08ld \33[m\n",
                 GOPLib.GOP_LibVer, GOPLib.GOP_BuildNum, GOPLib.GOP_ChangeList);

        if(GOPLib.GOP_LibVer < LEAST_SUPPORT_GOP_VERSION)
        {
            printf("\033[47;31mThe DFB only support libapiGOP.so whose version >= %d \033[5m  \n",LEAST_SUPPORT_GOP_VERSION);
            ret = GOP_API_FAIL;
        }
    }
    else
    {
        printf("MApi_GOP_GetChipCaps fail !!! ret = %d \n",ret);
    }
    dlclose(handle);

    return ret;
 }
 
DFBResult
DirectFBGOPSetForceWrite(bool benable)
{
  
    E_GOP_API_Result ret = GOP_API_FAIL;

    if (benable)
        MsOS_Init();
 
    ret = MApi_GOP_GWIN_SetForceWrite(benable);

    return ret;
 } 
#else

DFBResult
DirectFBGOPSetForceWrite(bool benable)
{
    return DFB_OK;
} 
 
 
#endif


#define MSTAR_MULTI_GOP_SUPPORT TRUE
#define MSTAR_MAX_OUTPUT_LAYER_COUNT 6
DFBResult
DirectFBInit( int *argc, char *(*argv[]) )
{
     DFBResult ret;

 #if DFB_BUILD_FOR_PRJ_OBAMA
     u32 video_phys, video_len, video_aligned, video_miu;
     const SYS_Info *psys_info = NULL;
     int i=0;
 #endif

#if (!ARCH_X86 && !ARCH_X86_64)     //X86 doesn't support this
    //check the libapiGOP.so version at the very beginning...
    if(DirectFB_CheckUtopiaVersion() != GOP_API_SUCCESS)
    {
        printf("Check Utopia Version failed!!!!!!!!!! \n");
        return DFB_FAILURE;
    }

     if(!DirectFBDoDFBInfo_SetKEYPADCfg())
     {
        printf("DFBCreate: DirectFBDoDFBInfo_SetKEYPADCfg fail! \n");
        //return DFB_FAILURE;
     }
#endif


     printf("==========================================\n");
     printf("DFB library build @ ");
     printf(BUILDTIME);
     printf("\n");
     printf("===========================================\n");

     ret = dfb_config_init( argc, argv );
     if (ret)
          return ret;


 #if DFB_BUILD_FOR_PRJ_OBAMA
     MAdp_SYS_Init();
     MDrv_MMIO_Init();
     if(!MDrv_SYS_Init())
        return DFB_FAILURE;

     psys_info = MDrv_SYS_GetInfo();
     if(NULL == psys_info)
        return DFB_FAILURE;

     dfb_config->mst_miu1_hal_offset = psys_info->Chip.MIU1Base;
     dfb_config->mst_miu1_cpu_offset = psys_info->Chip.MIU1BusBase;

     //refine me here
     dfb_config->mst_gfx_gop_index= MAdp_SYS_GetGFXGOPIndex();

#if MSTAR_MULTI_GOP_SUPPORT
    //adjust the default gop

     for(i=0; i<MSTAR_MAX_OUTPUT_LAYER_COUNT;i++)
     {
        if(dfb_config->mst_gop_available[i]==dfb_config->mst_gfx_gop_index)
        {
          dfb_config->mst_gop_available[i] = dfb_config->mst_gop_available[0];
          dfb_config->mst_gop_available[0] = dfb_config->mst_gfx_gop_index;
          break;
        }
     }

     if (i==MSTAR_MAX_OUTPUT_LAYER_COUNT)
        printf("\nwrong GOP number\n");

#endif
     dfb_config->system="DEVMEM";
     dfb_config->mst_gfx_h_offset=0;
     if(false==MAdp_SYS_GetMemoryInfo(MMAP_DFB_FRAME_BUFFER, (unsigned long *)&video_phys,
                        (unsigned long *)&video_len, (unsigned long *)&video_aligned, (unsigned long *)&video_miu))
     {
          return DFB_FAILURE;
     }

     dfb_config->video_length = video_len;
     dfb_config->video_phys_hal= video_phys;

     if(MADP_MIU1 == video_miu)
     {
          dfb_config->video_phys_cpu =  dfb_config->video_phys_hal+(dfb_config->mst_miu1_cpu_offset-dfb_config->mst_miu1_hal_offset);
     }
     else
          dfb_config->video_phys_cpu =   dfb_config->video_phys_hal;

#endif
     return DFB_OK;
}



DFBResult
DirectFBSetOption( const char *name, const char *value )
{
     DFBResult ret;

     if (dfb_config == NULL) {
          D_ERROR( "DirectFBSetOption: DirectFBInit has to be "
                   "called before DirectFBSetOption!\n" );
          return DFB_INIT;
     }



     if (!name)
          return DFB_INVARG;

     ret = dfb_config_set( name, value );
     if (ret)
          return ret;

      if (idirectfb_singleton) {
      D_ERROR( "DirectFBSetOption: DirectFBSetOption has to be "
               "called before DirectFBCreate!\n" );
      return DFB_INIT;
      
     }

     return DFB_OK;
}


/*
Afford one interface for the AP to get the miu cpu offset
*/

DFBResult
DirectFBGetMIUCPUOffset(DFBMIUIdentifier identifier, unsigned long * puloffset)
{

     if (dfb_config == NULL)
     {
          D_ERROR( "DirectFBSetOption: DirectFBInit has to be "
                   "called before DirectFBSetOption!\n" );
          return DFB_INIT;
     }

    if((identifier >= DFB_MIUMAX)||(!puloffset))
    {
        return DFB_INVARG;
    }

    if(DFB_MIU0 == identifier)
    {
       *puloffset = dfb_config->mst_miu0_cpu_offset;
    }
    else if(DFB_MIU1 == identifier)
    {
       *puloffset = dfb_config->mst_miu1_cpu_offset;
    }
    else
    {
         return DFB_INVARG;
    }

    return DFB_OK;
}

/*
Afford one interface for the AP to get the miu hal offset
*/
DFBResult
DirectFBGetMIUHALOffset(DFBMIUIdentifier identifier, unsigned long * puloffset)
{

     if (dfb_config == NULL)
     {
          D_ERROR( "DirectFBSetOption: DirectFBInit has to be "
                   "called before DirectFBSetOption!\n" );
          return DFB_INIT;
     }

    if((identifier >= DFB_MIUMAX)||(!puloffset))
    {
        return DFB_INVARG;
    }

    if(DFB_MIU0 == identifier)
    {
       *puloffset = dfb_config->mst_miu0_hal_offset;
    }
    else if(DFB_MIU1 == identifier)
    {
       *puloffset = dfb_config->mst_miu1_hal_offset;
    }
    else
    {
        return DFB_INVARG;
    }

    return DFB_OK;
}



/*
    Afford one interface to get the Mirror Mode
*/

DFBResult
DirectFBGetMirrorInfo(DFBMirrorInfo *pstdfbMirroInfo)
{

     if (dfb_config == NULL)
     {
          D_ERROR( "DirectFBSetOption: DirectFBInit has to be "
                   "called before DirectFBSetOption!\n" );
          return DFB_INIT;
     }

     pstdfbMirroInfo->DFB_HMirrorEnable = false;
     pstdfbMirroInfo->DFB_VMirrorEnable = false;
     if(dfb_config->mst_GOP_HMirror > 0)
        pstdfbMirroInfo->DFB_HMirrorEnable = true;

     if(dfb_config->mst_GOP_VMirror > 0)
        pstdfbMirroInfo->DFB_VMirrorEnable = true;

    return DFB_OK;
}

static void DirectfbInitThread(void)
{
    /* BUGFIX for Mantis 0593809 to avoid OOM problem.
       Make the Master Process to have the most hightest priority to release memory.
    */ 
    sleep(25);
    
    nice(-20);
}


/*
 * Programs have to call this to get the super interface
 * which is needed to access other functions
 */
DFBResult
DirectFBCreate( IDirectFB **interface )
{
     DFBResult  ret;
     IDirectFB *dfb = NULL;//Fix converity END
     CoreDFB   *core_dfb = NULL;//Fix converity END

     if (!dfb_config) {
          /*  don't use D_ERROR() here, it uses dfb_config  */
          direct_log_printf( NULL, "(!) DirectFBCreate: DirectFBInit "
                             "has to be called before DirectFBCreate!\n" );
          return DFB_INIT;
     }

     if (!interface)
          return DFB_INVARG;

     if (idirectfb_singleton) {
          idirectfb_singleton->AddRef( idirectfb_singleton );
          *interface = idirectfb_singleton;
          return DFB_OK;
     }

#if (!ARCH_X86 && !ARCH_X86_64)     //X86 doesn't support this
     if(!DirectFBDoDFBInfo_SetDFBRcCfg())
     {
        printf("DFBCreate: DirectFBDoDFBInfo_SetDFBRcCfg fail! \n");
        //return DFB_FAILURE;
     }
#endif

     direct_initialize();

     if ( !(direct_config->quiet & DMT_BANNER) && dfb_config->banner) {
          direct_log_printf( NULL,
                             "\n"
                             "   ~~~~~~~~~~~~~~~~~~~~~~~~~~| DirectFB " DIRECTFB_VERSION " |~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                             "        (c) 2001-2009  The world wide DirectFB Open Source Community\n"
                             "        (c) 2000-2004  Convergence (integrated media) GmbH\n"
                             "      ----------------------------------------------------------------\n"
                             "\n" );
     }

     if (dfb_config->remote.host)
          return CreateRemote( dfb_config->remote.host, dfb_config->remote.session, interface );

     ret = dfb_core_create( &core_dfb );
     if (ret)
          return ret;

     DIRECT_ALLOCATE_INTERFACE( dfb, IDirectFB );

     ret = IDirectFB_Construct( dfb, core_dfb );
     if (ret) {
          dfb_core_destroy( core_dfb, false );
          return ret;
     }

     if (dfb_core_is_master( core_dfb )) {
     	
        if (dfb_config->mst_disable_master_pri_high == false)
        {
                pthread_t id;
                int ret;

                ret = pthread_create(&id, NULL, (void *) DirectfbInitThread, NULL);

                if( 0 != ret)
                {
                    D_WARN(ret, "Create pthread error!\n");
                }
        } 

          /* not fatal */
          ret = dfb_wm_post_init( core_dfb );
          if (ret)
               D_DERROR( ret, "DirectFBCreate: Post initialization of WM failed!\n" );

          dfb_core_activate( core_dfb );

     }

     *interface = idirectfb_singleton = dfb;

     return DFB_OK;
}

DFBResult
DirectFBSetGOPDst(DFBDisplayLayerID         id, DFBDisplayLayerGOPDST gop_dst )
{
    return dfb_layer_Set_GopDst(id, gop_dst);
}

DFBDisplayLayerGOPDST
DirectFBGetGOPDst(DFBDisplayLayerID         id)
{
    return dfb_layer_Get_GopDst(id);
}

u8
DirectFBGetGOPIndex(DFBDisplayLayerID     id)
{
    return dfb_layer_Get_GopIndex(id);
}

DFBResult
DirectFBSetBootLogoPatch(DFBDisplayLayerID id, int miusel )
{
    return dfb_layer_Set_Boot_Logo_Patch(id, miusel);
}


DFBResult
DirectFBError( const char *msg, DFBResult error )
{
     if (msg)
          direct_log_printf( NULL, "(#) DirectFBError [%s]: %s\n", msg,
                             DirectFBErrorString( error ) );
     else
          direct_log_printf( NULL, "(#) DirectFBError: %s\n",
                             DirectFBErrorString( error ) );

     return error;
}

const char *
DirectFBErrorString( DFBResult error )
{
     if (D_RESULT_TYPE_IS( error, 'D','F','B' )) {
          switch (error) {
               case DFB_NOVIDEOMEMORY:
                    return "Out of video memory!";
               case DFB_MISSINGFONT:
                    return "No font has been set!";
               case DFB_MISSINGIMAGE:
                    return "No image has been set!";
               default:
                    return "UKNOWN DIRECTFB RESULT!";
          }
     }

     return DirectResultString( error );
}

DFBResult
DirectFBErrorFatal( const char *msg, DFBResult error )
{
     DirectFBError( msg, error );

     //if (idirectfb_singleton)
          //IDirectFB_Destruct( idirectfb_singleton );

     exit( error );
}

/**************************************************************************************************/

static DFBResult
CreateRemote( const char *host, int session, IDirectFB **ret_interface )
{
     DFBResult             ret;
     DirectInterfaceFuncs *funcs = NULL;
     void                 *interface;

     D_ASSERT( host != NULL );
     D_ASSERT( ret_interface != NULL );

     ret = DirectGetInterface( &funcs, "IDirectFB", "Requestor", NULL, NULL );
     if (ret)
          return ret;

     ret = funcs->Allocate( &interface );
     if (ret)
          return ret;

     ret = funcs->Construct( interface, host, session );
     if (ret)
          return ret;

     *ret_interface = idirectfb_singleton = interface;

     return DFB_OK;
}


#ifdef VISIBILITY_HIDDEN
#pragma GCC visibility pop
#endif  //VISIBILITY_HIDDEN

