#include <u.h>

#define Point OSXPoint
#define Rect OSXRect
#define Cursor OSXCursor
#include <Carbon/Carbon.h>
#undef Rect
#undef Point
#undef Cursor
#undef offsetof
#undef nil

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


extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t);

char*
mac2c(CFStringRef s)
{
	char *p;
	int n;

	n = CFStringGetLength(s)*8;	
	p = malloc(n);
	CFStringGetCString(s, p, n, kCFStringEncodingUTF8);
	return p;
}

CFStringRef
c2mac(char *p)
{
	return CFStringCreateWithBytes(nil, (uchar*)p, strlen(p), kCFStringEncodingUTF8, false);
}

Rectangle
mac2r(CGRect r, int size, int unit)
{
	Rectangle rr;

	rr.min.x = r.origin.x*size/unit;
	rr.min.y = r.origin.y*size/unit;
	rr.max.x = (r.origin.x+r.size.width)*size/unit + 0.99999999;
	rr.max.y = (r.origin.x+r.size.width)*size/unit + 0.99999999;
	return rr;
}

void
loadfonts(void)
{
	int i, n;
	CTFontCollectionRef allc;
	CFArrayRef array;
	CFStringRef s;
	CTFontDescriptorRef f;

	allc = CTFontCollectionCreateFromAvailableFonts(0);
	array = CTFontCollectionCreateMatchingFontDescriptors(allc);
	n = CFArrayGetCount(array);
	xfont = emalloc9p(n*sizeof xfont[0]);
	for(i=0; i<n; i++) {
		f = (void*)CFArrayGetValueAtIndex(array, i);
		if(f == nil)
			continue;
		s = CTFontDescriptorCopyAttribute(f, kCTFontNameAttribute);
		xfont[nxfont].name = mac2c(s);		
		CFRelease(s);
		nxfont++;
	}
}

CGRect
subfontbbox(CGFontRef font, int lo, int hi)
{
	int i, first;
	CGRect bbox;

	bbox.origin.x = 0;
	bbox.origin.y = 0;
	bbox.size.height = 0;
	bbox.size.width = 0;

	first = 1;
	for(i=lo; i<=hi; i++) {
		UniChar u;
		CGGlyph g;
		CGRect r;

		u = i;
		CGFontGetGlyphsForUnichars(font, &u, &g, 1);
		if(g == 0 || !CGFontGetGlyphBBoxes(font, &g, 1, &r))
			continue;

		r.size.width += r.origin.x;
		r.size.height += r.origin.y;
		if(first) {
			bbox = r;
			first = 0;
			continue;
		}
		if(bbox.origin.x > r.origin.x)
			bbox.origin.x = r.origin.x;	
		if(bbox.origin.y > r.origin.y)
			bbox.origin.y = r.origin.y;	
		if(bbox.size.width < r.size.width)
			bbox.size.width = r.size.width;
		if(bbox.size.height < r.size.height)
			bbox.size.height = r.size.height;
	}
	
	bbox.size.width -= bbox.origin.x;
	bbox.size.height -= bbox.origin.y;
	return bbox;
}

void
load(XFont *f)
{
	int i, j;
	CGFontRef font;
	CFStringRef s;
	UniChar u[256];
	CGGlyph g[256];
	CGRect bbox;

	if(f->loaded)
		return;
	f->loaded = 1;
	s = c2mac(f->name);
	font = CGFontCreateWithFontName(s);
	CFRelease(s);
	if(font == nil)
		return;
	
	// assume bbox gives latin1 is height/ascent for all
	bbox = subfontbbox(font, 0x00, 0xff);
	f->unit = CGFontGetUnitsPerEm(font);
	f->height = bbox.size.height;
	f->originy = bbox.origin.y;

	// figure out where the letters are
	for(i=0; i<0xffff; i+=0x100) {
		for(j=0; j<0x100; j++) {
			u[j] = i+j;
			g[j] = 0;
		}
		CGFontGetGlyphsForUnichars(font, u, g, 256);
		for(j=0; j<0x100; j++) {
			if(g[j] != 0) {
				f->range[i>>8] = 1;
				f->nrange++;
				break;
			}
		}
	}
	CFRelease(font);
}

Memsubfont*
mksubfont(char *name, int lo, int hi, int size, int antialias)
{
	CFStringRef s;
	CGColorSpaceRef color;
	CGContextRef ctxt;
	CGFontRef font;
	CGRect bbox;
	Memimage *m, *mc, *m1;
	int x, y, y0;
	int i, unit;
	Fontchar *fc, *fc0;
	Memsubfont *sf;

	s = c2mac(name);
	font = CGFontCreateWithFontName(s);
	CFRelease(s);
	if(font == nil)
		return nil;
	bbox = subfontbbox(font, lo, hi);
	unit = CGFontGetUnitsPerEm(font);
	x = (int)(bbox.size.width * size / unit + 0.99999999);
	y = bbox.size.height * size/unit + 0.99999999;
	y0 = (int)(-bbox.origin.y * size/unit + 0.99999999);
	m = allocmemimage(Rect(0, 0, x*(hi+1-lo), y), GREY8);
	if(m == nil)
		return nil;
	mc = allocmemimage(Rect(0, 0, x, y), GREY8);
	if(mc == nil)
		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);
		return nil;
	}
	fc0 = fc;

	color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
	ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8,
		mc->width*sizeof(u32int), color, kCGImageAlphaNone);
	CGColorSpaceRelease(color);
	if(ctxt == nil) {
		freememimage(m);
		freememimage(mc);
		free(fc);
		free(sf);
		return nil;
	}

	CGContextSetFont(ctxt, font);
	CGContextSetFontSize(ctxt, size);
	CGContextSetAllowsAntialiasing(ctxt, antialias);
	CGContextSetRGBFillColor(ctxt, 1, 1, 1, 1);
	CGContextSetTextPosition(ctxt, 0, 0);	// XXX

	x = 0;
	for(i=lo; i<=hi; i++, fc++) {
		UniChar u[2];
		CGGlyph g[2];
		CGRect r[2];
		CGPoint p1;
		int n;

		fc->x = x;
		fc->top = 0;
		fc->bottom = Dy(m->r);

		n = 0;
		u[n++] = i;
		if(0)	// debugging
			u[n++] = '|';
		g[0] = 0;
		CGFontGetGlyphsForUnichars(font, u, g, n);
		if(g[0] == 0 || !CGFontGetGlyphBBoxes(font, g, n, r)) {
		None:
			fc->width = 0;
			if(i == 0) {
				Point p;
				Fontchar *i;
				p = Pt(x, y0);
				// memimagestring(m, p, memwhite, ZP, defont, peterface);
				i = defont->info + 0;
				memdraw(m, Rect(p.x+i->left, p.y+i->top, p.x+i->left+(i[1].x-i[0].x), p.y+i->bottom),
					memwhite, ZP, defont->bits, Pt(i->x, i->top), S);
				p.x += i->width;
				fc->left = i->left;
				fc->width = i->width;
				x = p.x;
			}	
			continue;
		}
		memfillcolor(mc, DBlack);
		CGContextSetTextPosition(ctxt, 0, y0);
		CGContextShowGlyphs(ctxt, g, n);
		p1 = CGContextGetTextPosition(ctxt);
		if(p1.x <= 0)
			goto None;
		memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S);
		fc->width = p1.x;
		fc->left = 0;
		x += p1.x;
	}
	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;
	
	return sf;
}