/*
 * Copyright (C) 2005-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

/***********************************************************************/
/* Set this to '1' to enable debugging information */
#define DEBUG_ENABLE		0
#define WARNINGS		1

/***********************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>

#include "../lib/glue-log.h"

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

/* debugging macro */
#if DEBUG_ENABLE
#define DEBUG(fmt, arg...) \
faum_log(FAUM_LOG_DEBUG, "generate_ttf_glyphs", "0", "%s:% 4d: " fmt , \
	__FUNCTION__, __LINE__, arg);
#else
#define DEBUG(fmt, arg...)
#endif

/* same for warnings (if something is not being simulated correctly, but should
 * continue working without problems) */
#if WARNINGS
#define WARN(fmt, arg...) \
faum_log(FAUM_LOG_WARNING, "generate_ttf_glyphs", "0", "%s:% 4d: " fmt , \
	__FUNCTION__, __LINE__, arg);
#else
#define WARN(fmt, arg...)
#endif

/* same for errors (if something definitely went wrong, but the component,
 * continues working) */
#define ERROR(fmt, arg...) \
faum_log(FAUM_LOG_ERROR, "generate_ttf_glyphs", "0", "%s:% 4d: " fmt , \
	__FUNCTION__, __LINE__, arg);


/***********************************************************************/
/* defines (concerning the amount of certain objects)
 */
/***********************************************************************/

/* max width and height of screen, used for current_screen variable which holds
 * current screen in unsigned ints (-rgb)
 */
#define MAX_WIDTH	1600
#define MAX_HEIGHT	1280
/* corresponding to -> unsigned int <- currentscreen */
#define DEPTH		32

#define MAX_GLYPH_WIDTH		64
#define MAX_GLYPH_HEIGHT 	64
#define MAX_CHARS		256

/* set this to lower values to get less MUSTHAVEs and more MAYBEs */
#define MUSTHAVE_THRESHOLD	70

#define WHITE			240
#define ABSWHITE		254

#define MUSTHAVE       0
#define MAYBE        128
#define MUSTNOT      255

/***********************************************************************/
/* globals 
 */
/***********************************************************************/

/* current width and height of the screen that is used */
static int screen_w = -1;
static int screen_h = -1;

static unsigned int num_glyphs=0;

static struct {
	unsigned char ord;
	int xmin, ymin, xmax, ymax;
	unsigned char pattern[MAX_GLYPH_WIDTH][MAX_GLYPH_HEIGHT];
} glyph[MAX_CHARS];


/* containing the current screen as [-|r|g|b] in an int */
static unsigned long current_screen[MAX_HEIGHT][MAX_WIDTH];

/***********************************************************************/
/* prototypes
 */
/***********************************************************************/
static unsigned char 
get_brightness(unsigned long);



/*
 * load a screenshot from ppm format into memory
 */
static void
load_screenshot(char* filename)
{
	
	FILE *fp;
	char line[200];  /* read buf */
	int x,y; /* loop vars */
	int gw; /* max grayval */
	int r, g, b;
	int ret;
	char *s;

	assert(filename != NULL);
	
	DEBUG("reading screenshot %s\n", filename);

	/*
	 * Open screenshot file.
	 */
	fp = fopen(filename, "r");
	if (fp == NULL) {
		ERROR("can't open snapshot file '%s': %s\n", filename,
			strerror(errno));
		return;
	}

	/* read header */
	s = fgets(line, 200, fp);
	assert(s != NULL);
	if (strcmp(line,"P3\n")!=0) {
		ERROR("%s", "wrong file format!\n");
		goto done;
	}

	/* ignore comments */
	do {
		s = fgets(line, 200, fp);
		assert(s != NULL);
	} while (line[0]=='#');

	/* read width and height */
	sscanf(line, "%d %d", &screen_w, &screen_h);

	/* ignore comments */
	do {
		s = fgets(line, 200, fp);
		assert(s != NULL);
	} while (line[0]=='#');

	/* read max grayvalue */
	sscanf(line, "%d", &gw);

	r=0;
	g=0;
	b=0;
	for (y = 0; y < screen_h; y ++) {
		for (x = 0; x < screen_w; x++) {
			ret = fscanf(fp, "%d %d %d", &r, &g, &b);
			assert(0 < ret);
			current_screen[y][x] = (r<<16) + (g<<8) + (b);
		}
	}

done:	;
	/*
	 * Close screenshot file.
	 */
	ret = fclose(fp);
	assert(0 <= ret);
}


/*
 * load a text file that describes what glyphs are to be found in the
 * corresponding screenshot
 */
static void
load_asciitable(char* filename)
{
	FILE *fp;
	int count=0;
	int ret;
	int sr;

	assert(filename != NULL);
	
	DEBUG("reading ascii table %s\n", filename);

	/*
	 * Open screenshot file.
	 */
	fp = fopen(filename, "r");
	if (fp == NULL) {
		ERROR("can't open snapshot file '%s': %s\n", filename,
			strerror(errno));
		return;
	}
			
	
	do {
		sr=fscanf (fp, "%c ", &glyph[count].ord);
		++count;
	} while (sr!=EOF);

	--count;

	/*
	 * Close screenshot file.
	 */
	ret = fclose(fp);
	assert(0 <= ret);

	/* output to check loading */
#if DEBUG_ENABLE
	{
		int i;
		for (i=0; i<count; ++i) {
			fprintf(stderr, "%2i [%c]  ", i, glyph[i].ord);
		}
		fprintf(stderr, "\n");
	}
#endif

}



