/*
//
// option.c
//
// Program options and setting
//
// Copyright (c) 1995-96 Jim Nelson.  Permission to distribute
// granted by the author.  No warranties are made on the fitness of this
// source code.
// Amiga version - 1997 - Geert Bevin
//
*/

#include "htp.h"

static VARSTORE globalOption;
static VARSTORE localOption;
static VARSTORE *currentContext = NULL;

/* option variable types */
#define VAR_TYPE_OPTION                 (0)

/* option strings (user, not internal names) */
const char *OPT_STR_IMGXY = "IMGXY";
const char *OPT_STR_NOIMGXY = "NOIMGXY";
const char *OPT_STR_VERBOSE = "VERBOSE";
const char *OPT_STR_QUIET = "QUIET";
const char *OPT_STR_DEPEND = "DEPEND";
const char *OPT_STR_NODEPEND = "NODEPEND";
const char *OPT_STR_PRECIOUS = "PRECIOUS";
const char *OPT_STR_NOPRECIOUS = "NOPRECIOUS";
const char *OPT_STR_CONDENSE = "CONDENSE";
const char *OPT_STR_NOCONDENSE = "NOCONDENSE";
const char *OPT_STR_SET_DELIM = "DELIM";
const char *OPT_STR_DELIM_HTML = "HTML";
const char *OPT_STR_DELIM_SQUARE = "SQUARE";
const char *OPT_STR_DELIM_CURLY = "CURLY";
const char *OPT_STR_KEEP_TEMP = "KEEPTEMP";

/* option string names */
const char *OPT_N_IMGXY = "IMGXY";
const char *OPT_N_QUIET = "QUIET";
const char *OPT_N_DEPEND = "DEPEND";
const char *OPT_N_PRECIOUS = "PRECIOUS";
const char *OPT_N_CONDENSE = "CONDENSE";
const char *OPT_N_USAGE = "USAGE";
const char *OPT_N_SET_DELIM = "DELIM";
const char *OPT_N_KEEP_TEMP = "KEEPTEMP";

/* option string values */
const char *OPT_V_TRUE = "TRUE";
const char *OPT_V_FALSE = "FALSE";
const char *OPT_V_DELIM_HTML = "HTML";
const char *OPT_V_DELIM_SQUARE = "SQUARE";
const char *OPT_V_DELIM_CURLY = "CURLY";

/*
//
*/

BOOL SetOption(const char *name, const char *value)
{
    return StoreVariable(currentContext, name, value, VAR_TYPE_OPTION,
        0, NULL, NULL);
}

BOOL InitOptionHelper(const char *name, const char *value, OPTION_CALLBACK optionCallback,
    ULONG userParam)
{
    BOOL store;

    store = TRUE;
    if(optionCallback != NULL)
    {
        store = optionCallback(name, value, userParam);
    }

    if(store)
    {
        if(SetOption(name, value) == FALSE)
        {
            return FALSE;
        }
    }

    return TRUE;
}

BOOL InitializeGlobalOption(OPTION_CALLBACK optionCallback, ULONG userParam)
{
    BOOL result;

    if(InitializeVariableStore(&globalOption) == FALSE)
    {
        return FALSE;
    }

    currentContext = &globalOption;

    /* set defaults for all options */
    result = InitOptionHelper(OPT_N_IMGXY, OPT_V_TRUE, optionCallback, userParam);
    result = InitOptionHelper(OPT_N_QUIET, OPT_V_FALSE, optionCallback, userParam) && result;
    result = InitOptionHelper(OPT_N_DEPEND, OPT_V_TRUE, optionCallback, userParam) && result;
    result = InitOptionHelper(OPT_N_PRECIOUS, OPT_V_FALSE, optionCallback, userParam) && result;
    result = InitOptionHelper(OPT_N_CONDENSE, OPT_V_FALSE, optionCallback, userParam) && result;
    result = InitOptionHelper(OPT_N_USAGE, OPT_V_FALSE, optionCallback, userParam) && result;
    result = InitOptionHelper(OPT_N_SET_DELIM, OPT_V_DELIM_HTML, optionCallback, userParam) && result;
    result = InitOptionHelper(OPT_N_KEEP_TEMP, OPT_V_FALSE, optionCallback, userParam) && result;

    /* if a problem occurred, back out */
    if(result == FALSE)
    {
        DEBUG_PRINT(("Unable to store global defaults\n"));
        DestroyGlobalOption();
    }

    return result;
}

