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

int
evas_software_ddraw_init (HWND    window,
                          int     depth,
                          int     fullscreen,
                          Outbuf *buf)
{
   DDSURFACEDESC  surface_desc;
   DDPIXELFORMAT  pixel_format;
   HRESULT        res;
   int            width;
   int            height;

   if (!buf)
     return 0;

   buf->priv.dd.window = window;

   res = DirectDrawCreate(NULL, &buf->priv.dd.object, NULL);
   if (FAILED(res))
     return 0;

   if (buf->priv.dd.fullscreen)
     {
        DDSCAPS caps;

        res = buf->priv.dd.object->SetCooperativeLevel(window,
                                                       DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
        if (FAILED(res))
          goto release_object;

        width = GetSystemMetrics(SM_CXSCREEN);
        height = GetSystemMetrics(SM_CYSCREEN);

        ZeroMemory(&pixel_format, sizeof(pixel_format));
        pixel_format.dwSize = sizeof(pixel_format);
        buf->priv.dd.surface_primary->GetPixelFormat(&pixel_format);

        if (pixel_format.dwRGBBitCount != (DWORD)depth)
          goto release_object;

        buf->priv.dd.depth = depth;

        res = buf->priv.dd.object->SetDisplayMode(width, height, depth);
        if (FAILED(res))
          goto release_object;

        memset(&surface_desc, 0, sizeof(surface_desc));
        surface_desc.dwSize = sizeof(surface_desc);
        surface_desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
        surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
        surface_desc.dwBackBufferCount = 1;

        res = buf->priv.dd.object->CreateSurface(&surface_desc,
                                                 &buf->priv.dd.surface_primary, NULL);
        if (FAILED(res))
          goto release_object;

        caps.dwCaps = DDSCAPS_BACKBUFFER;
        res = buf->priv.dd.surface_primary->GetAttachedSurface(&caps,
                                                               &buf->priv.dd.surface_back);
        if (FAILED(res))
          goto release_surface_primary;
     }
   else
     {
        RECT           rect;

        if (!GetClientRect(window, &rect))
          goto release_object;

        width = rect.right - rect.left;
        height = rect.bottom - rect.top;

        res = buf->priv.dd.object->SetCooperativeLevel(window, DDSCL_NORMAL);
        if (FAILED(res))
          goto release_object;

        res = buf->priv.dd.object->CreateClipper(0, &buf->priv.dd.clipper, NULL);
        if (FAILED(res))
          goto release_object;

        res = buf->priv.dd.clipper->SetHWnd(0, window);
        if (FAILED(res))
          goto release_clipper;

        memset(&surface_desc, 0, sizeof(surface_desc));
        surface_desc.dwSize = sizeof(surface_desc);
        surface_desc.dwFlags = DDSD_CAPS;
        surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

        res = buf->priv.dd.object->CreateSurface(&surface_desc, &buf->priv.dd.surface_primary, NULL);
        if (FAILED(res))
          goto release_clipper;

        res = buf->priv.dd.surface_primary->SetClipper(buf->priv.dd.clipper);
        if (FAILED(res))
          goto release_surface_primary;

        memset (&surface_desc, 0, sizeof(surface_desc));
        surface_desc.dwSize = sizeof(surface_desc);
        surface_desc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
        surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
        surface_desc.dwWidth = width;
        surface_desc.dwHeight = height;

        res = buf->priv.dd.object->CreateSurface(&surface_desc, &buf->priv.dd.surface_back, NULL);
        if (FAILED(res))
          goto release_surface_primary;

        ZeroMemory(&pixel_format, sizeof(pixel_format));
        pixel_format.dwSize = sizeof(pixel_format);
        buf->priv.dd.surface_primary->GetPixelFormat(&pixel_format);

        if (pixel_format.dwRGBBitCount != (DWORD)depth)
          goto release_surface_back;

        buf->priv.dd.depth = depth;
     }

   return 1;

 release_surface_back:
   buf->priv.dd.surface_back->Release();
 release_surface_primary:
   buf->priv.dd.surface_primary->Release();
 release_clipper:
   if (!buf->priv.dd.fullscreen)
     buf->priv.dd.clipper->Release();
 release_object:
   buf->priv.dd.object->Release();

   return 0;
}

void
evas_software_ddraw_shutdown(Outbuf *buf)
{
   if (!buf)
     return;

   if (buf->priv.dd.fullscreen)
     if (buf->priv.dd.surface_back)
       buf->priv.dd.surface_back->Release();
   if (buf->priv.dd.surface_primary)
     buf->priv.dd.surface_primary->Release();
   if (!buf->priv.dd.fullscreen)
     if (buf->priv.dd.clipper)
       buf->priv.dd.clipper->Release();
   if (buf->priv.dd.object)
     buf->priv.dd.object->Release();
}

int
evas_software_ddraw_masks_get(Outbuf *buf)
{
   DDPIXELFORMAT pixel_format;

   ZeroMemory(&pixel_format, sizeof(pixel_format));
   pixel_format.dwSize = sizeof(pixel_format);

   if (FAILED(buf->priv.dd.surface_primary->GetPixelFormat(&pixel_format)))
     return 0;

   buf->priv.mask.r = pixel_format.dwRBitMask;
   buf->priv.mask.g = pixel_format.dwGBitMask;
   buf->priv.mask.b = pixel_format.dwBBitMask;

   return 1;
}

void *
evas_software_ddraw_lock(Outbuf *buf,
                         int    *ddraw_width,
                         int    *ddraw_height,
                         int    *ddraw_pitch,
                         int    *ddraw_depth)
{
   DDSURFACEDESC surface_desc;

   ZeroMemory(&surface_desc, sizeof(surface_desc));
   surface_desc.dwSize = sizeof(surface_desc);

   if (FAILED(buf->priv.dd.surface_back->Lock(NULL,
                                              &surface_desc,
                                              DDLOCK_WAIT | DDLOCK_WRITEONLY | DDLOCK_SURFACEMEMORYPTR | DDLOCK_NOSYSLOCK,
                                              NULL)))
     return NULL;

   *ddraw_width = surface_desc.dwWidth;
   *ddraw_height = surface_desc.dwHeight;
   *ddraw_pitch = surface_desc.lPitch;
   *ddraw_depth = surface_desc.ddpfPixelFormat.dwRGBBitCount >> 3;

   return surface_desc.lpSurface;
}

void
evas_software_ddraw_unlock_and_flip(Outbuf *buf)
{
   RECT    dst_rect;
   RECT    src_rect;
   POINT   p;

   if (FAILED(buf->priv.dd.surface_back->Unlock(NULL)))
     return;

   /* we figure out where on the primary surface our window lives */
   p.x = 0;
   p.y = 0;
   ClientToScreen(buf->priv.dd.window, &p);
   GetClientRect(buf->priv.dd.window, &dst_rect);
   OffsetRect(&dst_rect, p.x, p.y);
   SetRect(&src_rect, 0, 0, buf->width, buf->height);

   /* nothing to do if the function fails, so we don't check the result */
   buf->priv.dd.surface_primary->Blt(&dst_rect,
                                     buf->priv.dd.surface_back,
                                     &src_rect,
                                     DDBLT_WAIT, NULL);
}

void
evas_software_ddraw_surface_resize(Outbuf *buf)
{
   DDSURFACEDESC surface_desc;

   buf->priv.dd.surface_back->Release();
   memset (&surface_desc, 0, sizeof (surface_desc));
   surface_desc.dwSize = sizeof (surface_desc);
   /* FIXME: that code does not compile. Must know why */
#if 0
   surface_desc.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
   surface_desc.dwWidth = width;
   surface_desc.dwHeight = height;
   buf->priv.dd.surface_back->SetSurfaceDesc(&surface_desc, NULL);
#else
   surface_desc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
   surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
   surface_desc.dwWidth = buf->width;
   surface_desc.dwHeight = buf->height;
   buf->priv.dd.object->CreateSurface(&surface_desc,
                                      &buf->priv.dd.surface_back,
                                      NULL);
#endif
}
