/*
 * SDL bitmap driver
 *
 * Copyright 1999 Patrik Stridvall
 * Copyright 2002 TransGaming Technologies, Inc.
 */

#include "config.h"

#include <string.h>

#include "bitmap.h"
#include "gdi.h"
#include "sdldrv.h"
#include "winbase.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(sdldrv);

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

extern const DC_FUNCTIONS *SDLDRV_DC_Funcs;  /* hack */
extern const SDL_VideoInfo *video_info;

static LONG SDLDRV_DC_GetBitmapBits(BITMAPOBJ *bitmap, void *bits, LONG count);
static LONG SDLDRV_DC_SetBitmapBits(BITMAPOBJ *bitmap, void *bits, LONG count);

/***********************************************************************
 *		SDLDRV_DC_AllocBitmap
 */
SDL_Surface *SDLDRV_DC_AllocBitmap(BITMAPOBJ *bitmap, void *pix, int pitch)
{
  SDL_Surface *surface = bitmap->physBitmap;
  Uint32 R = 0, G = 0, B = 0;

  if (surface) return surface;

  /* FIXME: handle this better (use SDL_GetVideoInfo) */
  switch (bitmap->bitmap.bmBitsPixel) {
  case 1:
  case 2:
  case 4:
  case 8:
    R = G = B = 0;
    break;
  case 15:
  case 16:
  case 24:
  case 32:
    R = video_info->vfmt->Rmask;
    G = video_info->vfmt->Gmask;
    B = video_info->vfmt->Bmask;
    break;
  default:
    FIXME("masks for %d bpp\n", bitmap->bitmap.bmBitsPixel);
    break;
  }

  TRACE("bitmap: %dx%dx%d\n",
	bitmap->bitmap.bmWidth,
	bitmap->bitmap.bmHeight,
	bitmap->bitmap.bmBitsPixel);

  if (pix && pitch)
    surface = SDL_CreateRGBSurfaceFrom(pix,
				       bitmap->bitmap.bmWidth,
				       bitmap->bitmap.bmHeight,
				       bitmap->bitmap.bmBitsPixel,
				       pitch,
				       R, G, B, 0);
  else
    surface = SDL_CreateRGBSurface(0,
				   bitmap->bitmap.bmWidth,
				   bitmap->bitmap.bmHeight,
				   bitmap->bitmap.bmBitsPixel,
				   R, G, B, 0);
  TRACE("allocated SDL surface %p\n", surface);

  bitmap->physBitmap = surface;
  bitmap->funcs = SDLDRV_DC_Funcs;

  return surface;
}

/***********************************************************************
 *		SDLDRV_DIB_GenColorMap
 */
SDL_Color *X11DRV_DIB_GenColorMap( DC *dc, SDL_Color *colorMapping,
				   WORD coloruse, WORD depth, BOOL quads,
				   const void *colorPtr, int start, int end )
{
  int i;

  if (coloruse == DIB_RGB_COLORS) {
    if (quads) {
      RGBQUAD * rgb = (RGBQUAD *)colorPtr;

      for (i = start; i < end; i++, rgb++) {
	colorMapping[i].r = rgb->rgbRed;
	colorMapping[i].g = rgb->rgbGreen;
	colorMapping[i].b = rgb->rgbBlue;
	colorMapping[i].unused = 0;
      }
    }
    else {
      RGBTRIPLE * rgb = (RGBTRIPLE *)colorPtr;

      for (i = start; i < end; i++, rgb++) {
	colorMapping[i].r = rgb->rgbtRed;
	colorMapping[i].g = rgb->rgbtGreen;
	colorMapping[i].b = rgb->rgbtBlue;
	colorMapping[i].unused = 0;
      }
    }
  }
  else {  /* DIB_PAL_COLORS */
    SDLDRV_PDEVICE *physDev = (SDLDRV_PDEVICE *)dc->physDev;

    if (colorPtr) {
      WORD * index = (WORD *)colorPtr;

      for (i = start; i < end; i++, index++) {
	SDL_GetRGB(*index, physDev->surface->format,
		   &colorMapping[i].r,
		   &colorMapping[i].g,
		   &colorMapping[i].b);
	colorMapping[i].unused = 0;
      }
    } else {
      for (i = start; i < end; i++) {
	SDL_GetRGB(i, physDev->surface->format,
		   &colorMapping[i].r,
		   &colorMapping[i].g,
		   &colorMapping[i].b);
	colorMapping[i].unused = 0;
      }
    }
  }

  return colorMapping;
}

/***********************************************************************
 *		SDLDRV_DIB_BuildColorMap
 */
