/*
//
// varstore.c
//
// Variable store functions (implemented with a splay tree)
// The splay tree algorithm are taken from D. Sleator's examples.
//
// Copyright (c) 1995-96 Jim Nelson.
// Copyright (c) 2002 Jochen Hoenicke.
// Released under Artistic License.
//
*/

#include "htp.h"
#include "defs.h"

/* statistics for performance measurement */
#if DEBUG
uint variableLookups = 0;
uint variableStringCompares = 0;
uint variableRotations = 0;
#endif

/*
// top down splay routine.
//
// If name is in the tree with root t, it will be moved to the
// top of the tree and the element is returned.
//
// If name is not in the tree a neighbour of name is moved to the
// top of the tree and NULL is returned.
//
// If tree is empty NULL is returned.
*/
static VARIABLE *VariableSplay (const char *name, VARIABLE ** root) {
    VARIABLE N, *t, *l, *r, *result;
    int cmp;
    
    t = *root;
    if (t == NULL)
	return t;

    /* Fast path */
    cmp = stricmp(name, t->name);
    if (cmp == 0)
	return t;

    /* Slow path: Need to reorder tree */
    result = NULL;
    N.left = N.right = NULL;
    l = r = &N;

    for (;;) {
#if DEBUG
	variableStringCompares++;
#endif
	if (cmp < 0) {
	    if (t->left == NULL) 
		break;
	    
	    cmp = stricmp(name, t->left->name);
	    if (cmp < 0) {
		VARIABLE *y;
		y = t->left;                           /* rotate right */
		t->left = y->right;
		y->right = t;
		t = y;
#if DEBUG
		variableStringCompares++;
		variableRotations++;
#endif
		if (t->left == NULL) 
		    break;
		cmp = stricmp(name, t->left->name);
	    }

	    r->left = t;                               /* link right */
	    r = t;
	    t = t->left;
	} else if (cmp > 0) {
	    if (t->right == NULL)
		break;

	    cmp = stricmp(name, t->right->name);
	    if (cmp > 0) {
		VARIABLE *y;
		y = t->right;                          /* rotate left */
		t->right = y->left;
		y->left = t;
		t = y;
#if DEBUG
		variableStringCompares++;
		variableRotations++;
#endif
		if (t->right == NULL) 
		    break;
		cmp = stricmp(name, t->right->name);
	    }
	    l->right = t;                              /* link left */
	    l = t;
	    t = t->right;

	} else {
	    /* we found the name */
	    result = t;
	    break;
	}
    }
    l->right = t->left;                                /* assemble */
    r->left = t->right;
    t->left = N.right;
    t->right = N.left;
    *root = t;
    return result;
}

BOOL InitializeVariableStore(VARSTORE *varstore)
{
    assert(varstore != NULL);

    /* initialize the store */
    varstore->parent = NULL;
    varstore->root = NULL;
    varstore->child = NULL;
    varstore->isGlobal = FALSE;

    return TRUE;
}

/*
// Call destructors for variable and/or free the name/value.
*/
static void DestroyVariable(VARIABLE *variable)
{
    /* if the variable has a destructor callback, do it */
    if(variable->destructor != NULL)
    {
        variable->destructor(variable->name, variable->value, variable->type,
            variable->flag, variable->param);
    }
    /* Deallocate name and value if the corresponding flags are set */
    if (variable->flag & VAR_FLAG_DEALLOC_NAME) {
        FreeMemory(variable->name);
    }
    if (variable->flag & VAR_FLAG_DEALLOC_VALUE) {
        FreeMemory(variable->value);
    }
}

static void ClearVariableTree(VARIABLE *t)
{
    if (t == NULL)
	return;

    ClearVariableTree(t->left);
    ClearVariableTree(t->right);
    DestroyVariable(t);
    FreeMemory(t);
}

void DestroyVariableStore(VARSTORE *varstore)
{
    /* The children must have been destroyed earlier */
    assert(varstore->child == NULL);
    
    /* unlink from parent */
    if(varstore->parent != NULL)
    {
	varstore->parent->child = NULL;
	varstore->parent = NULL;
    }
    
    /* destroy all variables in this store */
    ClearVariableTree(varstore->root);
    varstore->root = NULL;
}

static VARIABLE *FindVariable(VARSTORE *varstore, const char *name)
{
    VARIABLE *ptr;

    assert(varstore != NULL);
    assert(name != NULL);

#if DEBUG
    variableLookups++;
#endif

    do {
        ptr = VariableSplay(name, &varstore->root);
        if(ptr)
            return ptr;

        /* check all parent stores as well */
        varstore = varstore->parent;
    } while (varstore != NULL);

    return NULL;
}   

void *GetVariableValue(VARSTORE *varstore, const char *name)
{
    char *value;
    VARIABLE *variable;

    if(name != NULL)
    {
        if((variable = FindVariable(varstore, name)) != NULL)
        {
            return variable->value;
        }
	if((value = getenv(name)) != NULL)
	{
	    return value;
	}
    }

    return NULL;
}   