/*
 * get brightness of a color value given as 'int' in c
 * according to YUV color model
 */
static unsigned char
get_brightness(unsigned long c) 
{
	int ret;

	/* if complete red, return maximum brightness */
	if ( ((c>>16) & 0xff)==255) return 255;
	
	ret=0.299*((c>>16) & 0xff) + 0.587*((c>>8) & 0xff) + 0.114*(c & 0xff);

	if (ret<0) {
		return 0;
		
	}
	if (ret>255) {
		return 255;
	}
	
	return ( (unsigned char) ret );
}

/* 
 * adds a single pixel to glyph g
 */
static void
add_pixel(int g, int x, int y) 
{
	
	/* first check if we are in boundaries of the screen */
	if ( (x < 0) || (screen_w < x) || (y < 0) || (screen_h < y) ) {
		return;
	}

	if (((current_screen[y][x]>>24)&0xff)==0xff) {
		return;
	}

	/* only count as pixel of glyph if it is not a helper pixel */
	if (get_brightness(current_screen[y][x])<WHITE) {
		if (glyph[g].xmin==-1) {
			glyph[g].xmin=x;
			glyph[g].xmax=x;
			glyph[g].ymin=y;
			glyph[g].ymax=y;
		} else {
			/* update min/ax x/y */
			if (x<glyph[g].xmin) 
				glyph[g].xmin=x;
			else if (x>glyph[g].xmax)
				glyph[g].xmax=x;
			if (y<glyph[g].ymin)
				glyph[g].ymin=y;
			else if (y>glyph[g].ymax)
				glyph[g].ymax=y;
		}

	}
	
	/* mark as done */
	current_screen[y][x]=(0xFF<<24) | current_screen[y][x];
	if (y>0) {
		if ( (get_brightness(current_screen[y-1][x])<ABSWHITE) 
		      && ( ((current_screen[y-1][x]>>24)&0xff)!=0xff) ) {
			add_pixel(g, x, y-1);
		}
	}
	
	if (x<screen_w-1) {
		if ( (get_brightness(current_screen[y][x+1])<ABSWHITE)
		    && ( ((current_screen[y][x+1]>>24)&0xff)!=0xff) ) {
			add_pixel(g, x+1, y);
		}

		if (y<screen_h-1) {
			if ( (get_brightness(current_screen[y+1][x+1])<ABSWHITE)
		            && ( ((current_screen[y+1][x+1]>>24)&0xff)!=0xff) ) {
				add_pixel(g, x+1, y+1);
			}
		}
		if (y>0) {
			if ( (get_brightness(current_screen[y-1][x+1])<ABSWHITE)
			    && ( ((current_screen[y-1][x+1]>>24)&0xff)!=0xff) ) {
				add_pixel(g, x+1, y-1);
			}
		}
	}
	
	if (y<screen_h-1) {
		if ( (get_brightness(current_screen[y+1][x])<ABSWHITE)
		    && ( ((current_screen[y+1][x]>>24)&0xff)!=0xff) ) {
			add_pixel(g, x, y+1);
		}
	}

	if (x>0) {
		if ( (get_brightness(current_screen[y][x-1])<ABSWHITE)
		    && ( ((current_screen[y][x-1]>>24)&0xff)!=0xff) ) {
			add_pixel(g, x-1, y);
		}

		if (y<screen_h-1) {
			if ( (get_brightness(current_screen[y+1][x-1])<ABSWHITE)
			    && ( ((current_screen[y+1][x-1]>>24)&0xff)!=0xff) ) {
				add_pixel(g, x-1, y+1);
			}
		}
		if (y>0) {
			if ( (get_brightness(current_screen[y-1][x-1])<ABSWHITE)
  			    && ( ((current_screen[y-1][x+1]>>24)&0xff)!=0xff) ) {
				add_pixel(g, x-1, y-1);
			}
		}
	}
	
}

/*
 * starts a glyph from position x/y and adds all connected pixels to it */
static void
create_single_glyph(int glyphnum, int x, int y) 
{
	int xi, yi;
	int xmin, xmax, ymin, ymax;

	/* get bounding box around glyph */
	add_pixel(glyphnum, x, y);
	
	/* add pattern */
	xmin=glyph[glyphnum].xmin;
	xmax=glyph[glyphnum].xmax;
	ymin=glyph[glyphnum].ymin;
	ymax=glyph[glyphnum].ymax;
	for (xi=xmin; xi<=xmax; ++xi) {
		for (yi=ymin; yi<=ymax; ++yi) {
			if (get_brightness(current_screen[yi][xi])<MUSTHAVE_THRESHOLD) {
				glyph[glyphnum].pattern[xi-xmin][yi-ymin]=MUSTHAVE;
			} else if (get_brightness(current_screen[yi][xi])<WHITE) {
				glyph[glyphnum].pattern[xi-xmin][yi-ymin]=MAYBE;
			} /* else remain MUSTNOT */
		}
	}
	
	DEBUG("generated glyph %d, %d/%d-%d/%d\n", glyphnum, 
			glyph[glyphnum].xmin, 
			glyph[glyphnum].ymin, 
			glyph[glyphnum].xmax, 
			glyph[glyphnum].ymax 
			);
}


