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

pdf_xobject *
pdf_keep_xobject(fz_context *ctx, pdf_xobject *xobj)
{
	return fz_keep_storable(ctx, &xobj->storable);
}

void
pdf_drop_xobject(fz_context *ctx, pdf_xobject *xobj)
{
	fz_drop_storable(ctx, &xobj->storable);
}

static void
pdf_drop_xobject_imp(fz_context *ctx, fz_storable *xobj_)
{
	pdf_xobject *xobj = (pdf_xobject *)xobj_;
	pdf_drop_obj(ctx, xobj->obj);
	fz_free(ctx, xobj);
}

static size_t
pdf_xobject_size(pdf_xobject *xobj)
{
	if (xobj == NULL)
		return 0;
	return sizeof(*xobj);
}

pdf_obj *
pdf_xobject_resources(fz_context *ctx, pdf_xobject *xobj)
{
	return pdf_dict_get(ctx, xobj->obj, PDF_NAME_Resources);
}

fz_rect *
pdf_xobject_bbox(fz_context *ctx, pdf_xobject *xobj, fz_rect *bbox)
{
	return pdf_to_rect(ctx, pdf_dict_get(ctx, xobj->obj, PDF_NAME_BBox), bbox);
}

fz_matrix *
pdf_xobject_matrix(fz_context *ctx, pdf_xobject *xobj, fz_matrix *matrix)
{
	return pdf_to_matrix(ctx, pdf_dict_get(ctx, xobj->obj, PDF_NAME_Matrix), matrix);
}

int pdf_xobject_isolated(fz_context *ctx, pdf_xobject *xobj)
{
	pdf_obj *group = pdf_dict_get(ctx, xobj->obj, PDF_NAME_Group);
	if (group)
		return pdf_to_bool(ctx, pdf_dict_get(ctx, group, PDF_NAME_I));
	return 0;
}

int pdf_xobject_knockout(fz_context *ctx, pdf_xobject *xobj)
{
	pdf_obj *group = pdf_dict_get(ctx, xobj->obj, PDF_NAME_Group);
	if (group)
		return pdf_to_bool(ctx, pdf_dict_get(ctx, group, PDF_NAME_K));
	return 0;
}

int pdf_xobject_transparency(fz_context *ctx, pdf_xobject *xobj)
{
	pdf_obj *group = pdf_dict_get(ctx, xobj->obj, PDF_NAME_Group);
	if (group)
		if (pdf_name_eq(ctx, pdf_dict_get(ctx, group, PDF_NAME_S), PDF_NAME_Transparency))
			return 1;
	return 0;
}

fz_colorspace *
pdf_xobject_colorspace(fz_context *ctx, pdf_xobject *xobj)
{
	pdf_obj *group = pdf_dict_get(ctx, xobj->obj, PDF_NAME_Group);
	if (group)
	{
		pdf_obj *cs = pdf_dict_get(ctx, group, PDF_NAME_CS);
		if (cs)
		{
			fz_colorspace *colorspace = NULL;
			fz_try(ctx)
				colorspace = pdf_load_colorspace(ctx, cs);
			fz_catch(ctx)
				fz_warn(ctx, "cannot load xobject colorspace");
			return colorspace;
		}
	}
	return NULL;
}

pdf_xobject *
pdf_load_xobject(fz_context *ctx, pdf_document *doc, pdf_obj *dict)
{
	pdf_xobject *form;

	if ((form = pdf_find_item(ctx, pdf_drop_xobject_imp, dict)) != NULL)
		return form;

	form = fz_malloc_struct(ctx, pdf_xobject);
	FZ_INIT_STORABLE(form, 1, pdf_drop_xobject_imp);
	form->obj = NULL;
	form->iteration = 0;

	/* Store item immediately, to avoid possible recursion if objects refer back to this one */
	pdf_store_item(ctx, dict, form, pdf_xobject_size(form));

	form->obj = pdf_keep_obj(ctx, dict);

	return form;
}

pdf_obj *
pdf_new_xobject(fz_context *ctx, pdf_document *doc, const fz_rect *bbox, const fz_matrix *mat)
{
	int idict_num;
	pdf_obj *idict = NULL;
	pdf_obj *dict = NULL;
	pdf_xobject *form = NULL;
	pdf_obj *res = NULL;
	pdf_obj *procset;

	fz_var(idict);
	fz_var(dict);
	fz_var(form);
	fz_var(res);

	fz_try(ctx)
	{
		dict = pdf_new_dict(ctx, doc, 0);
		pdf_dict_put_drop(ctx, dict, PDF_NAME_BBox, pdf_new_rect(ctx, doc, bbox));
		pdf_dict_put_drop(ctx, dict, PDF_NAME_FormType, pdf_new_int(ctx, doc, 1));
		pdf_dict_put_drop(ctx, dict, PDF_NAME_Length, pdf_new_int(ctx, doc, 0));
		pdf_dict_put_drop(ctx, dict, PDF_NAME_Matrix, pdf_new_matrix(ctx, doc, mat));

		res = pdf_new_dict(ctx, doc, 0);
		pdf_dict_put(ctx, dict, PDF_NAME_Resources, res);

		procset = pdf_new_array(ctx, doc, 2);
		pdf_dict_put_drop(ctx, res, PDF_NAME_ProcSet, procset);
		pdf_array_push(ctx, procset, PDF_NAME_PDF);
		pdf_array_push(ctx, procset, PDF_NAME_Text);

		pdf_dict_put(ctx, dict, PDF_NAME_Subtype, PDF_NAME_Form);
		pdf_dict_put(ctx, dict, PDF_NAME_Type, PDF_NAME_XObject);

		form = fz_malloc_struct(ctx, pdf_xobject);
		FZ_INIT_STORABLE(form, 1, pdf_drop_xobject_imp);
		form->obj = NULL;
		form->iteration = 0;

		idict_num = pdf_create_object(ctx, doc);
		pdf_update_object(ctx, doc, idict_num, dict);
		idict = pdf_new_indirect(ctx, doc, idict_num, 0);

		pdf_store_item(ctx, idict, form, pdf_xobject_size(form));

		form->obj = pdf_keep_obj(ctx, idict);
	}
	fz_always(ctx)
	{
		pdf_drop_obj(ctx, dict);
		pdf_drop_obj(ctx, res);
		pdf_drop_xobject(ctx, form);
	}
	fz_catch(ctx)
	{
		pdf_drop_obj(ctx, idict);
		fz_rethrow(ctx);
	}

	return idict;
}

void pdf_update_xobject_contents(fz_context *ctx, pdf_document *doc, pdf_xobject *form, fz_buffer *buffer)
{
	pdf_update_stream(ctx, doc, form->obj, buffer, 0);
	form->iteration ++;
}
