// 14 september 2015
#include "area.h"

struct uiDrawFont {
	HFONT font;
};

uiDrawFont *uiDrawPrepareFont(uiDrawFontSpec *spec)
{
	uiDrawFont *df;
	LOGFONTW lf;

	// TODO use uiNew
	df = malloc(sizeof (uiDrawFont));
	ZeroMemory(&lf, sizeof (LOGFONTW));
	// TODO
	df->font = xxxxxxxx(&lf);
	if (df->font == NULL)
		logLastError("error creating font in uiDrawPrepareFont()");
	return df;
}

void uiDrawFreeFont(uiDrawFont *df)
{
	if (DeleteObject(df->font) == 0)
		logLastError("error deleting font in uiDrawFreeFont()");
	// TODO use uiFree
	free(df);
}

/* uniscribe notes:
- https://msdn.microsoft.com/en-us/library/windows/desktop/dd317792%28v=vs.85%29.aspx
- https://maxradi.us/documents/uniscribe/
- http://www.catch22.net/tuts/introduction-uniscribe
- http://www.catch22.net/tuts/uniscribe-mysteries
- http://www.catch22.net/tuts/more-uniscribe-mysteries
- http://www.catch22.net/tuts/drawing-styled-text-uniscribe
- http://www.microsoft.com/msj/1198/multilang/multilang.aspx
Even with all this we're on our own with actual paragraph layout; see the last link for example code (which we don't use here). */

struct uniitem {
	SCRIPT_ITEM item;

	int nGlyphs;
	WORD *glyphs;
	SCRIPT_VISATTR *visattrs;
	int *advances;
	GOFFSET *goffsets;

	WCHAR *pwcChars;
	int nChars;
	WORD *clusters;
	SCRIPT_LOGATTR *logattr;

	ABC abc;
};

struct uniscribe {
	WCHAR *text;
	size_t len;
	HDC dc;

	SCRIPT_CONTROL control;
	SCRIPT_STATE state;

	struct uniitem *items;
	int nItems;

	SCRIPT_CACHE cache;
};

// the step numbers in this function are according to the "Lay Out Text Using Uniscribe" section of https://msdn.microsoft.com/en-us/library/windows/desktop/dd317792%28v=vs.85%29.aspx

// this does step 4
// steps 1 through 3 are irrelevent here
static void initUniscribe(struct uniscribe *s, WCHAR *text, HDC dc)
{
	SCRIPT_ITEM *items;
	int nAlloc;
	int i;
	HRESULT hr;

	ZeroMemory(s, sizeof (struct uniscribe));

	s->text = text;
	s->len = wcslen(s->text);
	s->dc = dc;

	items = NULL;
	nAlloc = 32;
	for (;;) {
		// TODO use uiRealloc
		items = realloc(items, nAlloc * sizeof (SCRIPT_ITEM));
		hr = ScriptItemize(s->text, s->len,
			nAlloc,
			&(s->control), &(s->state),
			items, &(s->nItems));
		if (hr == S_OK)
			break;
		if (hr == E_OUTOFMEMORY) {
			nAlloc *= 2;
			continue;
		}
		logHRESULT("error itemizing string in initUniscribe()", hr);
	}

	// this does part of step 6
	s->items = malloc(s->nItems * sizeof (struct uniitem));
ZeroMemory(s->items, s->nItems * sizeof (struct uniitem));//TODO remove when switching to uiAlloc
	for (i = 0; i < s->nItems; i++) {
		s->items[i].pwcChars = s->text + items[i].iCharPos;
		s->items[i].nChars = items[i + 1].iCharPos - items[i].iCharPos;
		s->items[i].item = items[i];
	}
	free(items);
}

static void resetItem(struct uniitem *item)
{
	if (item->nGlyphs == 0)
		return;
	item->nGlyphs = 0;
	// TODO switch to uiFree
	free(item->glyphs);
	item->glyphs = NULL;
	free(item->visattrs);
	item->visattrs = NULL;
	free(item->advances);
	item->advances = NULL;
	free(item->goffsets);
	item->goffsets = NULL;
	free(item->clusters);
	item->clusters = NULL;
	free(item->logattr);
	item->logattr = NULL;
}

// step 5
// we're not using multiple fonts; we have no lists to merge

// step 6
static void shapeItem(struct uniscribe *s, int n)
{
	struct uniitem *item;
	int nAlloc;
	HRESULT hr;

	item = &(s->items[n]);
	resetItem(item);
	item->clusters = malloc(nChars * sizeof (WORD));

	// TODO move to uiAlloc
	nAlloc = 1.5 * item->nChars + 16;
	for (;;) {
		item->glyphs = realloc(item->glyphs, nAlloc * sizeof (WORD));
		item->visattrs = realloc(item->visattrs, nAlloc * sizeof (SCRIPT_VISATTR));
		hr = ScriptShape(s->dc, &(s->cache),
			item->pwcChars, item->nChars,
			nAlloc,
			&(item->item.a),
			item->glyphs, item->clusters,
			item->visattrs, &(item->nGlyphs));
		if (hr == S_OK)
			break;
		if (hr == E_OUTOFMEMORY) {
			nAlloc *= 2;
			continue;
		}
		logHRESULT("error shaping glyphs in shapeItem()", hr);
	}
}

// step 7
// TODO

// step 8
static void placeItem(struct uniscribe *s, int n)
{
	struct uniitem *item;
	HRESULT hr;

	item = &(s->items[n]);

	item->advances = malloc(item->nGlyphs * sizeof (int));
	item->goffsets = malloc(item->nGlyphs * sizeof (GOFFSET));
	hr = ScriptPlace(s->dc, &(s->cache),
		item->glyphs, item->nGlyphs, item->visattrs,
		&(item->item.a),
		item->advances, item->goffsets, &(item->abc));
	if (hr != S_OK)
		logHRESULT("error placing glyphs in placeItem()", hr);
}

static void breakItem(struct uniscribe *s, int n)
{
	struct uniitem *item;
	HRESULT hr;

	item = &(s->items[n]);

	// TODO uiAlloc
	item->logattr = malloc(s->nChars * sizeof (SCRIPT_LOGATTR));
	hr = ScriptBreak(item->pwcChars, item->nChars,
		&(item->item.a),
		item->logattr);
	if (hr != S_OK)
		logLastError("error breaking item in breakItem()");
}

static void uninitUniscribe(struct uniscribe *s)
{
	HRESULT hr;
	int i;

	// TODO use uiFree

	for (i = 0; i < s->nItems; i++)
		resetItem(&(s->items[i]));
	free(s->items);

	hr = ScriptCacheFree(&(s->cache));
	if (hr != S_OK)
		logHRESULT("error freeing Uniscribe cache in uninitUniscribe()");
}