/** \file
 * \brief Conversion between imDib and imImage
 *
 * See Copyright Notice in im_lib.h
 */


#include <windows.h>
#include <assert.h>

#include "im.h"
#include "im_image.h"
#include "im_dib.h"
#include "im_util.h"


void imDibEncodeFromBitmap(imDib* dib, const unsigned char* data)
{
  int x, y;
  BYTE* bits;
  
  assert(dib);
  assert(dib->bmih->biBitCount > 16);
  assert(data);
  
  if (dib->bmih->biHeight < 0)
    bits = dib->bits + (dib->bits_size - dib->line_size); /* start of last line */
  else
    bits = dib->bits;
  
  for (y = 0; y < abs(dib->bmih->biHeight); y++)
  {
    for (x = 0; x < dib->bmih->biWidth; x++)
    {
      *bits++ = *(data+2);   // R
      *bits++ = *(data+1);   // G
      *bits++ = *(data+0);   // B

      data += 3;

      if (dib->bmih->biBitCount == 32)
        *bits++ = *data++;
    }
    
    bits += dib->pad_size;

    if (dib->bmih->biHeight < 0)
      bits -= 2*dib->line_size;
  }
}

void imDibDecodeToBitmap(const imDib* dib, unsigned char* data)
{
  int x, y, offset;
  unsigned short color;
  BYTE* bits;
  unsigned int rmask = 0, gmask = 0, bmask = 0, 
                roff = 0,  goff = 0,  boff = 0; /* pixel bit mask control when reading 16 and 32 bpp images */
  
  assert(dib);
  assert(dib->bmih->biBitCount > 8);
  assert(data);

  if (dib->bmih->biHeight < 0)
    bits = dib->bits + (dib->bits_size - dib->line_size); /* start of last line */
  else
    bits = dib->bits;
  
  if (dib->bmih->biBitCount == 16)
    offset = dib->line_size;  /* do not increment for each pixel, jump line */
  else
    offset = dib->pad_size;   /* increment for each pixel, jump pad */
  
  if (dib->bmih->biCompression == BI_BITFIELDS)
  {
    unsigned int Mask;
    unsigned int* palette = (unsigned int*)dib->bmic;
    
    rmask = Mask = palette[0];
    while (!(Mask & 0x01))
    {Mask >>= 1; roff++;}
    
    gmask = Mask = palette[1];
    while (!(Mask & 0x01))
    {Mask >>= 1; goff++;}
    
    bmask = Mask = palette[2];
    while (!(Mask & 0x01))
    {Mask >>= 1; boff++;}
  }
  else if (dib->bmih->biBitCount == 16)
  {
    bmask = 0x001F;
    gmask = 0x03E0;
    rmask = 0x7C00;
    boff = 0;
    goff = 5;
    roff = 10;
  }
  
  for (y = 0; y < abs(dib->bmih->biHeight); y++)
  {
    for (x = 0; x < dib->bmih->biWidth; x++)
    {
      if (dib->bmih->biBitCount == 16)
      {
        color = ((unsigned short*)bits)[x];
        *data++ = (unsigned char)((((rmask & color) >> roff) * 255) / (rmask >> roff));
        *data++ = (unsigned char)((((gmask & color) >> goff) * 255) / (gmask >> goff));
        *data++ = (unsigned char)((((bmask & color) >> boff) * 255) / (bmask >> boff));
      }
      else
      {
        *(data+2) = *bits++; // B
        *(data+1) = *bits++; // G
        *(data+0) = *bits++; // R

        data += 3;
        
        if (dib->bmih->biBitCount == 32)
          *data++ = *bits++;
      }
    }
    
    bits += offset;

    if (dib->bmih->biHeight < 0)
      bits -= 2*dib->line_size;
  }
}

