#include <fitz.h>
#include <mupdf.h>

#ifdef WIN32
#error Compile "fontfilems.c" instead
#endif

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

static FT_Library ftlib = nil;
static FcConfig *fclib = nil;

enum
{
	FD_FIXED = 1 << 0,
	FD_SERIF = 1 << 1,
	FD_SYMBOLIC = 1 << 2,
	FD_SCRIPT = 1 << 3,
	FD_NONSYMBOLIC = 1 << 5,
	FD_ITALIC = 1 << 6,
	FD_ALLCAP = 1 << 16,
	FD_SMALLCAP = 1 << 17,
	FD_FORCEBOLD = 1 << 18
};

static char *basenames[14] =
{
	"Courier", 
	"Courier-Bold", 
	"Courier-Oblique",
	"Courier-BoldOblique",
	"Helvetica",
	"Helvetica-Bold",
	"Helvetica-Oblique",
	"Helvetica-BoldOblique",
	"Times-Roman",
	"Times-Bold",
	"Times-Italic",
	"Times-BoldItalic",
	"Symbol", 
	"ZapfDingbats"
};

static char *basepatterns[14] =
{
	"Nimbus Mono L,Courier,Courier New:style=Regular,Roman",
	"Nimbus Mono L,Courier,Courier New:style=Bold",
	"Nimbus Mono L,Courier,Courier New:style=Oblique,Italic",
	"Nimbus Mono L,Courier,Courier New:style=BoldOblique,BoldItalic",
	"Nimbus Sans L,Helvetica,Arial:style=Regular,Roman",
	"Nimbus Sans L,Helvetica,Arial:style=Bold",
	"Nimbus Sans L,Helvetica,Arial:style=Oblique,Italic",
	"Nimbus Sans L,Helvetica,Arial:style=BoldOblique,BoldItalic",
	"Nimbus Roman No9 L,Times,Times New Roman:style=Regular,Roman",
	"Nimbus Roman No9 L,Times,Times New Roman:style=Bold,Medium",
	"Nimbus Roman No9 L,Times,Times New Roman:style=Italic,Regular Italic",
	"Nimbus Roman No9 L,Times,Times New Roman:style=BoldItalic,Medium Italic",
	"Standard Symbols L,Symbol",
	"Zapf Dingbats,Dingbats"
};

static fz_error *initfontlibs(void)
{
	int fterr;
	int maj, min, pat;

	if (ftlib)
		return nil;

	fterr = FT_Init_FreeType(&ftlib);
	if (fterr)
		return fz_throw("freetype failed initialisation: 0x%x", fterr);

	FT_Library_Version(ftlib, &maj, &min, &pat);
	if (maj == 2 && min == 1 && pat < 7)
		return fz_throw("freetype version too old: %d.%d.%d", maj, min, pat);

	fclib = FcInitLoadConfigAndFonts();
	if (!fclib)
		return fz_throw("fontconfig failed initialisation");

	return nil;
}

fz_error *
pdf_loadbuiltinfont(pdf_font *font, char *basefont)
{
	fz_error *error;
	FcResult fcerr;
	int fterr;

	FcPattern *searchpat;
	FcPattern *matchpat;
	FT_Face face;
	char *pattern;
	char *file;
	int index;
	int i;

	error = initfontlibs();
	if (error)
		return error;

	pattern = basefont;
	for (i = 0; i < 14; i++)
		if (!strcmp(basefont, basenames[i]))
			pattern = basepatterns[i];

	pdf_logfont("load builtin %s\n", pattern);

	fcerr = FcResultMatch;
	searchpat = FcNameParse(pattern);
	FcDefaultSubstitute(searchpat);
	FcConfigSubstitute(fclib, searchpat, FcMatchPattern);

	matchpat = FcFontMatch(fclib, searchpat, &fcerr);
	if (fcerr != FcResultMatch)
		return fz_throw("fontconfig could not find font %s", pattern);

	fcerr = FcPatternGetString(matchpat, FC_FILE, 0, (FcChar8**)&file);
	if (fcerr != FcResultMatch)
		return fz_throw("fontconfig could not find font %s", pattern);

	index = 0;
	fcerr = FcPatternGetInteger(matchpat, FC_INDEX, 0, &index);

	pdf_logfont("load font file %s %d\n", file, index);

	fterr = FT_New_Face(ftlib, file, index, &face);
	if (fterr)
		return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr);

	FcPatternDestroy(matchpat);
	FcPatternDestroy(searchpat);

	font->ftface = face;

	return nil;
}

