#include "evas_common.h"
#include "evas_engine.h"


typedef int (*evas_engine_wince_close_display)();

typedef struct Evas_Engine_WinCE_GAPI_Priv Evas_Engine_WinCE_GAPI_Priv;


#define GETGXINFO 0x00020000

typedef struct
{
    long Version;               //00 (should filled with 100 before calling ExtEscape)
    void *pvFrameBuffer;        //04
    unsigned long cbStride;     //08
    unsigned long cxWidth;      //0c
    unsigned long cyHeight;     //10
    unsigned long cBPP;         //14
    unsigned long ffFormat;     //18
    char Unused[0x84 - 7 * 4];
} _GXDeviceInfo;


#define LINK(type,name,import) \
  name = (gapi_##type)GetProcAddress (gapi_lib, import)

#define GX_FULLSCREEN 0x01
#define GX_NORMALKEYS 0x02

#define kfDirect555   0x40
#define kfDirect565   0x80


typedef struct
{
   DWORD cxWidth;
   DWORD cyHeight;
   LONG  cbxPitch;
   LONG  cbyPitch;
   LONG  cBPP;
   DWORD ffFormat;
} _GAPI_Display_Properties;

typedef int                      (*gapi_display_open)(HWND hWnd, DWORD dwFlags);
typedef int                      (*gapi_display_close)();
typedef _GAPI_Display_Properties (*gapi_display_properties_get)(void);
typedef void*                    (*gapi_draw_begin)(void);
typedef int                      (*gapi_draw_end)(void);
typedef int                      (*gapi_suspend)(void);
typedef int                      (*gapi_resume)(void);

gapi_suspend          suspend = NULL;
gapi_resume           resume = NULL;

int
evas_software_wince_gapi_suspend(void)
{
   if (suspend)
     return suspend();
   else
     return 0;
}

int
evas_software_wince_gapi_resume(void)
{
   if (resume)
     return resume();
   else
     return 0;
}


struct Evas_Engine_WinCE_GAPI_Priv
{
   HMODULE            lib;
   gapi_display_close close_display;
   gapi_draw_begin    draw_begin;
   gapi_draw_end      draw_end;
   void              *buffer;
   int                width;
   int                height;
   int                stride;
};

void *
evas_software_wince_gapi_init(HWND window,
                              int  width,
                              int  height)
{
   WCHAR                        oemstr[100];
   _GAPI_Display_Properties     prop;
   HMODULE                      gapi_lib;
   Evas_Engine_WinCE_GAPI_Priv *priv;

   gapi_display_open            display_open = NULL;
   gapi_display_close           display_close = NULL;
   gapi_display_properties_get  display_properties_get = NULL;
   gapi_draw_begin              draw_begin = NULL;
   gapi_draw_end                draw_end = NULL;

   priv = (Evas_Engine_WinCE_GAPI_Priv *)malloc(sizeof(Evas_Engine_WinCE_GAPI_Priv));
   if (!priv)
     return NULL;

   gapi_lib = LoadLibrary(L"\\Windows\\gx.dll");
   if (!gapi_lib)
     {
        gapi_lib = LoadLibrary(L"gx.dll");
        if (!gapi_lib)
          {
             ERR("[Engine] [WinCE GAPI] Can not load gx.dll");
             goto free_priv;
          }
     }

   LINK(display_open, display_open, L"?GXOpenDisplay@@YAHPAUHWND__@@K@Z");
   LINK(display_close, display_close, L"?GXCloseDisplay@@YAHXZ");
   LINK(display_properties_get, display_properties_get, L"?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ");
   LINK(draw_begin, draw_begin, L"?GXBeginDraw@@YAPAXXZ");
   LINK(draw_end, draw_end, L"?GXEndDraw@@YAHXZ");
   LINK(suspend, suspend, L"?GXSuspend@@YAHXZ" );
   LINK(resume, resume, L"?GXResume@@YAHXZ" );

   if (!display_open ||
       !display_close ||
       !display_properties_get ||
       !draw_begin ||
       !draw_end ||
       !suspend ||
       !resume)
     {
	ERR("[Engine] [WinCE GAPI] Can not find valid symbols");
        goto free_lib;
     }

   if (!display_open(window, GX_FULLSCREEN))
     {
	ERR("[Engine] [WinCE GAPI] Can not open display");
        goto free_lib;
     }

   prop = display_properties_get();

   // verify pixel format
   if(!(prop.ffFormat & kfDirect565) || (prop.cBPP != 16))
     {
        ERR("display format mismatch");
        goto close_display;
     }

   // verify we have a vga device
   if ((GetSystemMetrics(SM_CXSCREEN) != (int)prop.cxWidth) ||
       (GetSystemMetrics(SM_CYSCREEN) != (int)prop.cyHeight))
     {
        ERR("display size mismatch");
        goto close_display;
     }

   priv->lib = gapi_lib;
   priv->close_display = display_close;
   priv->draw_begin = draw_begin;
   priv->draw_end = draw_end;

   /* GAPI on Ipaq H38** and H39** is completely buggy */
   /* They are detected as portrait device (width = 240 and height = 320) */
   /* but the framebuffer is managed like a landscape device : */
   /*
     240
 +---------+
 |         |
 |         |
 |         |
 |         |
 |         | 320
 | ^^^     |
 | |||     |
 | |||     |
 | |||     |
 +---------+
  ---->

   */
   /* So these devices are considered as landscape devices */
   /* and width and height are switched. */
   /* Other devices are managed normally : */
   /*
     240
  +---------+
| |--->     |
| |--->     |
| |--->     |
v |         |
  |         | 320
  |         |
  |         |
  |         |
  |         |
  +---------+

    */

   SystemParametersInfo (SPI_GETOEMINFO, sizeof (oemstr), oemstr, 0);

   if (((oemstr[12] == 'H') &&
        (oemstr[13] == '3') &&
        (oemstr[14] == '8')) ||
       ((oemstr[12] == 'H') &&
        (oemstr[13] == '3') &&
        (oemstr[14] == '9')))
     {
        _GXDeviceInfo gxInfo = { 0 };
        HDC           dc;
        int           result;

        priv->width = prop.cyHeight;
        priv->height = prop.cxWidth;
        priv->stride = prop.cbxPitch;

        dc = GetDC (window);
        if (!dc)
          {
             ERR("Can not get device");
             goto close_display;
          }

        gxInfo.Version = 100;
        result = ExtEscape(dc, GETGXINFO, 0, NULL, sizeof(gxInfo),
                           (char *) &gxInfo);
        if (result <= 0)
          {
             ERR("ExtEscape failed");
             ReleaseDC(window, dc);
             goto close_display;
          }

        priv->buffer = gxInfo.pvFrameBuffer;
        ReleaseDC(window, dc);
     }
   else
     {
        priv->width = prop.cxWidth;
        priv->height = prop.cyHeight;
        priv->stride = prop.cbyPitch;
        priv->buffer = NULL;
     }

   if ((priv->width != width) ||
       (priv->height != height))
     {
        ERR("Size mismatch: asked: %dx%d, got: %dx%d",
            width, height, priv->width, priv->height);
        goto close_display;
     }

   return priv;

 close_display:
   display_close();
 free_lib:
   FreeLibrary(gapi_lib);
 free_priv:
   free(priv);
   return NULL;
}

void
evas_software_wince_gapi_shutdown(void *priv)
{
   Evas_Engine_WinCE_GAPI_Priv *p;

   p = (Evas_Engine_WinCE_GAPI_Priv *)priv;
   p->close_display();
   suspend = NULL;
   resume = NULL;
   FreeLibrary(p->lib);
   free(p);
}


FB_Output_Buffer *
evas_software_wince_gapi_output_buffer_new(void *priv,
                                           int   width,
                                           int   height)
{
   FB_Output_Buffer *fbob;
   void             *buffer;

   fbob = calloc(1, sizeof(FB_Output_Buffer));
   if (!fbob) return NULL;

   buffer = malloc (width * height * 2); /* we are sure to have 16bpp */
   if (!buffer)
     {
        free(fbob);
        return NULL;
     }

   fbob->priv = priv;

   fbob->im = (Soft16_Image *) evas_cache_image_data(evas_common_soft16_image_cache_get(), width, height, (DATA32 *)buffer, 0, EVAS_COLORSPACE_RGB565_A5P);
   if (fbob->im)
     fbob->im->stride = ((Evas_Engine_WinCE_GAPI_Priv *)priv)->stride >> 1;

   return fbob;
}

void
evas_software_wince_gapi_output_buffer_free(FB_Output_Buffer *fbob)
{
   free(fbob->im->pixels);
   free(fbob);
}

void
evas_software_wince_gapi_output_buffer_paste(FB_Output_Buffer *fbob)
{
   Evas_Engine_WinCE_GAPI_Priv *priv;
   void                        *buffer;

   priv = (Evas_Engine_WinCE_GAPI_Priv *)fbob->priv;

   buffer = priv->draw_begin();
   if (!buffer)
     return;

   if (priv->buffer) buffer = priv->buffer;

   if ((fbob->im->cache_entry.w == priv->width) &&
       (fbob->im->cache_entry.h == priv->height))
     memcpy(buffer, fbob->im->pixels,
            priv->width * priv->height * 2);

   priv->draw_end();
}

void
evas_software_wince_gapi_surface_resize(FB_Output_Buffer *fbob)
{
}