SDL_Color *SDLDRV_DIB_BuildColorMap( DC *dc, WORD coloruse, WORD depth,
				     const BITMAPINFO *info, int *nColors )
{
  int colors;
  BOOL isInfo;
  const void *colorPtr;
  SDL_Color *colorMapping;

  if ((isInfo = (info->bmiHeader.biSize == sizeof(BITMAPINFOHEADER)))) {
    colors = info->bmiHeader.biClrUsed;
    if (!colors) colors = 1 << info->bmiHeader.biBitCount;
    colorPtr = info->bmiColors;
  }
  else {  /* assume BITMAPCOREINFO */
    colors = 1 << ((BITMAPCOREHEADER *)&info->bmiHeader)->bcBitCount;
    colorPtr = (WORD *)((BITMAPCOREINFO *)info)->bmciColors;
  }

  if (colors > 256) {
    ERR("called with >256 colors!\n");
    return NULL;
  }

  /* just so CopyDIBSection doesn't have to create an identity palette */
  if (coloruse == (WORD)-1) colorPtr = NULL;

  if (!(colorMapping = (SDL_Color *)HeapAlloc(GetProcessHeap(), 0,
					      colors * sizeof(SDL_Color) )))
    return NULL;

  *nColors = colors;
  return X11DRV_DIB_GenColorMap( dc, colorMapping, coloruse, depth,
				 isInfo, colorPtr, 0, colors);
}


/***********************************************************************
 *           SDLDRV_DC_BitmapBits
 */
LONG SDLDRV_DC_BitmapBits(HBITMAP hbitmap, void *bits, LONG count, WORD flags)
{
  BITMAPOBJ *bitmap;
  LONG result;

  if(!(bitmap = (BITMAPOBJ *) GDI_GetObjPtr(hbitmap, BITMAP_MAGIC)))
    return FALSE;

  if(flags == DDB_GET)
    result = SDLDRV_DC_GetBitmapBits(bitmap, bits, count);
  else if(flags == DDB_SET)
    result = SDLDRV_DC_SetBitmapBits(bitmap, bits, count);
  else {
    ERR("Unknown flags value %d\n", flags);
    result = 0;
  }

  GDI_ReleaseObj(hbitmap);
  return result;
}

/***********************************************************************
 *		SDLDRV_DC_CreateBitmap
 */
BOOL SDLDRV_DC_CreateBitmap(HBITMAP hbitmap)
{
  SDL_Surface *surface;
  BITMAPOBJ *bitmap;

  TRACE("(0x%04x)\n", hbitmap);

  if(!(bitmap = (BITMAPOBJ *) GDI_GetObjPtr(hbitmap, BITMAP_MAGIC)))
    return FALSE;

  if(!(surface = SDLDRV_DC_AllocBitmap(bitmap, NULL, 0))) {
    GDI_ReleaseObj(hbitmap);
    return FALSE;
  }

  /* Set bitmap bits */
  if(bitmap->bitmap.bmBits) {
    SDLDRV_DC_BitmapBits(hbitmap, bitmap->bitmap.bmBits,
			 bitmap->bitmap.bmHeight * bitmap->bitmap.bmWidthBytes,
			 DDB_SET );
  }

  GDI_ReleaseObj(hbitmap);

  return TRUE;
}

/***********************************************************************
 *		SDLDRV_DC_BITMAP_DeleteObject
 */
BOOL SDLDRV_DC_BITMAP_DeleteObject(HBITMAP hbitmap, BITMAPOBJ *bitmap)
{
  TRACE("(0x%04x, %p)\n", hbitmap, bitmap);

  SDL_FreeSurface(bitmap->physBitmap);
  TRACE("freed SDL surface %p\n", bitmap->physBitmap);
  bitmap->physBitmap = NULL;
  bitmap->funcs = NULL;

  return TRUE;
}

/***********************************************************************
 *		SDLDRV_DC_GetBitmapBits
 */
static LONG SDLDRV_DC_GetBitmapBits(BITMAPOBJ *bitmap, void *bits, LONG count)
{
  FIXME("(%p, %p, %ld): stub\n", bitmap, bits, count);

  memset(bits, 0, count);

  return count;
}

/***********************************************************************
 *		SDLDRV_DC_BITMAP_SelectObject
 */
