/*
	libsmacker - A C library for decoding .smk Smacker Video files
	Copyright (C) 2012-2013 Greg Kennedy

	See smacker.h for more information.

	smk_hufftree.c
		Implementation of Smacker Huffman coding trees.
*/

#include "smk_hufftree.h"

/* malloc and friends */
#include "smk_malloc.h"

/* function to recursively delete a huffman tree */
void smk_huff_free(struct smk_huff_t *t)
{
	/* Sanity check: do not double-free */
	smk_null_check(t);

	/* If this is not a leaf node, free child trees first */
	if (t->b0)
	{
		smk_huff_free(t->b0);
		smk_huff_free(t->u.b1);
	}

	/* Safe-delete tree node. */
	smk_free(t);

error: ;
}

/* safe build with built-in error jump */
#define smk_huff_safe_build_rec(bs,p) \
{ \
	if (!(p = smk_huff_build_rec(bs))) \
	{ \
		fprintf(stderr,"libsmacker::smk_huff_safe_build_rec(" #bs "," #p ") - ERROR (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \
		goto error; \
	} \
}

/* Recursive tree-building function. */
static struct smk_huff_t *smk_huff_build_rec(struct smk_bit_t *bs)
{
	struct smk_huff_t *ret = NULL;
	char bit;

	/* sanity check */
	smk_null_check(bs);

	/* Read the bit */
	smk_bs_safe_read_1(bs,bit);

	/* Malloc a structure. */
	smk_malloc(ret,sizeof(struct smk_huff_t));

	if (bit)
	{
		/* Bit set: this forms a Branch node. */
		/* Recursively attempt to build the Left branch. */
		smk_huff_safe_build_rec(bs,ret->b0);

		/* Everything is still OK: attempt to build the Right branch. */
		smk_huff_safe_build_rec(bs,ret->u.b1);

		/* return branch pointer here */
		return ret;
	}

	/* Bit unset signifies a Leaf node. */
	/* Attempt to read value */
	smk_bs_safe_read_8(bs,ret->u.leaf.value);

	/* smk_malloc sets entries to 0 by default */
	/* ret->b0 = NULL; */
	ret->u.leaf.escapecode = 0xFF;

	return ret;

error:
	smk_free(ret);
	return NULL;
}

/*
	Entry point for huff_build.  Basically just checks the start/end tags
	and calls smk_huff_build_rec recursive function.
*/
struct smk_huff_t *smk_huff_build(struct smk_bit_t* bs)
{
	struct smk_huff_t *ret = NULL;
	char bit;

	/* sanity check */
	smk_null_check(bs);

	/* Smacker huff trees begin with a set-bit. */
	smk_bs_safe_read_1(bs,bit);

	if (!bit)
	{
		/* Got a bit, but it was not 1. In theory, there could be a smk file
			without this particular tree. */
		fputs("libsmacker::smk_huff_build(bs) - Warning: initial get_bit returned 0\n",stderr);
		goto error;
	}

	/* Begin parsing the tree data. */
	smk_huff_safe_build_rec(bs,ret);

	/* huff trees end with an unset-bit */
	smk_bs_safe_read_1(bs,bit);

	if (bit)
	{
		fputs("libsmacker::smk_huff_build(bs) - ERROR: final get_bit returned 1\n",stderr);
		goto error;
	}

	return ret;

error:
	smk_free(ret);
	return NULL;
}

/* Look up an 8-bit value from a basic huff tree.
	Return -1 on error. */
short smk_huff_lookup (struct smk_bit_t *bs, const struct smk_huff_t *t)
{
	char bit;

	/* sanity check */
	smk_null_check(bs);
	smk_null_check(t);

	if (!t->b0)
	{
		/* Reached a Leaf node.  Return its value. */
		return t->u.leaf.value;
	}

	smk_bs_safe_read_1(bs,bit);

	if (bit)
	{
		/* get_bit returned Set, follow Right branch. */
		return smk_huff_lookup(bs,t->u.b1);
	}

	/* follow Right branch */
	return smk_huff_lookup(bs,t->b0);

error:
	return -1;
}

#define smk_huff_big_safe_build_rec(bs,cache,low8,hi8,p) \
{ \
	if (!(p = smk_huff_big_build_rec(bs,cache,low8,hi8))) \
	{ \
		fprintf(stderr,"libsmacker::smk_huff_big_safe_build_rec(" #bs "," #cache "," #low8 "," #hi8 "," #p ") - ERROR (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \
		goto error; \
	} \
}

/* Recursively builds a Big tree. */
static struct smk_huff_t *smk_huff_big_build_rec(struct smk_bit_t *bs, const unsigned short cache[3], const struct smk_huff_t *low8, const struct smk_huff_t *hi8)
{
	struct smk_huff_t *ret = NULL;

	char bit;
	short lowval;

	/* sanity check */
	smk_null_check(bs);
	smk_null_check(cache);
	smk_null_check(low8);
	smk_null_check(hi8);

	/* Get the first bit */
	smk_bs_safe_read_1(bs,bit);

	/* Malloc a structure. */
	smk_malloc(ret,sizeof(struct smk_huff_t));

	if (bit)
	{
		/* Recursively attempt to build the Left branch. */
		smk_huff_big_safe_build_rec(bs,cache,low8,hi8,ret->b0);

		/* Recursively attempt to build the Left branch. */
		smk_huff_big_safe_build_rec(bs,cache,low8,hi8,ret->u.b1);

		/* return branch pointer here */
		return ret;
	}

	/* Bit unset signifies a Leaf node. */
	smk_huff_safe_lookup(bs,low8,lowval);
	smk_huff_safe_lookup(bs,hi8,ret->u.leaf.value);

	/* Looks OK: we got low and hi values.  Return a new LEAF */
	/* ret->b0 = NULL; */
	ret->u.leaf.value = lowval | (ret->u.leaf.value << 8);

	/* Last: when building the tree, some Values may correspond to cache positions.
		Identify these values and set the Escape code byte accordingly. */
	if (ret->u.leaf.value == cache[0])
	{
		ret->u.leaf.escapecode = 0;
	}
	else if (ret->u.leaf.value == cache[1])
	{
		ret->u.leaf.escapecode = 1;
	}
	else if (ret->u.leaf.value == cache[2])
	{
		ret->u.leaf.escapecode = 2;
	}
	else
	{
		ret->u.leaf.escapecode = 0xFF;
	}

	return ret;

error:
	smk_free(ret);
	return NULL;
}

/* Entry point for building a big 16-bit tree. */
struct smk_huff_big_t *smk_huff_big_build(struct smk_bit_t* bs)
{
	struct smk_huff_big_t *big = NULL;

	struct smk_huff_t *low8 = NULL, *hi8 = NULL;

	short lowval;

	char bit;
	unsigned char i;

	/* sanity check */
	smk_null_check(bs);

	/* Smacker huff trees begin with a set-bit. */
	smk_bs_safe_read_1(bs,bit);

	if (!bit)
	{
		fputs("libsmacker::smk_huff_big_build(bs) - ERROR: initial get_bit returned 0\n",stderr);
		goto error;
	}

	/* build low-8-bits tree */
	smk_huff_safe_build(bs,low8);
	/* build hi-8-bits tree */
	smk_huff_safe_build(bs,hi8);

	/* Everything looks OK so far.  Time to malloc structure. */
	smk_malloc(big,sizeof(struct smk_huff_big_t));

	/* Init the escape code cache. */
	for (i = 0; i < 3; i ++)
	{
		smk_bs_safe_read_8(bs,lowval);
		smk_bs_safe_read_8(bs,big->cache[i]);
		big->cache[i] = lowval | (big->cache[i] << 8);
/* fprintf(stderr,"Escape code cache: cache[%d] = %d\n",i,big->cache[i]); */
	}

	/* Finally, call recursive function to retrieve the Bigtree. */
	smk_huff_big_safe_build_rec(bs,big->cache,low8,hi8,big->t);

	/* Done with 8-bit hufftrees, free them. */
	smk_huff_free(hi8);
	hi8 = NULL;
	smk_huff_free(low8);
	low8 = NULL;

	/* Check final end tag. */
	smk_bs_safe_read_1(bs,bit);

	if (bit)
	{
		fputs("libsmacker::smk_huff_big_build(bs) - ERROR: final get_bit returned 1\n",stderr);
		goto error;
	}

	return big;

error:
	smk_free(big);
	smk_free(hi8);
	smk_free(low8);
	return NULL;
}

static int smk_huff_big_lookup_rec (struct smk_bit_t *bs, unsigned short cache[3], struct smk_huff_t *t)
{
	unsigned short val;
	char bit;

	/* sanity check */
	smk_null_check(bs);
	smk_null_check(cache);
	smk_null_check(t);

	/* Reached a Leaf node */
	if (!t->b0)
	{
		if (t->u.leaf.escapecode != 0xFF)
		{
			/* Found escape code. Retrieve value from Cache. */
			val = cache[t->u.leaf.escapecode];
		}
		else
		{
			/* Use value directly. */
			val = t->u.leaf.value;
		}

		if ( cache[0] != val)
		{
			/* Update the cache, by moving val to the front of the queue,
				if it isn't already there. */
			cache[2] = cache[1];
			cache[1] = cache[0];
			cache[0] = val;
		}

		return val;
	}

	smk_bs_safe_read_1(bs,bit);

	if (bit)
	{
		/* get_bit returned Set, follow Right branch. */
		return smk_huff_big_lookup_rec(bs,cache,t->u.b1);
	}

	return smk_huff_big_lookup_rec(bs,cache,t->b0);

error:
	return -1;
}

/* Convenience call-out for recursive bigtree lookup function */
long smk_huff_big_lookup (struct smk_bit_t *bs, struct smk_huff_big_t *big)
{
	return smk_huff_big_lookup_rec(bs,big->cache,big->t);
}

/* Resets a Big hufftree cache */
void smk_huff_big_reset (struct smk_huff_big_t *big)
{
	big->cache[0] = 0;
	big->cache[1] = 0;
	big->cache[2] = 0;
}
