#include "stdafx.h"

#include "UI.h"

#include <stdio.h>

#include "Objects.h"
#include "Dispatch.h"
#include "CSB.h"
#include "Data.h"

typedef struct {
        ui32      biSize;
        i32       biWidth;
        i32       biHeight;
        ui16      biPlanes;
        ui16      biBitCount;
        ui32      biCompression;
        ui32      biSizeImage;
        i32       biXPelsPerMeter;
        i32       biYPelsPerMeter;
        i32       biClrUsed;
        ui32      biClrImportant;
} CSB_BITMAPINFOHEADER;


typedef struct {
        ui16    bfType;
        ui32    bfSize;
        ui16    bfReserved1;
        ui16    bfReserved2;
        ui32    bfOffBits;
} CSB_BITMAPFILEHEADER;


class BITMAPENTRY
{
public:
  pnt bitmap;
  i32 width;
  i32 height;
  BITMAPENTRY(void){bitmap = NULL;};
  ~BITMAPENTRY(void){if(bitmap!=NULL)UI_free(bitmap);bitmap=NULL;};
};

#if defined _bigEndian
void SwapFileHeader( CSB_BITMAPFILEHEADER* a)
{
    a->bfType = BE16(a->bfType);
    a->bfSize = BE32(a->bfSize);
    a->bfReserved1 = BE16(a->bfReserved1);
    a->bfReserved2 = BE16(a->bfReserved2);
    a->bfOffBits = BE32(a->bfOffBits);
}


void SwapInfoHeader( CSB_BITMAPINFOHEADER* a)
{
    a->biSize = BE32(a->biSize);
    a->biWidth = BE32(a->biWidth);
    a->biHeight = BE32(a->biHeight);
    a->biPlanes = BE16(a->biPlanes);
    a->biBitCount = BE16(a->biBitCount);
    a->biCompression = BE32(a->biCompression);
    a->biSizeImage = BE32(a->biSizeImage);
    a->biXPelsPerMeter = BE32(a->biXPelsPerMeter);
    a->biYPelsPerMeter = BE32(a->biYPelsPerMeter);
    a->biClrUsed = BE32(a->biClrUsed);
    a->biClrImportant = BE32(a->biClrImportant);
}
#endif //_bigendian

void bmpError(char *fname, char *msg, bool Die)
{
  if (!PlayFile.IsOpen())
  {
    char *m;
    m = (char *)UI_malloc(strlen(fname) + strlen(msg) + 100,
                          MALLOC003);
    sprintf(m,"Bitmap file error - filename = %s\n%s",
              fname, msg);
    UI_MessageBox(m, "Error", MESSAGE_OK);
    UI_free (m);
  };
  if (Die) die(0x33dc);
}

class SAFEFILE
{
public:
  i16 f;
  pnt buf;
  SAFEFILE(void){f=-1; buf=NULL;};
  ~SAFEFILE(void)
  {
    if(f>=0)CLOSE(f);f=-1;
    if (buf != NULL) UI_free(buf); buf = NULL;
  };
};

