#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <direct.h>

#define MAX_PALS	32


typedef unsigned char u8;


struct _finddata_t	filedata;

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));
}

void 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;

	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);
	handle = _findfirst( tmp, &filedata );
	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);
		}

		i = _findnext( handle, &filedata );
	}

	qsort(pals,numPals,sizeof(mypal),PalSort);
	for(i=0;i<numPals;i++)
	{
		printf("%s\n",pals[i].name);
	}

	printf("%d palettes read\n",numPals);

	_getcwd(curDir,80);
	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);
	}

	handle = _findfirst( "*.bmp", &filedata );
	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");
		}
		i = _findnext( handle, &filedata );
	}
}

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,sizeof(BITMAPFILEHEADER),stream);
	if(fileHeader.bfType!=0x4D42)
	{
		return(2);
	}

	fread(&infoHeader,1,sizeof(BITMAPINFOHEADER),stream);

	*sx=infoHeader.biHeight;
	*sy=infoHeader.biWidth;

	if(infoHeader.biBitCount != 8)
	{
		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,sizeof(BITMAPFILEHEADER),stream);
	if(fileHeader.bfType!=0x4D42)
	{
		return(2);
	}

	fread(&infoHeader,1,sizeof(BITMAPINFOHEADER),stream);

	x=*sx=infoHeader.biWidth;
	y=*sy=infoHeader.biHeight;

	if(infoHeader.biBitCount != 24)
	{
		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;

	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);
}