HBITMAP SDLDRV_DC_BITMAP_SelectObject(DC *dc, HBITMAP hbitmap, BITMAPOBJ *bitmap)
{
  HBITMAP hPreviousBitmap;
  SDLDRV_PDEVICE *physDev = (SDLDRV_PDEVICE*)dc->physDev;

  TRACE("(%p, 0x%04x, %p)\n", dc, hbitmap, bitmap);

  if(!(dc->flags & DC_MEMORY))
    return 0;

  /* Assure that the bitmap device dependent */
  if(!bitmap->physBitmap && !SDLDRV_DC_CreateBitmap(hbitmap))
    return 0;

  if(bitmap->funcs != dc->funcs) {
    ERR("Trying to select a non-SDL DDB into a SDL DC\n");
    return 0;
  }

  dc->totalExtent.left   = 0;
  dc->totalExtent.top    = 0;
  dc->totalExtent.right  = bitmap->bitmap.bmWidth;
  dc->totalExtent.bottom = bitmap->bitmap.bmHeight;
  physDev->surface = bitmap->physBitmap;

  /* FIXME: Should be done in the common code instead */
  if(dc->hVisRgn) {
    SetRectRgn(dc->hVisRgn, 0, 0,
	       bitmap->bitmap.bmWidth, bitmap->bitmap.bmHeight);
  } else {
    HRGN hrgn;

    if(!(hrgn = CreateRectRgn(0, 0, bitmap->bitmap.bmWidth, bitmap->bitmap.bmHeight)))
      return 0;

    dc->hVisRgn = hrgn;
  }

  hPreviousBitmap = dc->hBitmap;
  dc->hBitmap = hbitmap;

  return hPreviousBitmap;
}

/***********************************************************************
 *		SDLDRV_DC_SetBitmapBits
 */
static LONG SDLDRV_DC_SetBitmapBits(BITMAPOBJ *bitmap, void *bits, LONG count)
{
  FIXME("(%p, %p, %ld): semistub\n", bitmap, bits, count);

  return count;
}

/***********************************************************************
 *		SDLDRV_DC_CreateDIBSection
 */
HBITMAP SDLDRV_DC_CreateDIBSection(
  DC *dc, BITMAPINFO *bmi, UINT usage,
  LPVOID *bits, HANDLE section, DWORD offset,
  DWORD ovr_pitch)
{
  HBITMAP res = 0;
  BITMAPINFOHEADER *bi = &bmi->bmiHeader;
  BITMAPOBJ *bitmap;

  TRACE("(%p, %p, %u, %p, 0x%04x, %ld, %ld)\n",
	dc, bmi, usage, bits, section, offset, ovr_pitch);

  if (bits) *bits = NULL;

  res = CreateDIBitmap(dc->hSelf, bi, 0, NULL, bmi, usage);

  bitmap = (BITMAPOBJ *) GDI_GetObjPtr(res, BITMAP_MAGIC);
  if (bitmap) {
    SDL_Surface *surface = SDLDRV_DC_AllocBitmap(bitmap, (void*)offset, ovr_pitch);
    if (surface && bits) *bits = surface->pixels;
    GDI_ReleaseObj(res);
  }

  return res;
}

/***********************************************************************
 *		SDLDRV_BITMAP_DeleteDIBSection
 */
void SDLDRV_BITMAP_DeleteDIBSection(BITMAPOBJ *bmp)
{
  FIXME("(%p): stub\n", bmp);
}

/***********************************************************************
 *		SDLDRV_BITMAP_SetDIBColorTable
 */
UINT SDLDRV_BITMAP_SetDIBColorTable(BITMAPOBJ *bmp, DC *dc, UINT start, UINT count, const RGBQUAD *colors)
{
  SDL_Surface *surface = bmp->physBitmap;
  SDL_Color *cmap;

  TRACE("(%p): stub\n", bmp);

  cmap = HeapAlloc(GetProcessHeap(), 0, count*sizeof(SDL_Color));

  X11DRV_DIB_GenColorMap(dc, cmap, DIB_RGB_COLORS,
			 surface->format->BitsPerPixel,
			 TRUE, colors, start, count + start);

  SDL_SetPalette(surface, SDL_LOGPAL, cmap, start, count);

  HeapFree(GetProcessHeap(), 0, cmap);

  return 0;
}

/***********************************************************************
 *		SDLDRV_BITMAP_GetDIBColorTable
 */
UINT SDLDRV_BITMAP_GetDIBColorTable(BITMAPOBJ *bmp, DC *dc, UINT start, UINT count, RGBQUAD *colors)
{
  FIXME("(%p): stub\n", bmp);
  return 0;
}

/***********************************************************************
 *		SDLDRV_BITMAP_Lock
 */