void DestroyGlobalOption(void)
{
    currentContext = NULL;
    DestroyVariableStore(&globalOption);
}

BOOL InitializeLocalOption(void)
{
    if(InitializeVariableStore(&localOption) == TRUE)
    {
        PushVariableStoreContext(&globalOption, &localOption);
        currentContext = &localOption;
        return TRUE;
    }

    return FALSE;
}

void DestroyLocalOption(void)
{
    currentContext = &globalOption;
    DestroyVariableStore(&localOption);
}

BOOL ParseToken(const char *token, OPTION_CALLBACK optionCallback, ULONG userParam)
{
    const char *name;
    const char *value;
    BOOL store;
    char *tokenCopy;
    char *tokenPtr;
    char *tokenValue;
    FIND_TOKEN findToken;

    name = NULL;
    value = NULL;

    /* process the string ... probably a better way to do this */
    /* rather than brute force, but for now, it works */
    if(stricmp(token, OPT_STR_IMGXY) == 0)
    {
        name = OPT_N_IMGXY;
        value = OPT_V_TRUE;
    }
    else if(stricmp(token, OPT_STR_NOIMGXY) == 0)
    {
        name = OPT_N_IMGXY;
        value = OPT_V_FALSE;
    }
    else if(stricmp(token, OPT_STR_VERBOSE) == 0)
    {
        name = OPT_N_QUIET;
        value = OPT_V_FALSE;
    }
    else if(stricmp(token, OPT_STR_QUIET) == 0)
    {
        name = OPT_N_QUIET;
        value = OPT_V_TRUE;
    }
    else if(stricmp(token, OPT_STR_DEPEND) == 0)
    {
        name = OPT_N_DEPEND;
        value = OPT_V_TRUE;
    }
    else if(stricmp(token, OPT_STR_NODEPEND) == 0)
    {
        name = OPT_N_DEPEND;
        value = OPT_V_FALSE;
    }
    else if(stricmp(token, OPT_STR_PRECIOUS) == 0)
    {
        name = OPT_N_PRECIOUS;
        value = OPT_V_TRUE;
    }
    else if(stricmp(token, OPT_STR_NOPRECIOUS) == 0)
    {
        name = OPT_N_PRECIOUS;
        value = OPT_V_FALSE;
    }
    else if(stricmp(token, OPT_STR_CONDENSE) == 0)
    {
        name = OPT_N_CONDENSE;
        value = OPT_V_TRUE;
    }
    else if(stricmp(token, OPT_STR_NOCONDENSE) == 0)
    {
        name = OPT_N_CONDENSE;
        value = OPT_V_FALSE;
    }
    else if(stricmp(token, OPT_STR_KEEP_TEMP) == 0)
    {
        name = OPT_N_KEEP_TEMP;
        value = OPT_V_TRUE;
    }
    else if((stricmp(token, "?") == 0) || (stricmp(token, "H") == 0))
    {
        name = OPT_N_USAGE;
        value = OPT_V_TRUE;
    }
    else
    {
        tokenPtr = NULL;
        tokenValue = NULL;

        /* further break up the token, looking for an equal sign */
        if((tokenCopy = DuplicateString(token)) == NULL)
        {
            DEBUG_PRINT(("unable to duplicate token string"));
            return FALSE;
        }

        tokenPtr = StringFirstToken(&findToken, tokenCopy, "=");
        if(tokenPtr != NULL)
        {
            tokenValue = StringNextToken(&findToken);
        }

        if((tokenPtr != NULL) && (tokenValue != NULL))
        {
            /* tokenPtr now points to the left side of the equal sign (if there */
            /* is one */
            if(stricmp(tokenPtr, OPT_STR_SET_DELIM) == 0)
            {
                name = OPT_N_SET_DELIM;

                if(stricmp(tokenValue, OPT_STR_DELIM_HTML) == 0)
                {
                    value = OPT_V_DELIM_HTML;
                }
                else if(stricmp(tokenValue, OPT_STR_DELIM_SQUARE) == 0)
                {
                    value = OPT_V_DELIM_SQUARE;
                }
                else if(stricmp(tokenValue, OPT_STR_DELIM_CURLY) == 0)
                {
                    value = OPT_V_DELIM_CURLY;
                }
                else
                {
                    /* something screwy here! */
                    name = NULL;
                    value = NULL;
                }
            }
        }

        FreeMemory(tokenCopy);
        tokenCopy = NULL;
    }

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

        store = TRUE;
        if(optionCallback != NULL)
        {
            store = optionCallback(name, value, userParam);
        }

        if(store)
        {
            return SetOption(name, value);
        }

        return TRUE;
    }
    else if(optionCallback != NULL)
    {
        /* report the bad string through the option callback */
        return optionCallback(NULL, token, userParam);
    }

    /* FALSE indicates an error only */
    return FALSE;
}