BOOL StoreVariable(VARSTORE *varstore, const char *name, void *value,
    uint type, uint flag, void *param, VAR_DESTRUCTOR destructor)
{
    VARIABLE *variable;

    assert(varstore != NULL);
    assert(name != NULL);

    /* remove any variable with the same name in the store */
    variable = VariableSplay(name, &varstore->root);

    if (variable) {

	/* Replace an existing variable */
	DestroyVariable(variable);

    } else {

	/* allocate space for the variable structure */
	if((variable = AllocMemory(sizeof(VARIABLE))) == NULL)
	{
	    DEBUG_PRINT(("unable to allocate memory for new variable"));
	    return FALSE;
	}
	if (varstore->root == NULL) {
	    variable->left = variable->right = NULL;
	} else if (stricmp(name, varstore->root->name) < 0) {
	    variable->left = varstore->root->left;
	    variable->right = varstore->root;
	    varstore->root->left = NULL;
	} else {
	    variable->right = varstore->root->right;
	    variable->left = varstore->root;
	    varstore->root->right = NULL;
	}
	varstore->root = variable;
    }

    /* set the variable's name and value */
    variable->name = (char *) name;
    variable->value = value;

    /* set flags and destructor pointer */
    variable->type = type;
    variable->flag = flag;
    variable->param = param;
    variable->destructor = destructor;

    return TRUE;
}   

/*
//  TODO: Get rid of the function.  IncProcessor is the only one using this.
*/
BOOL UpdateVariableValue(VARSTORE *varstore, const char *name, void *value)
{
    VARIABLE *variable;

    assert(name != NULL);
    assert(value != NULL);

    do {
        variable = VariableSplay(name, &varstore->root);
        if (variable)
            break;

        /* check the parent store if this isn't the global store */
        if (varstore->isGlobal)
            break;
        varstore = varstore->parent;
    } while (varstore != NULL);

    if (variable == NULL)
        return FALSE;
    
    /* Replace an existing variable */
    if (variable->value != NULL
        && (variable->flag & VAR_FLAG_DEALLOC_VALUE))
        FreeMemory(variable->value);
    
    variable->value = value;
    variable->flag |= VAR_FLAG_DEALLOC_VALUE;
    variable->flag &= ~VAR_FLAG_UNDEFINED;
    return TRUE;
}

uint GetVariableType(VARSTORE *varstore, const char *name)
{
    VARIABLE *variable;
    
    if(name != NULL)
    {
        if((variable = FindVariable(varstore, name)) != NULL)
        {
            return variable->type;
        }
	if(getenv(name) != NULL)
	{
	    return 1;
	}
    }

    return VAR_TYPE_UNKNOWN;
}

uint GetVariableFlag(VARSTORE *varstore, const char *name)
{
    VARIABLE *variable;

    if(name != NULL)
    {
        if((variable = FindVariable(varstore, name)) != NULL)
        {
            return variable->flag;
        }
    }

    return VAR_FLAG_UNKNOWN;
}   

BOOL RemoveVariable(VARSTORE *varstore, const char *name)
{
    VARIABLE *ptr;

    assert(varstore != NULL);
    assert(name != NULL);

    ptr = VariableSplay(name, &varstore->root);
    if(ptr)
    {
	/* found the variable */
	if (ptr->left == NULL)
	{
	    varstore->root = ptr->right;
	}
	else
	{
	    VariableSplay(name, &ptr->left);
	    varstore->root = ptr->left;
	    varstore->root->right = ptr->right;
	}
	/* call destructors */
	DestroyVariable(ptr);
	/* free the structure */
	FreeMemory(ptr);
	return TRUE;
    }

    /* check all parent stores as well */
    if(varstore->parent != NULL)
    {
        return RemoveVariable(varstore->parent, name);
    }

    return FALSE;
}

BOOL VariableExists(VARSTORE *varstore, const char *name)
{
    if (FindVariable(varstore, name) != NULL) {
	return TRUE;
    }
    if (getenv(name) != NULL) {
	return TRUE;
    }
    return FALSE;
}

void *GetVariableParam(VARSTORE *varstore, const char *name)
{
    VARIABLE *var;

    if(name != NULL)
    {
        if((var = FindVariable(varstore, name)) != NULL)
        {
            return var->param;
        }
    }

    return NULL;
}

void PushVariableStoreContext(VARSTORE *parent, VARSTORE *varstore)
{
    assert(parent != NULL);
    assert(parent->child == NULL);
    assert(varstore != NULL);
    assert(varstore->parent == NULL);

    parent->child = varstore;
    varstore->parent = parent;
    varstore->child = NULL;
}

VARSTORE *PopVariableStoreContext(VARSTORE *varstore)
{
    assert(varstore != NULL);
    assert(varstore->child == NULL);

    /* unlink from parent and return current context */
    varstore->parent->child = NULL;
    varstore->parent = NULL;

    return varstore;
}
