//#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
//#include <io.h>
//#include <direct.h>

#define MAX_PALS	32

#define BI_RGB 0 
#define BI_RLE8 1
#define BI_RLE4 2

#define BYTE  unsigned char
#define DWORD int
#define LONG  long int
#define UINT  unsigned int
#define WORD  unsigned short 

#define LPSTR  char*

#define BOOL  int
#define FALSE  0
#define TRUE  1

// MS-Windows bitmaps' definition:

//#include <dirent.h>
#include "dosfile.h"

typedef struct tagBITMAPFILEHEADER
{
 WORD bfType;
 DWORD bfSize;
 WORD bfReserved1;
 WORD bfReserved2;
 DWORD bfOffBits;
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER
{
 DWORD biSize;
 LONG biWidth;
 LONG biHeight;
 WORD biPlanes;
 WORD biBitCount;
 DWORD biCompression;
 DWORD biSizeImage;
 LONG biXPelsPerMeter;
 LONG biYPelsPerMeter;
 DWORD biClrUsed;
 DWORD biClrImportant;
} BITMAPINFOHEADER;

typedef unsigned char u8;

#define LINUX 1

#ifdef LINUX
struct finddata_t	filedata;
#else
struct _finddata_t	filedata;
#endif

BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;

char	curDir[80];

int		numPals;
typedef struct _mypal
{
	unsigned char *name;
	unsigned char *data;
} mypal;
mypal		pals[MAX_PALS];

char palDir[80];


int Load8BitBMP(char *fn,int *sx,int *sy,unsigned char **data,unsigned char **pal);
int Load24BitBMP(char *fn,int *sx,int *sy,unsigned char **data);
int QuantizeBest(int sx,int sy,unsigned char *src,unsigned char *dest,char *name);


static void Plot( u8 *data, int x, int y, int w, int h, u8 c )
{
	data[(y*w)+x] = c;
}


static void MaskCorners( u8 *data, int w, int h )
{
	int
		i,
		j;

	for (i=0; i<6; i++)
	{
		j = (w - i - 1);

		Plot( data, i, 0, w, h, 0 );
		Plot( data, j, 0, w, h, 0 );
		Plot( data, i, h-1, w, h, 0 );
		Plot( data, j, h-1, w, h, 0 );
	}

	for (i=0; i<3; i++)
	{
		j = (w - i - 1);

		Plot( data, i, 1, w, h, 0 );
		Plot( data, j, 1, w, h, 0 );
		Plot( data, i, h-2, w, h, 0 );
		Plot( data, j, h-2, w, h, 0 );
	}

	for (i=0; i<2; i++)
	{
		j = (w - i - 1);

		Plot( data, i, 2, w, h, 0 );
		Plot( data, j, 2, w, h, 0 );
		Plot( data, i, h-3, w, h, 0 );
		Plot( data, j, h-3, w, h, 0 );
	}

	j = (w - 1);

	Plot( data, 0, 3, w, h, 0 );
	Plot( data, j, 3, w, h, 0 );
	Plot( data, 0, h-4, w, h, 0 );
	Plot( data, j, h-4, w, h, 0 );
}


int PalSort(const void *a,const void *b)
{
	return(strcmp(((mypal *)a)->name,((mypal *)b)->name));
}

int main(int argc, char **argv)
{
	int	i,j,k;
	unsigned char *data,*data2;
	char	tmp[80];
	char	tmp2[80];
	int	sx,sy;
	int	writePals;
	long
		handle;

	strcpy(palDir,"./");
	writePals=0;

	printf("Wordsize = %d, DWORD = %d, long = %d\n", sizeof( WORD ), sizeof (DWORD ), sizeof( LONG ));

	for(i=1;i<argc;i++)
	{
		if(!stricmp(argv[i],"-paldir"))
		{
			i++;
			strcpy(palDir,argv[i]);
			if(palDir[strlen(palDir-1)]!='/') strcat(palDir,"/");
		}
		else if(!stricmp(argv[i],"-writepals"))
		{
			writePals=1;
		}
	}

	memset(pals,0,sizeof(pals));
	numPals=0;
	sprintf(tmp,"%s*.bmp",palDir);
#ifdef LINUX
	handle = findfirst( tmp, &filedata, -1 );
#else
	handle = _findfirst( tmp, &filedata );
#endif
	i = (int)handle;
	while (i >= 0)
	{
		sprintf(tmp2,"%s%s",palDir,filedata.name);
		printf("Reading palette from %s...",tmp2);
		j=Load8BitBMP(tmp2,&sx,&sy,&data,&pals[numPals].data);
		if(j)
		{
			printf("error %02x\n",j);
		}
		else
		{
			printf("ok\n");
			pals[numPals].name=(char *)malloc(strlen(filedata.name)+1);
			strcpy(pals[numPals].name,filedata.name);
			numPals++;
			free(data);
		}

#if LINUX
		i = findnext( &filedata );
#else
		i = _findnext( handle, &filedata );
#endif
	}

	qsort(pals,numPals,sizeof(mypal),PalSort);
	for(i=0;i<numPals;i++)
	{
		printf("%s\n",pals[i].name);
	}

	printf("%d palettes read\n",numPals);

#if LINUX
	getcwd(curDir,80);
#else
	_getcwd(curDir,80);
#endif
	for(j=strlen(curDir)-1;j>=0;j--) if(curDir[j]=='/') break;
	if(j) j++;
	for(i=0;curDir[i+j];i++) curDir[i]=curDir[i+j];
	curDir[i]=0;

	if(writePals)
	{
		FILE *out;

		sprintf(tmp,"%ssuperpal.bin",palDir);
		out=fopen(tmp,"wb");
		if(!out)
		{
			printf("can't write superpalettes\n");
			exit(1);
		}
		for(i=0;i<numPals;i++)
		{
			for(j=0;j<256;j++)
			{
				int r,g,b;

				r=(pals[i].data[j*3+0]); r>>=3;
				g=(pals[i].data[j*3+1]); g>>=3;
				b=(pals[i].data[j*3+2]); b>>=3;

				if (j)
					k=(r<<11) | (g<<6) | (b<<1) | 1;
				else
					k = 0;

				fputc(k>>8,out);
				fputc(k&0xFF,out);
			}
		}
		fclose(out);
	}

	if(!numPals)
	{
		exit(1);
	}

#ifdef LINUX
	handle = findfirst( "*.bmp", &filedata, -1 );
#else
	handle = _findfirst( "*.bmp", &filedata );
#endif
	i = (int)handle;
	while (i >= 0)
	{
		j=Load24BitBMP(filedata.name,&sx,&sy,&data);
		if(j)
		{
			printf("error %02x\n",j);
		}
		else
		{
			FILE *out;

			data2=(unsigned char *)malloc(sx*sy);
			strcpy(tmp,filedata.name);
			for(j=0;j<strlen(tmp);j++) if(tmp[j]=='.') tmp[j]=0;
			k=QuantizeBest(sx,sy,data,data2,tmp);
			MaskCorners( data2, sx, sy );
			strcat(tmp,".raw");
			out=fopen(tmp,"wb");
			fputc(k,out);
			fwrite(data2,1,sx*sy,out);
			fclose(out);
			printf("ok\n");
		}
#if LINUX
		i = findnext( &filedata );
#else
		i = _findnext( handle, &filedata );
#endif
	}
}

int Load8BitBMP(char *fn,int *sx,int *sy,unsigned char **data,unsigned char **pal)
{
	FILE	*stream;
	int	numCols;
	int	i,j,k;

	stream=fopen(fn,"rb");
	if(!stream) return(1);

	fread(&fileHeader,1,14 /*sizeof(BITMAPFILEHEADER)*/,stream);
	if(fileHeader.bfType!=0x4D42)
	{
		return(2);
	}

	fread(&infoHeader,1,40/*sizeof(BITMAPINFOHEADER)*/,stream);

	*sx=infoHeader.biHeight;
	*sy=infoHeader.biWidth;

	printf("size = %d, Height and width = %d, %d\n", 
	       infoHeader.biSize,
	       infoHeader.biHeight,
	       infoHeader.biWidth);

	printf("planes = %d\n", infoHeader.biPlanes );

	if(infoHeader.biBitCount != 8)
	{
	   printf("Bit count is wrong: %d, should be 8\n", infoHeader.biBitCount );
	   return(3);
	}

	if(infoHeader.biCompression != BI_RGB)
	{
		return(4);
	}

	numCols=infoHeader.biClrUsed;
	if(!numCols) numCols=256;

	*pal=(unsigned char *)malloc(numCols * 3);
	*data=(unsigned char *)malloc((*sx) * (*sy));
	if(!data || !pal) return(5);
	memset(*pal,0,768);

	for(i=0;i<numCols;i++)
	{
		(*pal)[i*3+2]=fgetc(stream);
		(*pal)[i*3+1]=fgetc(stream);
		(*pal)[i*3+0]=fgetc(stream);
		fgetc(stream);
	}

	fseek(stream,fileHeader.bfOffBits,SEEK_SET);
	k=4-((*sx) % 4); if(k==4) k=0;

	for(i=(*sy)-1;i>=0;i--)
	{
		for(j=0;j<*sx;j++)
		{
			(*data)[i*(*sx) + j]=fgetc(stream);
		}
		for(j=0;j<k;j++) fgetc(stream);
	}

	fclose(stream);
	return(0);
}



int Load24BitBMP(char *fn,int *sx,int *sy,unsigned char **data)
{
	FILE	*stream;
	int	i,j,k;
	unsigned char *c;
	int	x,y;

	stream=fopen(fn,"rb");
	if(!stream) return(1);

	fread(&fileHeader,1,14 /*sizeof(BITMAPFILEHEADER) */,stream);
	if(fileHeader.bfType!=0x4D42)
	{
		return(2);
	}

	fread(&infoHeader,1,40/*sizeof(BITMAPINFOHEADER)*/,stream);

	x=*sx=infoHeader.biWidth;
	y=*sy=infoHeader.biHeight;

	if(infoHeader.biBitCount != 24)
	{
	   printf("Failed to load 24 bit thing.\n");
		return(3);
	}

	if(infoHeader.biCompression != BI_RGB)
	{
		return(4);
	}

	*data=(unsigned char *)malloc((*sx) * (*sy) * 3);
	if(!data) return(5);

	fseek(stream,fileHeader.bfOffBits,SEEK_SET);
	k=4-((*sx) % 4); if(k==4) k=0;

	printf("size = %d, Height and width = %d, %d\n", 
	       infoHeader.biSize,
	       infoHeader.biHeight,
	       infoHeader.biWidth);

	printf("size = x =%d, y = %d\n", *sx, *sy );

	printf("planes = %d\n", infoHeader.biPlanes );

	printf("My color table is %d * %d = %d, offset = %d\n", *sx, *sy, (*sx) * (*sy) * 3, fileHeader.bfOffBits );

	memset(*data,0,x*y*3);

	for(i=0;i<y;i++)
	{
		c=(*data) + ((y-i-1)*x*3);

		for(j=0;j<x;j++)
		{
			c[j*3+2]=fgetc(stream);
			c[j*3+1]=fgetc(stream);
			c[j*3+0]=fgetc(stream);
		}
//		for(j=0;j<k*3;j++) fgetc(stream);
	}

	fclose(stream);
	return(0);
}

int FindColor(unsigned char *pal,int r,int g,int b,int *err)
{
	int d,bd,i,bi;

	bd=9999999;
	for(i=1;i<256;i++)
	{
		d=(r-(int)pal[i*3+0])*(r-(int)pal[i*3+0])+
		  (g-(int)pal[i*3+1])*(g-(int)pal[i*3+1])+
		  (b-(int)pal[i*3+2])*(b-(int)pal[i*3+2]);
		if(d<bd)
		{
			bd=d;
			bi=i;
		}
	}
	*err=bd;
	return(bi);
}

int Quantize(int sx,int sy,unsigned char *src,unsigned char *dest,unsigned char *pal)
{
	int i,k,l,m;

	m=0;
	for(i=0;i<sx*sy;i++)
	{
		k=FindColor(pal,src[0],src[1],src[2],&l);
		*(dest++)=k;
		src+=3;
		m+=l;
	}

	return(m);
}

int Clamp(int t0)
{
	if(t0<0)	t0=0;
	if(t0>255)	t0=255;
	return(t0);
}

void Add(unsigned char *d,int a,int b,int c,int f)
{
	int i;

	i=(int)d[0]; i+=a*f/8; i=Clamp(i); d[0]=i;
	i=(int)d[1]; i+=b*f/8; i=Clamp(i); d[1]=i;
	i=(int)d[2]; i+=c*f/8; i=Clamp(i); d[2]=i;

}

void Quantize2(int sx,int sy,unsigned char *src,unsigned char *dest,unsigned char *pal)
{
	int i,j,k,l,m;
	int e0,e1,e2;

	m=0;
	for(i=0;i<sy;i++)
	{
		for(j=0;j<sx;j++)
		{
			k=FindColor(pal,src[0],src[1],src[2],&l);
			*(dest++)=k;

			e0=(int)src[0]-(int)pal[k*3+0];
			e1=(int)src[1]-(int)pal[k*3+1];
			e2=(int)src[2]-(int)pal[k*3+2];

			if(j!=sx-1)
				Add(&src[3],e0,e1,e2,2);
			if(i!=sy-1)
				Add(&src[sx*3],e0,e1,e2,1);
			if(i!=sy-1 && j!=sx-1)
				Add(&src[sx*3+3],e0,e1,e2,0);

			src+=3;
		}
	}
}


int QuantizeBest(int sx,int sy,unsigned char *src,unsigned char *dest,char *name)
{
	int i,d,bd,bi;

	bd=9999999;
	for(i=0;i<numPals;i++)
	{
		d=Quantize(sx,sy,src,dest,pals[i].data);
		if(d<bd || i==0)
		{
			bd=d;
			bi=i;
		}
	}
	printf("%010d : converted [%s\\%s] using palette [%s]\n",bd,curDir,name,pals[bi].name);
	Quantize2(sx,sy,src,dest,pals[bi].data);

	return(bi);
}

