#include <u.h>

#include <fontconfig/fontconfig.h>
#include <ft2build.h>
#include FT_FREETYPE_H

#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include "a.h"

static FcConfig    *fc;
static FT_Library  lib;
static int         dpi = 96;

void
loadfonts(void)
{
	int i;
	FT_Error e;
	FcFontSet *sysfonts;

	if(!FcInit() || (fc=FcInitLoadConfigAndFonts()) == NULL) {
		fprint(2, "fontconfig initialization failed\n");
		exits("fontconfig failed");
	}

	e = FT_Init_FreeType(&lib);
	if(e) {
		fprint(2, "freetype initialization failed: %d\n", e);
		exits("freetype failed");
	}

	sysfonts = FcConfigGetFonts(fc, FcSetSystem);

	xfont = emalloc9p(sysfonts->nfont*sizeof xfont[0]);
	memset(xfont, 0, sysfonts->nfont*sizeof xfont[0]);
	for(i=0; i<sysfonts->nfont; i++) {
		FcChar8 *fullname, *fontfile;
		int index;
		FcPattern *pat = sysfonts->fonts[i];

		if(FcPatternGetString(pat, FC_FULLNAME, 0, &fullname) != FcResultMatch ||
		   FcPatternGetString(pat, FC_FILE, 0, &fontfile) != FcResultMatch     ||
		   FcPatternGetInteger(pat, FC_INDEX, 0, &index) != FcResultMatch)
			continue;

		xfont[nxfont].name     = strdup((char*)fullname);
		xfont[nxfont].fontfile = strdup((char*)fontfile);
		xfont[nxfont].index    = index;
		nxfont++;
	}

	FcFontSetDestroy(sysfonts);
}

void
load(XFont *f)
{
	FT_Face face;
	FT_Error e;
	FT_ULong charcode;
	FT_UInt glyph_index;

	if(f->loaded)
		return;

	e = FT_New_Face(lib, f->fontfile, f->index, &face);

	if(e){
		fprint(2, "load failed for %s (%s) index:%d\n", f->name, f->fontfile, f->index);
		return;
	}

	if(!FT_IS_SCALABLE(face)) {
		fprint(2, "%s is a non scalable font, skipping\n", f->name);
		FT_Done_Face(face);
	    f->loaded = 1;
		return;
	}

	f->unit = face->units_per_EM;
	f->height = (int)((face->ascender - face->descender) * 1.2);
	f->originy = face->descender; // bbox.yMin (or descender)  is negative, becase the baseline is y-coord 0

	for(charcode=FT_Get_First_Char(face, &glyph_index); glyph_index != 0;
		charcode=FT_Get_Next_Char(face, charcode, &glyph_index)) {

		int idx = charcode>>8;

		if(charcode > 0xffff)
			break;

		if(!f->range[idx]) {
			f->range[idx] = 1;
			f->nrange++;
		}
	}

	FT_Done_Face(face);
	f->loaded = 1;
}

