#include "emscripten.h"
#include "mupdf/fitz.h"

static fz_context *ctx;

EMSCRIPTEN_KEEPALIVE
void initContext(void)
{
	ctx = fz_new_context(NULL, NULL, 100<<20);
	fz_register_document_handlers(ctx);
}

EMSCRIPTEN_KEEPALIVE
fz_document *openDocument(const char *filename)
{
	return fz_open_document(ctx, filename);
}

EMSCRIPTEN_KEEPALIVE
void freeDocument(fz_document *doc)
{
	fz_drop_document(ctx, doc);
}

EMSCRIPTEN_KEEPALIVE
int countPages(fz_document *doc)
{
	return fz_count_pages(ctx, doc);
}

static fz_page *lastPage = NULL;

static void loadPage(fz_document *doc, int number)
{
	static fz_document *lastPageDoc = NULL;
	static int lastPageNumber = -1;
	if (lastPageNumber != number || lastPageDoc != doc)
	{
		if (lastPage)
			fz_drop_page(ctx, lastPage);
		lastPage = fz_load_page(ctx, doc, number-1);
		lastPageDoc = doc;
		lastPageNumber = number;
	}
}

EMSCRIPTEN_KEEPALIVE
char *drawPageAsHTML(fz_document *doc, int number)
{
	static unsigned char *data = NULL;
	fz_stext_page *text;
	fz_buffer *buf;
	fz_output *out;

	fz_free(ctx, data);
	data = NULL;

	loadPage(doc, number);

	buf = fz_new_buffer(ctx, 0);
	{
		out = fz_new_output_with_buffer(ctx, buf);
		{
			text = fz_new_stext_page_from_page(ctx, lastPage, NULL);
			fz_print_stext_page_as_html(ctx, out, text, number);
			fz_drop_stext_page(ctx, text);
		}
		fz_write_byte(ctx, out, 0);
		fz_close_output(ctx, out);
		fz_drop_output(ctx, out);
	}
	fz_buffer_extract(ctx, buf, &data);
	fz_drop_buffer(ctx, buf);

	return (char*)data;
}

EMSCRIPTEN_KEEPALIVE
char *drawPageAsSVG(fz_document *doc, int number)
{
	static int id = 0;
	static unsigned char *data = NULL;
	fz_buffer *buf;
	fz_output *out;
	fz_device *dev;
	fz_rect bbox;

	fz_free(ctx, data);
	data = NULL;

	loadPage(doc, number);

	buf = fz_new_buffer(ctx, 0);
	{
		out = fz_new_output_with_buffer(ctx, buf);
		{
			bbox = fz_bound_page(ctx, lastPage);
			dev = fz_new_svg_device(ctx, out, bbox.x1-bbox.x0, bbox.y1-bbox.y0, FZ_SVG_TEXT_AS_PATH, 0, &id);
			fz_run_page(ctx, lastPage, dev, fz_identity, NULL);
			fz_close_device(ctx, dev);
			fz_drop_device(ctx, dev);
		}
		fz_write_byte(ctx, out, 0);
		fz_close_output(ctx, out);
		fz_drop_output(ctx, out);
	}
	fz_buffer_extract(ctx, buf, &data);
	fz_drop_buffer(ctx, buf);

	return (char*)data;
}

EMSCRIPTEN_KEEPALIVE
char *drawPageAsPNG(fz_document *doc, int number, float dpi)
{
	static unsigned char *data = NULL;
	float zoom = dpi / 72;
	fz_pixmap *pix;
	fz_buffer *buf;
	fz_output *out;

	fz_free(ctx, data);
	data = NULL;

	loadPage(doc, number);

	buf = fz_new_buffer(ctx, 0);
	{
		out = fz_new_output_with_buffer(ctx, buf);
		{
			pix = fz_new_pixmap_from_page(ctx, lastPage, fz_scale(zoom, zoom), fz_device_rgb(ctx), 0);
			fz_write_pixmap_as_data_uri(ctx, out, pix);
			fz_drop_pixmap(ctx, pix);
		}
		fz_write_byte(ctx, out, 0);
		fz_close_output(ctx, out);
		fz_drop_output(ctx, out);
	}
	fz_buffer_extract(ctx, buf, &data);
	fz_drop_buffer(ctx, buf);

	return (char*)data;
}

static fz_irect pageBounds(fz_document *doc, int number, float dpi)
{
	loadPage(doc, number);
	return fz_round_rect(fz_transform_rect(fz_bound_page(ctx, lastPage), fz_scale(dpi/72, dpi/72)));
}

EMSCRIPTEN_KEEPALIVE
int pageWidth(fz_document *doc, int number, float dpi)
{
	fz_irect bbox = pageBounds(doc, number, dpi);
	return bbox.x1 - bbox.x0;
}

EMSCRIPTEN_KEEPALIVE
int pageHeight(fz_document *doc, int number, float dpi)
{
	fz_irect bbox = pageBounds(doc, number, dpi);
	return bbox.y1 - bbox.y0;
}

EMSCRIPTEN_KEEPALIVE
char *pageLinks(fz_document *doc, int number, float dpi)
{
	static unsigned char *data = NULL;
	fz_buffer *buf;
	fz_link *links, *link;

	fz_free(ctx, data);
	data = NULL;

	loadPage(doc, number);

	buf = fz_new_buffer(ctx, 0);
	{
		links = fz_load_links(ctx, lastPage);
		{
			for (link = links; link; link = link->next)
			{
				fz_irect bbox = fz_round_rect(fz_transform_rect(link->rect, fz_scale(dpi/72, dpi/72)));
				fz_append_printf(ctx, buf, "<area shape=\"rect\" coords=\"%d,%d,%d,%d\"",
					bbox.x0, bbox.y0, bbox.x1, bbox.y1);
				if (fz_is_external_link(ctx, link->uri))
					fz_append_printf(ctx, buf, " href=\"%s\">\n", link->uri);
				else
				{
					int linkNumber = fz_resolve_link(ctx, doc, link->uri, NULL, NULL);
					fz_append_printf(ctx, buf, " href=\"#page%d\">\n", linkNumber+1);
				}
			}
		}
		fz_append_byte(ctx, buf, 0);
		fz_drop_link(ctx, links);
	}
	fz_buffer_extract(ctx, buf, &data);
	fz_drop_buffer(ctx, buf);

	return (char*)data;
}

EMSCRIPTEN_KEEPALIVE
char *documentTitle(fz_document *doc)
{
	static char buf[100];
	if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_TITLE, buf, sizeof buf) > 0)
		return buf;
	return "Untitled";
}

EMSCRIPTEN_KEEPALIVE
fz_outline *loadOutline(fz_document *doc)
{
	return fz_load_outline(ctx, doc);
}

EMSCRIPTEN_KEEPALIVE
void freeOutline(fz_outline *outline)
{
	fz_drop_outline(ctx, outline);
}

EMSCRIPTEN_KEEPALIVE
char *outlineTitle(fz_outline *node)
{
	return node->title;
}

EMSCRIPTEN_KEEPALIVE
int outlinePage(fz_outline *node)
{
	return node->page + 1;
}

EMSCRIPTEN_KEEPALIVE
fz_outline *outlineDown(fz_outline *node)
{
	return node->down;
}

EMSCRIPTEN_KEEPALIVE
fz_outline *outlineNext(fz_outline *node)
{
	return node->next;
}
