
#include "opal_config.h"

#include "opal/constants.h"
#include "opal/util/keyval_parse.h"
#include "opal/util/keyval/keyval_lex.h"
#include "opal/util/output.h"
#include "opal/threads/mutex.h"
#ifdef HAVE_STRING_H
#include <string.h>
#endif  /* HAVE_STRING_H */

static const char *keyval_filename;
static opal_keyval_parse_fn_t keyval_callback;
static char *key_buffer = NULL;
static size_t key_buffer_len = 0;
static opal_mutex_t keyval_mutex;

static int parse_line(void);
static void parse_error(int num);

int opal_util_keyval_parse_init(void)
{
    OBJ_CONSTRUCT(&keyval_mutex, opal_mutex_t);

    return OPAL_SUCCESS;
}


int
opal_util_keyval_parse_finalize(void)
{
    if (NULL != key_buffer) free(key_buffer);

    OBJ_DESTRUCT(&keyval_mutex);

    return OPAL_SUCCESS;
}


int
opal_util_keyval_parse(const char *filename,
                       opal_keyval_parse_fn_t callback)
{
    int val;
    int ret = OPAL_SUCCESS;;

    OPAL_THREAD_LOCK(&keyval_mutex);

    keyval_filename = filename;
    keyval_callback = callback;

    /* Open the opal */
    opal_util_keyval_yyin = fopen(keyval_filename, "r");
    if (NULL == opal_util_keyval_yyin) {
        ret = OPAL_ERR_NOT_FOUND;
        goto cleanup;
    }

    opal_util_keyval_parse_done = false;
    opal_util_keyval_yynewlines = 1;
    opal_util_keyval_init_buffer(opal_util_keyval_yyin);
    while (!opal_util_keyval_parse_done) {
        val = opal_util_keyval_yylex();
        switch (val) {
        case OPAL_UTIL_KEYVAL_PARSE_DONE:
            /* This will also set opal_util_keyval_parse_done to true, so just
               break here */
            break;

        case OPAL_UTIL_KEYVAL_PARSE_NEWLINE:
            /* blank line!  ignore it */
            break;

        case OPAL_UTIL_KEYVAL_PARSE_SINGLE_WORD:
            parse_line();
            break;

        default:
            /* anything else is an error */
            parse_error(1);
            break;
        }
    }
    fclose(opal_util_keyval_yyin);
    opal_util_keyval_yylex_destroy ();

cleanup:
    OPAL_THREAD_UNLOCK(&keyval_mutex);
    return ret;
}



static int parse_line(void)
{
    int val;

    /* Save the name name */
    if (key_buffer_len < strlen(opal_util_keyval_yytext) + 1) {
        char *tmp;
        key_buffer_len = strlen(opal_util_keyval_yytext) + 1;
        tmp = (char*)realloc(key_buffer, key_buffer_len);
        if (NULL == tmp) {
            free(key_buffer);
            key_buffer_len = 0;
            key_buffer = NULL;
            return OPAL_ERR_TEMP_OUT_OF_RESOURCE;
        }
        key_buffer = tmp;
    }

    strncpy(key_buffer, opal_util_keyval_yytext, key_buffer_len);

    /* The first thing we have to see is an "=" */

    val = opal_util_keyval_yylex();
    if (opal_util_keyval_parse_done || OPAL_UTIL_KEYVAL_PARSE_EQUAL != val) {
        parse_error(2);
        return OPAL_ERROR;
    }

    /* Next we get the value */

    val = opal_util_keyval_yylex();
    if (OPAL_UTIL_KEYVAL_PARSE_SINGLE_WORD == val ||
        OPAL_UTIL_KEYVAL_PARSE_VALUE == val) {
        keyval_callback(key_buffer, opal_util_keyval_yytext);

        /* Now we need to see the newline */

        val = opal_util_keyval_yylex();
        if (OPAL_UTIL_KEYVAL_PARSE_NEWLINE == val ||
            OPAL_UTIL_KEYVAL_PARSE_DONE == val) {
            return OPAL_SUCCESS;
        }
    }

    /* Did we get an EOL or EOF? */

    else if (OPAL_UTIL_KEYVAL_PARSE_DONE == val ||
             OPAL_UTIL_KEYVAL_PARSE_NEWLINE == val) {
        keyval_callback(key_buffer, NULL);
        return OPAL_SUCCESS;
    }

    /* Nope -- we got something unexpected.  Bonk! */
    parse_error(3);
    return OPAL_ERROR;
}


static void parse_error(int num)
{
    /* JMS need better error/warning message here */
    opal_output(0, "keyval parser: error %d reading file %s at line %d:\n  %s\n",
                num, keyval_filename, opal_util_keyval_yynewlines, opal_util_keyval_yytext);
}