fz_error *
pdf_loadsystemfont(pdf_font *font, char *basefont, char *collection)
{
	fz_error *error;
	FcResult fcerr;
	int fterr;

	char fontname[200];
	FcPattern *searchpat;
	FcPattern *matchpat;
	FT_Face face;
	char *style;
	char *file;
	int index;

	error = initfontlibs();
	if (error)
		return error;

	/* parse windows-style font name descriptors Font,Style */
	/* TODO: reliable way to split style from Font-Style type names */
	strlcpy(fontname, basefont, sizeof fontname);
	style = strchr(fontname, ',');
	if (style)
		*style++ = 0;

	searchpat = FcPatternCreate();
	if (!searchpat)
		return fz_outofmem;

	error = fz_outofmem;

	if (!FcPatternAddString(searchpat, FC_FAMILY, fontname))
		goto cleanup;

	if (collection)
	{
		if (!strcmp(collection, "Adobe-GB1"))
			if (!FcPatternAddString(searchpat, FC_LANG, "zh-TW"))
				goto cleanup;
		if (!strcmp(collection, "Adobe-CNS1"))
			if (!FcPatternAddString(searchpat, FC_LANG, "zh-CN"))
				goto cleanup;
		if (!strcmp(collection, "Adobe-Japan1"))
			if (!FcPatternAddString(searchpat, FC_LANG, "ja"))
				goto cleanup;
		if (!strcmp(collection, "Adobe-Japan2"))
			if (!FcPatternAddString(searchpat, FC_LANG, "ja"))
				goto cleanup;
		if (!strcmp(collection, "Adobe-Korea1"))
			if (!FcPatternAddString(searchpat, FC_LANG, "ko"))
				goto cleanup;
	}

	if (style)
		if (!FcPatternAddString(searchpat, FC_STYLE, style))
			goto cleanup;

	if (font->flags & FD_SERIF)
		FcPatternAddString(searchpat, FC_FAMILY, "serif");
	else
		FcPatternAddString(searchpat, FC_FAMILY, "sans-serif");
	if (font->flags & FD_ITALIC)
		FcPatternAddString(searchpat, FC_STYLE, "Italic");
	if (font->flags & FD_FORCEBOLD)
		FcPatternAddString(searchpat, FC_STYLE, "Bold");

	if (!FcPatternAddBool(searchpat, FC_OUTLINE, 1))
		goto cleanup;

	file = FcNameUnparse(searchpat);
	pdf_logfont("fontconfig0 %s\n", file);
	free(file);

	fcerr = FcResultMatch;
/*	FcDefaultSubstitute(searchpat); */
	FcConfigSubstitute(fclib, searchpat, FcMatchPattern);

	file = FcNameUnparse(searchpat);
	pdf_logfont("fontconfig1 %s\n", file);
	free(file);

	matchpat = FcFontMatch(fclib, searchpat, &fcerr);
	if (fcerr != FcResultMatch)
		return fz_throw("fontconfig could not find font %s", basefont);

	fcerr = FcPatternGetString(matchpat, FC_FAMILY, 0, (FcChar8**)&file);
	if (file && strcmp(fontname, file))
		font->substitute = 1;

	fcerr = FcPatternGetString(matchpat, FC_STYLE, 0, (FcChar8**)&file);
	if (file && style && strcmp(style, file))
		font->substitute = 1;

	fcerr = FcPatternGetString(matchpat, FC_FILE, 0, (FcChar8**)&file);
	if (fcerr != FcResultMatch)
		return fz_throw("fontconfig could not find font %s", basefont);

	index = 0;
	fcerr = FcPatternGetInteger(matchpat, FC_INDEX, 0, &index);

	pdf_logfont("load font file %s %d\n", file, index);

	fterr = FT_New_Face(ftlib, file, index, &face);
	if (fterr) {
		FcPatternDestroy(matchpat);
		FcPatternDestroy(searchpat);
		return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr);
	}

	FcPatternDestroy(matchpat);
	FcPatternDestroy(searchpat);

	font->ftface = face;

	return nil;

cleanup:
	FcPatternDestroy(searchpat);
	return error;
}

fz_error *
pdf_loadembeddedfont(pdf_font *font, pdf_xref *xref, fz_obj *stmref)
{
	fz_error *error;
	int fterr;
	FT_Face face;
	fz_buffer *buf;

	error = initfontlibs();
	if (error)
		return error;

	pdf_logfont("load embedded font\n");

	error = pdf_loadstream(&buf, xref, fz_tonum(stmref), fz_togen(stmref));
	if (error)
		return error;

	fterr = FT_New_Memory_Face(ftlib, buf->rp, buf->wp - buf->rp, 0, &face);

	if (fterr) {
		fz_free(buf);
		return fz_throw("freetype could not load embedded font: 0x%x", fterr);
	}

	font->ftface = face;
	font->fontdata = buf;

	return nil;
}

