#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;
}
