/*
 * Author: Foks
 * originally published at radiokot.ru
 */

#include <cstdio>
#include <cstring>
#include <queue>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

using namespace std;

#define FONT_NAME  "-adobe-helvetica-medium-r-normal--16-*-*-*-p-*-iso8859-1"

#define SYM_PREFIX  "SYMBOL_"

queue<char> charList;
int hexDigits[16];
int charNum;

void AddChar(char x)
{
	printf("    .dw (" SYM_PREFIX "%i<<1)  ; `%c'\n", charNum, x);
	charList.push(x);
	if(x >= '0' && x <= '9')
		hexDigits[x-'0'] = charNum;
	if(x >= 'A' && x <= 'F')
		hexDigits[x+10-'A'] = charNum;
	charNum++;
}

void AddCharRange(char* Name, char Start, char End)
{
	printf("%s:\n", Name);
	for(char i = Start; i <= End; i++)
		AddChar(i);
	printf("\n");
}

void AddHexDigitTable(char* Name)
{
	printf("%s:\n", Name);
	for(char i = 0; i < 16; i++)
	{
		char x = (i<10) ? ('0'+i) : ('A'+i-10);
		if(hexDigits[i] == 0)
			AddChar(x);
		else
			printf("    .dw (" SYM_PREFIX "%i<<1)   ; `%c'\n", hexDigits[i], x);
	}
	printf("\n");
}

int main()
{
	fprintf(stderr, "Rendering font character bitmaps...\n");
	charNum = 1;
	memset(hexDigits, 0, 16);
	// Prepare character table and character list
	AddCharRange("CHAR_TABLE", 32, 'z');
	AddHexDigitTable("HEX_DIGITS");
	//AddChar('@');
	//AddChar('j');
	// Generate characters
	Display* xdisp = XOpenDisplay(NULL);
	if(xdisp)
	{
		// connected to X.
		int screen = XDefaultScreen(xdisp);
		Window xroot = XDefaultRootWindow(xdisp);
		GC xgc = XDefaultGC(xdisp, screen);
		int depth = XDefaultDepth(xdisp, screen);
		unsigned long black = XBlackPixel(xdisp, screen);
		unsigned long white = XWhitePixel(xdisp, screen);
		// load font
		XFontStruct *xfont = XLoadQueryFont(xdisp, FONT_NAME);
		if(xfont)
		{
			// determine font bounds
			int max_height = xfont->max_bounds.ascent + xfont->max_bounds.descent;
			int max_width = xfont->max_bounds.rbearing - xfont->min_bounds.lbearing;
			if(max_width > 16)
				fprintf(stderr, "Warning: max_width = %i > 16\n", max_width);
			// create pixmap
			Pixmap xpix = XCreatePixmap(xdisp, xroot, max_width, max_height, depth);
			// set default font and prepare colors
			XSetFont(xdisp, xgc, xfont->fid);
			XSetBackground(xdisp, xgc, white);
			XSetForeground(xdisp, xgc, black);
			// Output font bounds
			printf(".equ FONT_ASCENT = %i\n", xfont->max_bounds.ascent);
			printf(".equ FONT_HEIGHT = %i\n", max_height);
			printf(".equ FONT_LBEARING = %i\n\n", -xfont->min_bounds.lbearing);
			// render characters from list
			charNum = 1;
			while(!charList.empty())
			{
				char x = charList.front();
				printf(SYM_PREFIX "%i:   ; `%c'\n", charNum, x);
				XCharStruct overall;
				int direction, ascent, descent;
				XTextExtents(xfont, &x, 1, &direction, &ascent, &descent, &overall);
				//fprintf(stderr, "Character `%c'\n", x);
				//fprintf(stderr, "direction: %i\n", direction);
				//fprintf(stderr, "ascent: %i\n", ascent);
				//fprintf(stderr, "descent: %i\n", descent);
				//fprintf(stderr, "overall_lbearing: %i\n", overall.lbearing);
				//fprintf(stderr, "overall_rbearing: %i\n", overall.rbearing);
				//fprintf(stderr, "overall_ascent: %i\n", overall.ascent);
				//fprintf(stderr, "overall_descent: %i\n", overall.descent);
				int width = overall.rbearing - overall.lbearing;
				int height = overall.ascent + overall.descent;
				if(width > 16)
				{
					fprintf(stderr, "Character `%c' has width %i, which is greater than 16.\n", x, width);
					fprintf(stderr, "Stopped.\n", x, width);
					break;
				}
				printf("	.db %i, %i\n", overall.lbearing, overall.ascent);
				printf("    .db %i, %i\n", width, height);
				XDrawImageString(xdisp, xpix, xgc, (overall.lbearing < 0) ? 0 : (-overall.lbearing), overall.ascent, &x, 1);
				XImage* ximg = XGetImage(xdisp, xpix, 0, 0, max_width, max_height, 0xFFFFFFFF, XYPixmap);
				int i, j;
				for(j = 0; j < height; j++)
				{
					printf("    .dw 0b");
					for(i = 0; i < width; i++)
					{
						unsigned long pixel = XGetPixel(ximg, i, j);
						printf((pixel == black) ? "1" : "0");
					}
					for(i = width; i < 16; i++)
						printf("0");
					printf("\n");
				}
				XDestroyImage(ximg);
				charNum++;
				charList.pop();
			}
			// free the font and pixmap
			XFreePixmap(xdisp, xpix);
			XFreeFont(xdisp, xfont);
		} else
			fprintf(stderr, "Failed to load font.\n");
		// close connection
		XCloseDisplay(xdisp);
	} else
		fprintf(stderr, "Failed to connect to X server.\n");
	if(charList.empty())
	fprintf(stderr, "Success: %i characters.\n", charNum);
	return 0;
}

