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

typedef struct pdf_item_s pdf_item;

struct pdf_item_s
{
	pdf_itemkind kind;
	fz_obj *key;
	void *val;
	pdf_item *next;
};

struct refkey
{
	pdf_itemkind kind;
	int oid;
	int gen;
};

struct pdf_store_s
{
	fz_hashtable *hash;		/* hash for oid/gen keys */
	pdf_item *root;		/* linked list for everything else */
};

fz_error *
pdf_newstore(pdf_store **storep)
{
	fz_error *error;
	pdf_store *store;

	store = fz_malloc(sizeof(pdf_store));
	if (!store)
		return fz_outofmem;

	error = fz_newhash(&store->hash, 4096, sizeof(struct refkey));
	if (error)
	{
		fz_free(store);
		return error;
	}

	store->root = nil;

	*storep = store;
	return nil;
}

static void dropitem(pdf_itemkind kind, void *val)
{
	switch (kind)
	{
	case PDF_KCOLORSPACE: fz_dropcolorspace(val); break;
	case PDF_KFUNCTION: pdf_dropfunction(val); break;
	case PDF_KXOBJECT: pdf_dropxobject(val); break;
	case PDF_KIMAGE: fz_dropimage(val); break;
	case PDF_KPATTERN: pdf_droppattern(val); break;
	case PDF_KSHADE: fz_dropshade(val); break;
	case PDF_KCMAP: pdf_dropcmap(val); break;
	case PDF_KFONT: fz_dropfont(val); break;
	}
}

void
pdf_emptystore(pdf_store *store)
{
	pdf_item *item;
	pdf_item *next;
	struct refkey *key;
	void *val;
	int i;
	for (i = 0; i < fz_hashlen(store->hash); i++)
	{
		key = fz_hashgetkey(store->hash, i);
		val = fz_hashgetval(store->hash, i);
		if (val) {
#ifdef PSP
			if (key->kind == PDF_KIMAGE)
				printf("es 1.2 - I %p\n", val);
#endif
			dropitem(key->kind, val);
		}
	}
	fz_emptyhash(store->hash);
	for (item = store->root; item; item = next)
	{
		next = item->next;
		fz_dropobj(item->key);
		dropitem(item->kind, item->val);
		fz_free(item);
	}
	store->root = nil;
}

void
pdf_dropstore(pdf_store *store)
{
	pdf_emptystore(store);
	fz_drophash(store->hash);
	fz_free(store);
}

fz_error *
pdf_storeitem(pdf_store *store, pdf_itemkind kind, fz_obj *key, void *val)
{
	fz_error *error;

	switch (kind)
	{
	case PDF_KCOLORSPACE: fz_keepcolorspace(val); break;
	case PDF_KFUNCTION: pdf_keepfunction(val); break;
	case PDF_KXOBJECT: pdf_keepxobject(val); break;
	case PDF_KIMAGE: fz_keepimage(val); break;
	case PDF_KPATTERN: pdf_keeppattern(val); break;
	case PDF_KSHADE: fz_keepshade(val); break;
	case PDF_KCMAP: pdf_keepcmap(val); break;
	case PDF_KFONT: fz_keepfont(val); break;
	}

	if (fz_isindirect(key))
	{
		struct refkey item;

		pdf_logrsrc("store item %d: %d %d R = %p\n", kind, fz_tonum(key), fz_togen(key), val);

		item.kind = kind;
		item.oid = fz_tonum(key);
		item.gen = fz_togen(key);

		error = fz_hashinsert(store->hash, &item, val);
		if (error)
			return error;
	}

	else
	{
		pdf_item *item;

		item = fz_malloc(sizeof(pdf_item));
		if (!item)
			return fz_outofmem;

		pdf_logrsrc("store item %d: ... = %p\n", kind, val);

		item->kind = kind;
		item->key = fz_keepobj(key);
		item->val = val;

		item->next = store->root;
		store->root = item;
	}

	return nil;
}

void *
pdf_finditem(pdf_store *store, pdf_itemkind kind, fz_obj *key)
{
	pdf_item *item;
	struct refkey refkey;

	if (key == nil)
		return nil;

	if (fz_isindirect(key))
	{
		refkey.kind = kind;
		refkey.oid = fz_tonum(key);
		refkey.gen = fz_togen(key);
		return fz_hashfind(store->hash, &refkey);
	}

	else
	{
		for (item = store->root; item; item = item->next)
			if (item->kind == kind && !fz_objcmp(item->key, key))
				return item->val;
	}

	return nil;
}