imImage* imDibToImage(const imDib* dib)
{
  assert(dib);

  int color_space = IM_RGB;
  if (dib->bmih->biBitCount <= 8)
    color_space = IM_MAP;

  imImage* image = imImageCreate(dib->bmih->biWidth, abs(dib->bmih->biHeight), color_space, IM_BYTE);
  if (!image) 
    return NULL;

  if (image->color_space == IM_MAP)
  {
    image->palette_count = dib->palette_count;
    imDibDecodeToMap(dib, (imbyte*)image->data[0], image->palette);
  }
  else
  {
    if (dib->bmih->biBitCount == 32)
    {
      imImageAddAlpha(image);
      imDibDecodeToRGBA(dib, (imbyte*)image->data[0], (imbyte*)image->data[1], (imbyte*)image->data[2], (imbyte*)image->data[3]);
    }
    else
      imDibDecodeToRGBA(dib, (imbyte*)image->data[0], (imbyte*)image->data[1], (imbyte*)image->data[2], NULL);
  }

  return image;
}

imDib* imDibFromImage(const imImage* image)
{
  assert(image);
  assert(imImageIsBitmap(image));

  if (!imImageIsBitmap(image))
    return NULL;

  int bpp;
  if (image->color_space != IM_RGB)
    bpp = 8;
  else
  {
    if (image->has_alpha)
      bpp = 32;
    else
      bpp = 24;
  }

  imDib* dib = imDibCreate(image->width, image->height, bpp);     
  if (!dib) return NULL;

  if (image->color_space != IM_RGB)
    imDibEncodeFromMap(dib, (const imbyte*)image->data[0], image->palette, image->palette_count);
  else
  {
    if (image->has_alpha)
      imDibEncodeFromRGBA(dib, (const imbyte*)image->data[0], (const imbyte*)image->data[1], (const imbyte*)image->data[2], (const imbyte*)image->data[3]);
    else
      imDibEncodeFromRGBA(dib, (const imbyte*)image->data[0], (const imbyte*)image->data[1], (const imbyte*)image->data[2], NULL);
  }

  return dib;
}

imDib* imDibSectionFromImage(HDC hDC, HBITMAP *bitmap, const imImage* image)
{
  assert(image);
  assert(imImageIsBitmap(image));

  if (!imImageIsBitmap(image))
    return NULL;

  int bpp;
  if (image->color_space != IM_RGB)
    bpp = 8;
  else
  {
    if (image->has_alpha)
      bpp = 32;
    else
      bpp = 24;
  }

  imDib* dib = imDibCreateSection(hDC, bitmap, image->width, image->height, bpp);
  if (!dib) return NULL;

  if (image->color_space != IM_RGB)
    imDibEncodeFromMap(dib, (const imbyte*)image->data[0], image->palette, image->palette_count);
  else
  {
    if (image->has_alpha)
      imDibEncodeFromRGBA(dib, (const imbyte*)image->data[0], (const imbyte*)image->data[1], (const imbyte*)image->data[2], (const imbyte*)image->data[3]);
    else
      imDibEncodeFromRGBA(dib, (const imbyte*)image->data[0], (const imbyte*)image->data[1], (const imbyte*)image->data[2], NULL);
  }

  return dib;
}


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

#include "im_binfile.h"

imImage* imImageLoadFromResource(HMODULE module, LPCTSTR name, int index, int *error)
{
  *error = IM_ERR_OPEN;

  if (!module)
    module = GetModuleHandle(NULL);

  HRSRC mrc = FindResource(module, name, RT_RCDATA); // The type must be RCDATA even for a bitmap resource.
  if (!mrc)
    return NULL;

  HGLOBAL imhandle = LoadResource(module, mrc);
  if (!imhandle)
    return NULL;

  DWORD size = SizeofResource(module, mrc);
  void* buffer = LockResource(imhandle);
  if (!size || !buffer)
    return NULL;

  int old_mode = imBinFileSetCurrentModule(IM_MEMFILE);
  imBinMemoryFileName MemFileName;
  MemFileName.buffer = (unsigned char*)buffer;
  MemFileName.size = (int)size; //The size MUST be set.
  MemFileName.reallocate = 0; //Since we are loading an image we dont need to reallocate. The size is fixed.
  imFile *ifile = imFileOpen((const char*)&MemFileName, error);
  if (!ifile)
  {
    imBinFileSetCurrentModule(old_mode);
    return NULL;
  }

  imImage *myimage = imFileLoadImage(ifile, index, error);
  imFileClose(ifile);
  imBinFileSetCurrentModule(old_mode);

  return myimage;
}
