#include <v_lib.h>
#include "eval.h"
#include "efunc.h"
#include "messages.h"
#include "istrings.h"

#ifdef  __ICL
/* invalid type conversion: void_p -> unsigned */
#pragma warning(disable:171)
#endif  /* __ICL */

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

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

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

typedef struct state_field_s
{
	char_p	  *name_ptr;
	char_p	  *desc_ptr;
	u32_t	   type;
	u32_t	   offset;
	struct /* can't use 'union' since we use static initialization */
	{
		s32_t s32_val;
		u32_t u32_val;
		vboolean vboolean_val;
		vec_t real_val;
	} def;
}   state_field_t, *state_field_p;

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

V_LOCAL state_field_t state_table[] = 
{
	{
		&ipl_key_custom_width,
		&ipl_desc_custom_width,
		STATE_U32,
		STATE_OFF(custom_width),
		{0,0,false,0}
	},
	{
		&ipl_key_custom_height,
		&ipl_desc_custom_height,
		STATE_U32,
		STATE_OFF(custom_height),
		{0,0,false,0}
	},
	{
		&ipl_key_custom_size,
		&ipl_desc_custom_size,
		STATE_U32,
		STATE_OFF(custom_size),
		{0,0,false,0}
	},
	{
		&ipl_key_input_powers2,
		&ipl_desc_input_powers2,
		STATE_VBOOLEAN,
		STATE_OFF(input_powers2),
		{0,0,false,0}
	},
	{
		&ipl_key_input_square,
		&ipl_desc_input_square,
		STATE_VBOOLEAN,
		STATE_OFF(input_square),
		{0,0,false,0}
	},
	{
		&ipl_key_frequency_domain,
		&ipl_desc_frequency_domain,
		STATE_VBOOLEAN,
		STATE_OFF(frequency_domain),
		{0,0,false,0}
	},
	{
		&ipl_key_pad_value,
		&ipl_desc_pad_value,
		STATE_VEC_T,
		STATE_OFF(pad_value),
		{0,0,false,0}
	},
	{NULL}
};

V_LOCAL vboolean
stateSearchKey(char *key,u32_t *type_ret,u32_t *offset_ret)
{
	state_field_p head = state_table;
	for(;head->name_ptr;head++)
		if(STR_EQ(*head->name_ptr,key))
		{
			*type_ret = head->type;
			*offset_ret = head->offset;
			return true;
		}
	return false;
}

void
stateDefault(eval_state_p state)
{
	state_field_p head = state_table;
	for(;head->name_ptr;head++)
	{
		switch(head->type)
		{
			case STATE_S32:
				FIELD_ACCESS(*state,head->offset,s32_t) = head->def.s32_val;
				break;
			case STATE_U32:
				FIELD_ACCESS(*state,head->offset,u32_t) = head->def.u32_val;
				break;
			case STATE_VBOOLEAN:
				FIELD_ACCESS(*state,head->offset,vboolean) = head->def.vboolean_val;
				break;
			case STATE_VEC_T:
				FIELD_ACCESS(*state,head->offset,vec_t) = head->def.real_val;
				break;
			default:
				break;
		}
	}
}

V_LOCAL void
stateDump(eval_state_p state)
{
	STATIC_STRING(str_state,"state->")
	state_field_p head = state_table;
	for(;head->name_ptr;head++)
	{
		switch(head->type)
		{
			case STATE_S32:
				conlPrintf(LOG_TRACE,"%s%s = %d\n",str_state,
					*head->name_ptr,
					FIELD_ACCESS(*state,head->offset,s32_t));
				break;
			case STATE_U32:
				conlPrintf(LOG_TRACE,"%s%s = %u\n",str_state,
					*head->name_ptr,
					FIELD_ACCESS(*state,head->offset,u32_t));
				break;
			case STATE_VBOOLEAN:
				conlPrintf(LOG_TRACE,"%s%s = %s\n",str_state,
					*head->name_ptr,
					(FIELD_ACCESS(*state,head->offset,vboolean)) ? 
					ipl_key_true : ipl_key_false);
				break;
			case STATE_VEC_T:
				conlPrintf(LOG_TRACE,"%s%s = %g\n",str_state,
					*head->name_ptr,
					FIELD_ACCESS(*state,head->offset,vec_t));
				break;
			default:
				break;
		}
	}
}

void
stateHelp(void)
{
	char *type_name;
	state_field_p head = state_table;
	for(;head->name_ptr;head++)
	{
		switch(head->type)
		{
			case STATE_S32:
				type_name = ipl_key_signed;
				break;
			case STATE_U32:
				type_name = ipl_key_unsigned;
				break;
			case STATE_VBOOLEAN:
				type_name = ipl_key_boolean;
				break;
			case STATE_VEC_T:
				type_name = ipl_key_real;
				break;
			default:
				type_name = ipl_key_unknown_type;
				break;
		}
		conPrintf("\t\"%s\" (%s): %s%c\n",*head->name_ptr,type_name,
			*head->desc_ptr,(head + 1)->name_ptr ? ';' : '.');
	}
}

vboolean
stateChange(eval_state_p state,vhandle_t script)
{
	CALLER_NAME(stateChange)
	char *key;
	s32_t ival;
	u32_t uval;
	vec_t fval;
	vboolean bval;
	u32_t type, offset;
	key = lexNext(script);
	if(!key)
	{
		SYN_UNEXPECTED_EOF(script);
		return false;
	}
	if(!stateSearchKey(key,&type,&offset))
	{
		if(STR_EQ(key,ipl_key_dump))
		{
			stateDump(state);
			return true;
		}
		SYN_UNEXPECTED(script,key);
		return false;
	}
	switch(type)
	{
		case STATE_S32:
			if(evalInt(script,&ival))
			{
				FIELD_ACCESS(*state,offset,s32_t) = ival;
				return true;
			}
			break;
		case STATE_U32:
			if(evalUInt(script,&uval))
			{
				FIELD_ACCESS(*state,offset,u32_t) = uval;
				return true;
			}
			break;
		case STATE_VBOOLEAN:
			if(evalBoolean(script,&bval))
			{
				FIELD_ACCESS(*state,offset,vboolean) = bval;
				return true;
			}
			break;
		case STATE_VEC_T:
			if(evalReal(script,&fval))
			{
				FIELD_ACCESS(*state,offset,vec_t) = fval;
				return true;
			}
			break;
		default:
			break;
	}
	return false;
}
