/* $Id: sprite.c 3044 2005-10-14 20:10:12Z peter1138 $ */

#include "stdafx.h"
#include "openttd.h"
#include "sprite.h"
#include "variables.h"
#include "debug.h"


SpriteGroup *EvalDeterministicSpriteGroup(const DeterministicSpriteGroup *dsg, int value)
{
	int i;

	value >>= dsg->shift_num; // This should bring us to the byte range.
	value &= dsg->and_mask;

	if (dsg->operation != DSG_OP_NONE)
		value += (signed char) dsg->add_val;

	switch (dsg->operation) {
		case DSG_OP_DIV:
			value /= (signed char) dsg->divmod_val;
			break;
		case DSG_OP_MOD:
			value %= (signed char) dsg->divmod_val;
			break;
		case DSG_OP_NONE:
			break;
	}

	for (i = 0; i < dsg->num_ranges; i++) {
		DeterministicSpriteGroupRange *range = &dsg->ranges[i];

		if (range->low <= value && value <= range->high)
			return range->group;
	}

	return dsg->default_group;
}

int GetDeterministicSpriteValue(byte var)
{
	switch (var) {
		case 0x00:
			return _date;
		case 0x01:
			return _cur_year;
		case 0x02:
			return _cur_month;
		case 0x03:
			return _opt.landscape;
		case 0x09:
			return _date_fract;
		case 0x0A:
			return _tick_counter;
		case 0x0C:
			/* If we got here, it means there was no callback or
			 * callbacks aren't supported on our callpath. */
			return 0;
		default:
			return -1;
	}
}

SpriteGroup *EvalRandomizedSpriteGroup(const RandomizedSpriteGroup *rsg, byte random_bits)
{
	byte mask;
	byte index;

	/* Noone likes mangling with bits, but you don't get around it here.
	 * Sorry. --pasky */
	// rsg->num_groups is always power of 2
	mask = (rsg->num_groups - 1) << rsg->lowest_randbit;
	index = (random_bits & mask) >> rsg->lowest_randbit;
	assert(index < rsg->num_groups);
	return rsg->groups[index];
}

byte RandomizedSpriteGroupTriggeredBits(const RandomizedSpriteGroup *rsg,
	byte triggers, byte *waiting_triggers)
{
	byte match = rsg->triggers & (*waiting_triggers | triggers);
	bool res;

	if (rsg->cmp_mode == RSG_CMP_ANY) {
		res = (match != 0);
	} else { /* RSG_CMP_ALL */
		res = (match == rsg->triggers);
	}

	if (!res) {
		*waiting_triggers |= triggers;
		return 0;
	}

	*waiting_triggers &= ~match;

	return (rsg->num_groups - 1) << rsg->lowest_randbit;
}

/**
 * Traverse a sprite group and release its and its child's memory.
 * A group is only released if its reference count is zero.
 * We pass a pointer to a pointer so that the original reference can be set to NULL.
 * @param group_ptr Pointer to sprite group reference.
 */
void UnloadSpriteGroup(SpriteGroup **group_ptr)
{
	SpriteGroup *group;
	int i;

	assert(group_ptr != NULL);
	assert(*group_ptr != NULL);

	group = *group_ptr;
	*group_ptr = NULL; // Remove this reference.

	group->ref_count--;
	if (group->ref_count > 0) {
		DEBUG(grf, 6)("UnloadSpriteGroup: Group at `%p' (type %d) has %d reference(s) left.", group, group->type, group->ref_count);
		return; // Still some references left, so don't clear up.
	}

	DEBUG(grf, 6)("UnloadSpriteGroup: Releasing group at `%p'.", group);
	switch (group->type) {
		case SGT_REAL:
		{
			RealSpriteGroup *rsg = &group->g.real;
			for (i = 0; i < rsg->loading_count; i++) {
				if (rsg->loading[i] != NULL) UnloadSpriteGroup(&rsg->loading[i]);
			}
			for (i = 0; i < rsg->loaded_count; i++) {
				if (rsg->loaded[i] != NULL) UnloadSpriteGroup(&rsg->loaded[i]);
			}
			free(group);
			return;
		}

		case SGT_DETERMINISTIC:
		{
			DeterministicSpriteGroup *dsg = &group->g.determ;
			for (i = 0; i < group->g.determ.num_ranges; i++) {
				if (dsg->ranges[i].group != NULL) UnloadSpriteGroup(&dsg->ranges[i].group);
			}
			if (dsg->default_group != NULL) UnloadSpriteGroup(&dsg->default_group);
			free(group->g.determ.ranges);
			free(group);
			return;
		}

		case SGT_RANDOMIZED:
		{
			for (i = 0; i < group->g.random.num_groups; i++) {
				if (group->g.random.groups[i] != NULL) UnloadSpriteGroup(&group->g.random.groups[i]);
			}
			free(group->g.random.groups);
			free(group);
			return;
		}

		case SGT_CALLBACK:
		case SGT_RESULT:
			free(group);
			return;
	}

	DEBUG(grf, 1)("Unable to remove unknown sprite group type `0x%x'.", group->type);
}