INT SDLDRV_BITMAP_Lock(BITMAPOBJ *bmp, INT req, BOOL lossy)
{
  FIXME("(%p): stub\n", bmp);
  return DIB_Status_None;
}

/***********************************************************************
 *		SDLDRV_BITMAP_Unlock
 */
void SDLDRV_BITMAP_Unlock(BITMAPOBJ *bmp, BOOL commit)
{
  FIXME("(%p): stub\n", bmp);
}

/***********************************************************************
 *		SDLDRV_BITMAP_GetDIBits
 */
INT SDLDRV_BITMAP_GetDIBits(
  BITMAPOBJ *bmp, DC *dc, UINT startscan, UINT lines,
  LPVOID bits, BITMAPINFO *info, UINT coloruse, HBITMAP hbitmap)
{
  FIXME("(%p, %p, %u, %u, %p, %p, %u, 0x%04x): stub\n",
	bmp, dc, startscan, lines, bits, info, coloruse, hbitmap);

  return 0;
}


/***********************************************************************
 *		SDLDRV_BITMAP_SetDIBits
 */
INT SDLDRV_BITMAP_SetDIBits(
  BITMAPOBJ *bmp, DC *dc, UINT startscan, UINT lines,
  LPCVOID bits, const BITMAPINFO *info, UINT coloruse, HBITMAP hbitmap)
{
  DWORD width, pitch;
  int height, tmpheight;
  WORD bpp, compr;
  Uint32 R = 0, G = 0, B = 0;

  SDL_Surface *src, *dst = bmp->physBitmap;
  SDL_Rect rsrc, rdst;
  SDL_Color *cmap = NULL;
  int nColors;

  TRACE("(%p, %p, %u, %u, %p, %p, %u, 0x%04x): stub\n",
	bmp, dc, startscan, lines, bits, info, coloruse, hbitmap);

  if (DIB_GetBitmapInfo(&info->bmiHeader, &width, &height, &bpp, &compr) == -1)
    return 0;

  tmpheight = height;
  if (height < 0) height = -height;
  if (!lines || (startscan >= height))
      return 0;

  if (startscan + lines > height) lines = height - startscan;

  pitch = ((width * bpp + 31) &~31) / 8;

  switch (bpp) {
  case 1:
  case 4:
  case 8:
    cmap = SDLDRV_DIB_BuildColorMap(coloruse == DIB_PAL_COLORS ? dc : NULL,
				    coloruse, dc->bitsPerPixel, info, &nColors);
    R = G = B = 0;
    break;

  case 15:
  case 16:
    R = (compr == BI_BITFIELDS) ? *(DWORD *)info->bmiColors       : 0x7c00;
    G = (compr == BI_BITFIELDS) ? *((DWORD *)info->bmiColors + 1) : 0x03e0;
    B = (compr == BI_BITFIELDS) ? *((DWORD *)info->bmiColors + 2) : 0x001f;
    break;

  case 24:
  case 32:
    R = (compr == BI_BITFIELDS) ? *(DWORD *)info->bmiColors       : 0xff0000;
    G = (compr == BI_BITFIELDS) ? *((DWORD *)info->bmiColors + 1) : 0x00ff00;
    B = (compr == BI_BITFIELDS) ? *((DWORD *)info->bmiColors + 2) : 0x0000ff;
    break;

  default:
    break;
  }

  rsrc.x = 0;
  rsrc.y = 0;
  rsrc.w = width;
  rsrc.h = lines;

  rdst.x = 0;
  rdst.y = height - startscan - lines;

  src = SDL_CreateRGBSurfaceFrom((LPVOID)bits, width, height, bpp, pitch,
				 R, G, B, 0);
  if (cmap) SDL_SetPalette(src, SDL_LOGPAL, cmap, 0, nColors);
  SDL_BlitSurface(src, &rsrc, dst, &rdst);
  SDL_FreeSurface(src);

  if (cmap) HeapFree(GetProcessHeap(), 0, cmap);

  return lines;
}

/***********************************************************************
 *		SDLDRV_DC_SetDIBitsToDevice
 */
INT SDLDRV_DC_SetDIBitsToDevice(DC *dc, INT xDest, INT yDest, DWORD cx,
				DWORD cy, INT xSrc, INT ySrc,
				UINT startscan, UINT lines, LPCVOID bits,
				const BITMAPINFO *info, UINT coloruse)
{
  FIXME("(%p, %d, %d, %ld, %ld, %d, %d, %u, %u, %p, %p, %u): stub\n",
	dc, xDest, yDest, cx, cy, xSrc, ySrc, startscan, lines, bits, info, coloruse);

  return 0;
}