Memsubfont*
mksubfont(char *name, int lo, int hi, int size, int antialias)
{
	XFont *xf, *xfp, *xfe;
	FT_Face face;
	FT_Error e;
	Memimage *m, *mc, *m1;
	double pixel_size;
	int x, y, y0;
	int i;
	Fontchar *fc, *fc0;
	Memsubfont *sf;
	Point rect_points[4];

	xf = nil;
	for(xfp=xfont, xfe=xfont+nxfont; xfp != xfe; xfp++) {
		if(strcmp(xfp->name, name) == 0) {
			xf = xfp;
			break;
		}
	}

	if(!xf)
		return nil;

	e = FT_New_Face(lib, xf->fontfile, xf->index, &face);

	if(e){
		fprint(2, "load failed for %s (%s) index:%d\n", xf->name, xf->fontfile, xf->index);
		return nil;
	}

	e = FT_Set_Char_Size(face, 0, size<<6, dpi, dpi);
	if(e){
		fprint(2, "FT_Set_Char_Size failed\n");
		FT_Done_Face(face);
		return nil;
	}

	pixel_size = (dpi*size)/72.0;
	x = (int)((face->max_advance_width) * pixel_size/xf->unit + 0.99999999);
	y = (int)((face->ascender - face->descender) * pixel_size/xf->unit + 0.99999999);
	y0 = (int)(-face->descender * pixel_size/xf->unit + 0.99999999);

	m = allocmemimage(Rect(0, 0, x*(hi+1-lo), y), antialias ? GREY8 : GREY1);
	if(m == nil) {
		FT_Done_Face(face);
		return nil;
	}
	mc = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1);
	if(mc == nil) {
		freememimage(m);
		FT_Done_Face(face);
		return nil;
	}
	memfillcolor(m, DBlack);
	memfillcolor(mc, DBlack);
	fc = malloc((hi+2 - lo) * sizeof fc[0]);
	sf = malloc(sizeof *sf);
	if(fc == nil || sf == nil) {
		freememimage(m);
		freememimage(mc);
		free(fc);
		free(sf);
		FT_Done_Face(face);
		return nil;
	}
	fc0 = fc;

	//rect_points[0] = mc->r.min;
	//rect_points[1] = Pt(mc->r.max.x, mc->r.min.y);
	//rect_points[2] = mc->r.max;
	//rect_points[3] = Pt(mc->r.min.x, mc->r.max.y);

	x = 0;
	for(i=lo; i<=hi; i++, fc++) {
		int r;
		int advance;

		memfillcolor(mc, DBlack);

		e = FT_Load_Char(face, i, FT_LOAD_RENDER|(antialias ? 0:FT_LOAD_TARGET_MONO));
		if(e){
			fprint(2, "FT_Load_Char failed for %d\n", i);
			//mempoly(mc, rect_points, 4, Endsquare, Endsquare, 0, memopaque, ZP, S);
			memimageline(mc, m->r.min, Pt(m->r.max.x, m->r.min.y), Endsquare, Endsquare, 0, memopaque, ZP, S);
			memimageline(mc, m->r.min, Pt(m->r.min.x, m->r.max.y), Endsquare, Endsquare, 0, memopaque, ZP, S);
			memimageline(mc, Pt(m->r.max.x, m->r.min.y), m->r.max, Endsquare, Endsquare, 0, memopaque, ZP, S);
			memimageline(mc, Pt(m->r.min.x, m->r.max.y), m->r.max, Endsquare, Endsquare, 0, memopaque, ZP, S);
			memimageline(mc, m->r.min, m->r.max, Endsquare, Endsquare, 0, memopaque, ZP, S);
			advance = Dx(m->r);

			memimagedraw(m, Rect(x, 0, x + advance, y), mc, ZP, memopaque, ZP, S);
		} else {
			FT_Bitmap *bitmap = &face->glyph->bitmap;
			uchar *base = byteaddr(mc, mc->r.min);
			advance = (face->glyph->advance.x+32) >> 6;

			for(r=0; r < bitmap->rows; r++)
				memmove(base + r*mc->width*sizeof(u32int), bitmap->buffer + r*bitmap->pitch, bitmap->pitch);

			memimagedraw(m, Rect(x, 0, x + advance, y), mc,
				Pt(-face->glyph->bitmap_left, -(y - y0 - face->glyph->bitmap_top)),
				memopaque, ZP, S);
		}

		fc->x = x;
		fc->top = 0;
		fc->bottom = y;
		fc->left = 0;
		fc->width = advance;
		x += advance;

#ifdef DEBUG_FT_BITMAP
		for(r=0; r < bitmap->rows; r++) {
			int c;
			uchar *span = bitmap->buffer+(r*bitmap->pitch);
			for(c = 0; c < bitmap->width; c++) {
				fprint(1, "%02x", span[c]);
			}
			fprint(1,"\n");
		}
#endif

#ifdef DEBUG_9_BITMAP
		for(r=0; r < mc->r.max.y; r++) {
			int c;
			uchar *span = base+(r*mc->width*sizeof(u32int));
			for(c = 0; c < Dx(mc->r); c++) {
				fprint(1, "%02x", span[c]);
			}
			fprint(1,"\n");
		}
#endif
	}
	fc->x = x;

	// round up to 32-bit boundary
	// so that in-memory data is same
	// layout as in-file data.
	if(antialias)
		x += -x & 3;
	else
		x += -x & 31;
	m1 = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1);
	memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S);
	freememimage(m);

	sf->name = nil;
	sf->n = hi+1 - lo;
	sf->height = Dy(m1->r);
	sf->ascent = Dy(m1->r) - y0;
	sf->info = fc0;
	sf->bits = m1;

	FT_Done_Face(face);
	return sf;
}
