#include <v_lib.h>
#include "eval.h"

#define	STATE_S32			0
#define	STATE_U32			1
#define	STATE_VBOOLEAN		2
#define	STATE_VEC_T			3

#define	FIELD_OFF(struct_id,field_id)	\
	(unsigned)(&((*((struct_id*)NULL)).field_id))

#define FIELD_ACCESS(storage,offset,field_type) \
	(*((field_type*)((unsigned char*)(&(storage))+(offset))))

typedef struct
{
	char		*name;
	unsigned	type;
	unsigned	offset;
	struct /* can't use 'union' since we use static initialization */
	{
		s32_t s32_val;
		u32_t u32_val;
		vboolean vboolean_val;
		vec_t vec_val;
	} def;
} state_field;

#define	STATE_OFF(field_id)	FIELD_OFF(eval_state_t,field_id)

state_field state_key_table[] = 
{
	{"custom_width",STATE_U32,STATE_OFF(custom_width),{0,0,false,0}},
	{"custom_size",STATE_U32,STATE_OFF(custom_width),{0,0,false,0}}, /* alias */
	{"custom_height",STATE_U32,STATE_OFF(custom_height),{0,0,false,0}},
	{"input_powers2",STATE_VBOOLEAN,STATE_OFF(input_powers2),{0,0,false,0}},
	{"input_square",STATE_VBOOLEAN,STATE_OFF(input_square),{0,0,false,0}},
	{NULL,0,0}
};

vboolean
stateSearchKey(char *key,u32_t *type_ret,u32_t *offset_ret)
{
	state_field *ptr;
	for(ptr = state_key_table;ptr->name;ptr++)
		if(STR_EQ(ptr->name,key))
		{
			*type_ret = ptr->type;
			*offset_ret = ptr->offset;
			return true;
		}
	return false;
}

void
stateDefault(eval_state_p state)
{
	state_field *ptr;
	for(ptr = state_key_table;ptr->name;ptr++)
	{
		switch(ptr->type)
		{
    		case STATE_S32:
   				FIELD_ACCESS(*state,ptr->offset,s32_t) = ptr->def.s32_val;
    			break;
    		case STATE_U32:
   				FIELD_ACCESS(*state,ptr->offset,u32_t) = ptr->def.u32_val;
    			break;
    		case STATE_VBOOLEAN:
				FIELD_ACCESS(*state,ptr->offset,vboolean) = ptr->def.vboolean_val;
    			break;
    		case STATE_VEC_T:
   				FIELD_ACCESS(*state,ptr->offset,vec_t) = ptr->def.vec_val;
    			break;
    		default:
    			break;
		}
	}
}

void
stateDump(eval_state_p state)
{
	state_field *ptr;
	for(ptr = state_key_table;ptr->name;ptr++)
	{
		switch(ptr->type)
		{
    		case STATE_S32:
   				conlPrintf(LOG_TRACE,"state->%s = %d\n",ptr->name,
   					FIELD_ACCESS(*state,ptr->offset,s32_t));
    			break;
    		case STATE_U32:
   				conlPrintf(LOG_TRACE,"state->%s = %u\n",ptr->name,
   					FIELD_ACCESS(*state,ptr->offset,u32_t));
    			break;
    		case STATE_VBOOLEAN:
   				conlPrintf(LOG_TRACE,"state->%s = %s\n",ptr->name,
   					(FIELD_ACCESS(*state,ptr->offset,vboolean)) ? "true" : "false");
    			break;
    		case STATE_VEC_T:
   				conlPrintf(LOG_TRACE,"state->%s = %g\n",ptr->name,
   					FIELD_ACCESS(*state,ptr->offset,vec_t));
    			break;
    		default:
    			break;
		}
	}
}

void
stateHelp(void)
{
	state_field *ptr;
	conPuts("Known state fields:\n");
	for(ptr = state_key_table;ptr->name;ptr++)
	{
		switch(ptr->type)
		{
    		case STATE_S32:
   				conPrintf("%s <integer>\n",ptr->name);
    			break;
    		case STATE_U32:
   				conPrintf("%s <unsigned integer>\n",ptr->name);
    			break;
    		case STATE_VBOOLEAN:
   				conPrintf("%s <true | false>\n",ptr->name);
    			break;
    		case STATE_VEC_T:
   				conPrintf("%s <float>\n",ptr->name);
    			break;
    		default:
    			break;
		}
	}
}

vboolean
stateChange(eval_state_p state,vhandle_t script)
{
	char *key;
	s32_t ival;
	u32_t uval;
	vec_t fval;
	u32_t type, offset;
	key = tokNext(script);
	if(!key)
	{
		conlPuts(LOG_ERROR,"stateChange(): unexpected EOF.\n");
		return false;
	}
	if(!stateSearchKey(key,&type,&offset))
	{
		if(STR_EQ(key,"dump"))
		{
			stateDump(state);
			return true;
		}
		conlPrintf(LOG_WARNING,
			"stateChange(): unknown key '%s', skipping next token.\n",key);
		tokNext(script);
		return false;
	}
	switch(type)
	{
		case STATE_S32:
			if(evalIntFunction(script,&ival))
			{
				FIELD_ACCESS(*state,offset,s32_t) = ival;
				return true;
			}
			conlPrintf(LOG_ERROR,
				"stateChange(): expected integer, got '%s'\n",
					dbs(tokLast(script)));
			break;
		case STATE_U32:
			if(evalUIntFunction(script,&uval))
			{
				FIELD_ACCESS(*state,offset,u32_t) = uval;
				return true;
			}
			conlPrintf(LOG_ERROR,
				"stateChange(): expected unsigned integer, got '%s'\n",
					dbs(tokLast(script)));
			break;
		case STATE_VBOOLEAN:
			if(tokNext(script))
			{
				if(tokLastWasEx(script,"true",STR_COMP))
					FIELD_ACCESS(*state,offset,vboolean) = true;
				else if(tokLastWasEx(script,"false",STR_COMP))
					FIELD_ACCESS(*state,offset,vboolean) = false;
				else
				{
					conlPrintf(LOG_ERROR,
						"stateChange(): expected 'true' or 'false', "
						"got '%s'\n",tokLast(script));
					break;
				}
				return true;
			}
			break;
		case STATE_VEC_T:
			if(evalRealFunction(script,&fval))
			{
				FIELD_ACCESS(*state,offset,vec_t) = fval;
				return true;
			}
			conlPrintf(LOG_ERROR,
				"stateChange(): expected float, got '%s'\n",
					dbs(tokLast(script)));
			break;
		default:
			break;
	}
	return false;
}