/*
 * build pattern of each glyph in the screen
 */
static void
generate_glyphs(void)
{
	int x, y;

	DEBUG("%s", "Generating glyphs...\n");
	
	for (y=0; y<screen_h; ++y) {
		for (x=0; x<screen_w; ++x) {
			/* look for a pixel != white */
			/* red (255/0/0) and background white (255/253/253) will
			 * return 255 as brightness */
			if (get_brightness(current_screen[y][x])>=ABSWHITE) continue;
			/* and != done */
			if ( ((current_screen[y][x]>>24)&0xff)==0xff) continue;
			
			DEBUG("Beginning glyph %d at position %d/%d (b=%d)\n", 
				num_glyphs, x, y,
				get_brightness(current_screen[y][x]));
			
			create_single_glyph(num_glyphs, x, y);

			++num_glyphs;
		}
	}
	DEBUG("Have %d glyphs now\n", num_glyphs);
}
	
/*
 * store information of each glyph in a file
 */
static void
save_glyphs(void)
{
	FILE *fp;
	int c;

	DEBUG("Saving %d glyphs\n", num_glyphs);

	fp = fopen("ttf_samples.c", "w");
	assert(fp != (FILE *) 0);

	fprintf(fp, "/*\n");
	fprintf(fp, " * WARNING:\n");
	fprintf(fp, " *\n");
	fprintf(fp, " * This file is generated by ./generate_ttf_glyphs!\n");
	fprintf(fp, " * Do not edit here!\n");
	fprintf(fp, " */\n");

	fprintf(fp, "#include \"ttf_samples.h\"\n\n");

	fprintf(fp, "/* the number of samples in the array */\n");

	fprintf(fp, "const int NSAMPLES2 = %d;\n\n", num_glyphs);

	fprintf(fp, "ttf_sample_struct sample2[] = {\n");

	for (c = 0; c < num_glyphs; c++) {		
		int x, y;
		int xmin, ymin, xmax, ymax;
		
		xmin=0;
		xmax=glyph[c].xmax-glyph[c].xmin;
		ymin=0;
		ymax=glyph[c].ymax-glyph[c].ymin;

		fprintf(fp, "\t{ %d, \n", glyph[c].ord);

		fprintf(fp, "\t %d, \n", (xmax-xmin+1)*(ymax-ymin+1));
		fprintf(fp, "\t %d, \n", xmax-xmin+1);
		fprintf(fp, "\t %d, {\n", ymax-ymin+1);

		for (y = ymin; y <= ymax; y++) {
			unsigned char bits;

			bits = 0;
			fprintf(fp, "\t\t/* ");
			for (x = xmin; x <= xmax; x++) {
				if (glyph[c].pattern[x][y]==MUSTHAVE) {
					fprintf(fp, "@");
				} else if (glyph[c].pattern[x][y]==MAYBE)  {
					fprintf(fp, "o");
				} else {
					fprintf(fp, ".");
				}
			}
			fprintf(fp, " */  ");
			for (x = xmin; x <= xmax; x++) {
				if (glyph[c].pattern[x][y]==MUSTHAVE) {
					fprintf(fp, "MUSTHAVE, ");
				} else if (glyph[c].pattern[x][y]==MAYBE)  {
					fprintf(fp, "MAYBE, ");
				} else {
					fprintf(fp, "MUSTNOT, ");
				}
			}
			fprintf(fp, "\n");
		}
		
		fprintf(fp, "\t}},\n");
	}

	fprintf(fp, "};\n");

	(void) fclose(fp);

}
	
int
main(int argc, char** argv)
{
	int i, x, y;

	if (argc<3) {
		fprintf(stderr, "usage: %s <screenshot> <asciitable>\n", argv[0]);
		exit(1);
	}
	

	/* init */
	 memset(current_screen, 0, sizeof(unsigned long)*MAX_WIDTH*MAX_HEIGHT);
	 for (i=0; i<MAX_CHARS; ++i) {
		 glyph[i].ord=0;
		 glyph[i].xmin=-1;
		 glyph[i].xmax=-1;
		 glyph[i].ymin=-1;
		 glyph[i].ymax=-1;
		 for (x=0; x<MAX_GLYPH_WIDTH; ++x) {
			 for (y=0; y<MAX_GLYPH_HEIGHT; ++y) {
				 glyph[i].pattern[x][y]=MUSTNOT;
			 }
		 }
	 }

	load_screenshot(argv[1]);

	load_asciitable(argv[2]);
	
	generate_glyphs();
	
	save_glyphs();

	exit(0);
}