bool ImportBitmap(char *filename1,
                  char *filename2, 
                  BITMAPENTRY *pbme, 
                  bool printIfError,
                  bool dieIfError) 
{
  ui32 width, height;
  i32 size;	
  char *filename;
  i32 destWordWidth, destHeight;
  CSB_BITMAPFILEHEADER bmFileHeader;
  CSB_BITMAPINFOHEADER bmInfoHeader;
  ui16 word0=0, word1=0, word2=0, word3=0;
  ui32 nibble;
  ui32 bmiColors[16];
  ui16 *dstLine, *dstWord;
  ui8  *srcLine, *srcByte;
  i32 srcByteWidth;
  ui32 srcX;
	// TODO: Add your command handler code here
	SAFEFILE f;
  i32 i, j, k;
  //char bitmap[(portraitPixelWidth+8)*portraitPixelHeight];
  filename = filename1;
  f.f = OPEN(filename,"rb");
  if (f.f < 0)
  {
    filename = filename2;
    f.f = OPEN(filename,"rb");
    if (f.f < 0)
    {
      if (!printIfError) return false;
      bmpError(filename," Cannot open file", dieIfError);
      return false;
    };
  };
  if (READ(f.f,sizeof(bmFileHeader),(char *)&bmFileHeader) != sizeof(bmFileHeader))
  {
    if (!printIfError) return false;
    bmpError(filename, "Cannot read bitmap file header",dieIfError);
    return false;
  };
#if defined _bigEndian
  SwapFileHeader(&bmFileHeader);
#endif
  if (bmFileHeader.bfType != ('M'<<8) + 'B')
  {
    if (!printIfError) return false;
    bmpError(filename, "That does not look like a bitmap file",dieIfError);
    return false;
  };
  if (READ(f.f,sizeof(bmInfoHeader),(char *)&bmInfoHeader) != sizeof(bmInfoHeader))
  {
    if (!printIfError) return false;
    bmpError(filename, "Cannot read the InfoHeader",dieIfError);
    return false;
  };
#if defined _bigEndian
  SwapInfoHeader(&bmInfoHeader);
#endif
//  if (   (bmInfoHeader.biWidth != portraitPixelWidth+8)
//       ||(bmInfoHeader.biHeight != 4 * portraitPixelHeight) )
//  {
//    CString msg;
//    msg.Format("The bitmap must be exactly\n%d by %d",
//                portraitPixelWidth+8,
//                4 * portraitPixelHeight);
//    Strange(msg);
//    return;
//  };
  if (bmInfoHeader.biBitCount != 4)
  {
    if (!printIfError) return false;
    bmpError(filename, "That is not a 16-color bitmap",dieIfError);
    return false;
  };
  if (READ(f.f,sizeof(bmiColors),(char *)bmiColors) != sizeof(bmiColors))
  {
    if (!printIfError) return false;
    bmpError(filename, "Cannot read palette",dieIfError);
    return false;
  };
  width = bmInfoHeader.biWidth;
  height = bmInfoHeader.biHeight;
  size = (width+1)/2 * height;
  f.buf = (pnt)UI_malloc(width*height/2, MALLOC004);
  if (f.buf == NULL)
  {
    if (!printIfError) return false;
    bmpError(filename,"Cannot allocate memory for bitmap",dieIfError);
    return false;
  };
  if (READ(f.f, size, f.buf) != size)
  {
    if (!printIfError) return false;
    bmpError(filename,"Cannot read bitmap pixel data",dieIfError);
    return false;
  };
  destWordWidth = (width+15)/16 * 4; //4 words per 16 pixels
  srcByteWidth = (width+1)/2;//4 bits per pixel
  destHeight = height;
  pbme->bitmap = (pnt)UI_malloc(destWordWidth*2*destHeight,
                                MALLOC005);
  pbme->width = width;
  pbme->height = height;
  if (pbme->bitmap == NULL)
  {
    if (!printIfError) return false;
    bmpError(filename,"Cannot allocate memory for bitmap",dieIfError);
    return false;
  };
  srcLine = (ui8 *)f.buf + srcByteWidth * (destHeight-1);
  dstLine = (ui16 *)pbme->bitmap;
  for (i=0; 
       i<destHeight; 
       i++, srcLine-=srcByteWidth, dstLine+=destWordWidth)
  {
    srcByte = srcLine;
    dstWord = dstLine;
    word0 = word1 = word2 = word3 = 0;
    srcX = 0;
    for (j=0; j<destWordWidth/4; j++)
    {
      for (k=0; k<16; k++, srcX++)
      {
        if (srcX >= width) nibble = 0;
        else
        {
          if ((k & 1) == 0)
          {
            nibble = ((*srcByte) >> 4) & 15;
          }
          else
          {
            nibble = (*srcByte) & 15;
            srcByte++;
          };
        };
        word0 <<= 1;
        word1 <<= 1;
        word2 <<= 1;
        word3 <<= 1;
        word0 |= (nibble >> 3) & 1;
        word1 |= (nibble >> 2) & 1;
        word2 |= (nibble >> 1) & 1;
        word3 |= (nibble >> 0) & 1;
      };
      dstWord[0] = LE16(word3);
      dstWord[1] = LE16(word2);
      dstWord[2] = LE16(word1);
      dstWord[3] = LE16(word0);
      dstWord += 4;
    };
  };
  return true;
}

struct PORTRAIT
{
  i32 index;
  PORTRAIT *next;
  char portrait[29*16];
};

class PORTRAITS
{
public:
  PORTRAIT *first;
  PORTRAITS(void){first=NULL;};
  ~PORTRAITS(void);
};

PORTRAITS::~PORTRAITS(void)
{
  PORTRAIT *temp;
  while (first != NULL)
  {
    temp = first;
    first = temp->next;
    delete temp;
  };
};

ui8 errorPortraitBitmap[] = 
{
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0x83,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xBF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xBF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0x86,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xBE,0x8F,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xBE,0x7F,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xBE,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xBE,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0x82,0xEF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFE,0xE8,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFE,0xE7,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFE,0xEF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFE,0xEF,0x00,0x00,0x00,0x00,
 0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xEE,0x00,0x00,0x00,0x00,
 0x00,0x00,0xF7,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xEE,0x00,0x00,0x00,0x00,
 0x00,0x00,0xF7,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xEE,0x00,0x00,0x00,0x00,
 0x00,0x00,0xF7,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xEE,0x00,0x00,0x00,0x00,
 0x00,0x00,0xF6,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,
 0x00,0x00,0xF6,0x8F,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,
 0x00,0x00,0xF6,0x7F,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,
 0x00,0x00,0xF6,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0x0E,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFE,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFE,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFE,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFE,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
 0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
};

void GetErrorPortrait(BITMAPENTRY *bitmapentry)
{
  bitmapentry->width = 32;
  bitmapentry->height = 29;
  bitmapentry->bitmap = (pnt)UI_malloc(32*29/2, MALLOC006);
  if (bitmapentry->bitmap == NULL)
  {
    die(0xcb4a,"Cannot allocate memory");
  };
  memcpy(bitmapentry->bitmap,errorPortraitBitmap,32*29/2);
}

PORTRAITS portraitCache;

pnt GetExternalPortraitAddress(i32 index)
{
  BITMAPENTRY bitmapentry;
  PORTRAIT *pp;
  char filename1[30];
  char filename2[30];
  for (pp=portraitCache.first;
       pp != NULL;
       pp = pp->next)
  {
    if (pp->index == index) return pp->portrait;
  };
  sprintf(filename2,"Portrait%03d.bmp",index);
  sprintf(filename1,"Portraits/Portrait%03d.bmp",index);
  if (!ImportBitmap(filename1,filename2,&bitmapentry, true, false))
  {
    GetErrorPortrait(&bitmapentry);
  };
  if (  (bitmapentry.width != 32)
      ||(bitmapentry.height != 29))
  {
    bmpError(filename2,"Portrait bitmap not 32 by 29",false);
    GetErrorPortrait(&bitmapentry);
  };
  pp = new PORTRAIT;
  pp->index = index;
  memcpy(pp->portrait, bitmapentry.bitmap, 32*29/2);
  pp->next = portraitCache.first;
  portraitCache.first = pp;
  return pp->portrait;
}