BOOL ParseTextOption(const char *string, OPTION_CALLBACK optionCallback,
    ULONG userParam)
{
    char *tokenize;
    char *ptr;
    BOOL result;
    FIND_TOKEN findToken;

    /* copy the string to run through String...Token() */
    if((tokenize = DuplicateString(string)) == NULL)
    {
        DEBUG_PRINT(("unable to allocate duplicate string"));
        return FALSE;
    }

    result = TRUE;
    ptr = StringFirstToken(&findToken, tokenize, " ");
    while(ptr != NULL)
    {
        /* skip any leading slashes or dashes */
        while((((*ptr == '/') || (*ptr == '-'))) && (*ptr != NUL))
        {
            ptr++;
        }

        /* parse this token */
        if(ParseToken(ptr, optionCallback, userParam) == FALSE)
        {
            result = FALSE;
            break;
        }

        /* do it till the cows come home */
        ptr = StringNextToken(&findToken);
    }

    FreeMemory(tokenize);

    return result;
}

BOOL ParseMarkupOption(const HTML_MARKUP *htmlMarkup, OPTION_CALLBACK optionCallback,
    ULONG userParam)
{
    uint ctr;
    char token[128];
    BOOL result;

    result = TRUE;

    /* technically, would like to avoid iterating through the HTML_MARKUP */
    /* structure for certain (future design) reasons, but really have to */
    /* for this to work */
    for(ctr = 0; ctr < htmlMarkup->attribCount; ctr++)
    {
        /* have to build the token as well ... kind of hurts to break up */
        /* the attribute only to put it back together again */
        StringCopy(token, htmlMarkup->attrib[ctr].name, sizeof(token));
        if(htmlMarkup->attrib[ctr].value != NULL)
        {
            strncat(token, "=", sizeof(token));
            strncat(token, htmlMarkup->attrib[ctr].value, sizeof(token));
        }

        if(ParseToken(token, optionCallback, userParam) == FALSE)
        {
            result = FALSE;
            break;
        }
    }

    return result;
}

BOOL IsOptionEnabled(const char *name)
{
    const char *value;

    value = GetVariableValue(currentContext, name);

    /* !! ugh ... probably shouldnt assert() these, but for now ... */
    /* first case means that the caller passed in a bogus option name */
    /* second case means that the option isnt boolean */
    if(value == NULL)
    {
        DEBUG_PRINT(("looking up %s", name));
    }
    assert(value != NULL);
    assert((stricmp(value, OPT_V_TRUE) == 0) || (stricmp(value, OPT_V_FALSE) == 0));

    if(stricmp(value, OPT_V_TRUE) == 0)
    {
        return TRUE;
    }

    return FALSE;
}

const char *GetOptionValue(const char *name)
{
    const char *value;

    value = GetVariableValue(currentContext, name);
    assert(value != NULL);
    return value;
}

