/* eurephia_values.c  --  Generic interface for processing key->value pairs
 *
 *  GPLv2 only - Copyright (C) 2008 - 2012
 *               David Sommerseth <dazo@users.sourceforge.net>
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; version 2
 *  of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/**
 * @file   eurephia_values.c
 * @author David Sommerseth <dazo@users.sourceforge.net>
 * @date   2008-08-06
 *
 * @brief  Generic interface for handling key->value pairs
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <eurephia_nullsafe.h>
#include <eurephia_context.h>
#include <eurephia_log.h>


/**
 * Clears the values of a eurephiaVALUES key/value record.
 * This function does not remove the record from the stack, but
 * only frees the key and value pointers and sets them to NULL.
 *
 * @param vls Pointer to the eurephiaVALUES record to be cleared
 */
void eClear_key_value(eurephiaVALUES *vls) {
        if( vls != NULL ) {
                free_nullsafe(NULL, vls->key);
                free_nullsafe(NULL, vls->val);
        }
}

/**
 * Internal function. Makes sure a eurephiaVALUES stack is freed up
 *
 * @param vls Pointer to a eurephiaVALUES stack.
 */
void do_free_vals(eurephiaVALUES *vls) {
        if( vls->next != NULL ) {
                do_free_vals(vls->next);
        }
        eClear_key_value(vls);
        free_nullsafe(NULL, vls);
}


/**
 * Function for freeing up an eurephiaVALUES stack.  This function is normally not called
 * directly, but usually via the eFree_values(...) macro.
 *
 * @param ctx  eurephiaCTX
 * @param vls  Pointer to a eurephiaVALUES stack to be freed.
 */
void eFree_values_func(eurephiaCTX *ctx, eurephiaVALUES *vls) {
        DEBUG(ctx, 31, "Function call: eFree_values(ctx, vls(%i))",
              (vls != NULL ? vls->evid : -1));

        if( (vls == NULL) ) {
                return;
        }
        do_free_vals(vls);
}


/**
 * Retrieve an eurephiaVALUES element for a given value key
 *
 * @param vls  Pointer to the eurephiaVALUES stack where to search for the element
 * @param key  String containing the key name of the value requested.
 *
 * @return Returns an eurephiaVALUES element on success, otherwise NULL.
 */
eurephiaVALUES *eGet_valuestruct(eurephiaVALUES *vls, const char *key)
{
        eurephiaVALUES *ptr = NULL;

        if( (vls == NULL) || (key == NULL) ) {
                return NULL;
        }

        ptr = vls;
        while( ptr != NULL ) {
                if( (ptr->key != NULL) && (strcmp(key, ptr->key) == 0) ) {
                        return ptr;
                }
                ptr = ptr->next;
        }
        return NULL;
}


/**
 * Retrieves the value of a given key from an eurephiaVALUES stack.
 *
 * @param vls  Pointer to an eurephiaVALUES stack where to search for the value
 * @param key  String containing the key name of the value requested
 *
 * @return Returns a string (char *) with the requested value if found, otherwise NULL.
 */
char *eGet_value(eurephiaVALUES *vls, const char *key)
{
        eurephiaVALUES *ptr = NULL;

        ptr = eGet_valuestruct(vls, key);
        return (ptr != NULL ? ptr->val : NULL);
}


/**
 * Creates a new eurephiaVALUES stack
 *
 * @param ctx    eurephiaCTX
 * @param evgid  int value, giving the stack an ID number.  Useful when looking through log files later on.
 *
 * @return Returns an empty eurephiaVALUES struct on success, otherwise NULL.
 */
eurephiaVALUES *eCreate_value_space(eurephiaCTX *ctx, int evgid)
{
        eurephiaVALUES *ptr = NULL;

        DEBUG(ctx, 32, "Function call: eCreate_value_space(ctx, %i)", evgid);

        ptr = (eurephiaVALUES *) malloc_nullsafe(ctx, sizeof(eurephiaVALUES) + 2);
        if( ptr == NULL ) {
                return NULL;
        }
        ptr->evgid = evgid;
        return ptr;
}


/**
 * Adds a new eurephiaVALUES stack to another eurephiaVALUES stack.  If the evgid value differs, it will
 * be overwritten with the value of the destination stack.
 *
 * @param ctx    eurephiaCTX
 * @param vls    Destination eurephiaVALUES stack
 * @param newval Source eurephiaVALUES stack
 */
void eAdd_valuestruct(eurephiaCTX *ctx, eurephiaVALUES *vls, eurephiaVALUES *newval) {
        eurephiaVALUES *ptr = NULL;
        int vid = 0;

        DEBUG(ctx, 31, "Function call: eAdd_valuestruct(ctx, vls(%i), '%s', '%s')",
              (vls != NULL ? vls->evgid : -1), newval->key, newval->val);

        assert(vls != NULL);

        if( (vls->key == NULL) && (vls->val == NULL) && (vls->next == NULL) && (vls->evid == 0)) {
                // Update header record if it is empty, by copying newval record.  Free newval afterwards
                vls->key  = strdup_nullsafe(newval->key);
                vls->val  = strdup_nullsafe(newval->val);
                vls->evid = 0;
                vls->next = NULL;
                do_free_vals(newval);
        } else {
                // Add values to the value chain, loop to the end and append it
                ptr = vls;
                while( ptr->next != NULL ) {
                        ptr = ptr->next;
                        vid = (vid > ptr->evid ? vid : ptr->evid);
                }
                newval->evid = vid+1;     // Increase the value counter
                newval->evgid = ptr->evgid;
                ptr->next = newval;
        }
}


/**
 * Adds a new key/value pair to an eurephiaVALUES stack
 *
 * @param ctx  eurephiaCTX
 * @param vls  Destination eurephiaVALUES stack
 * @param key  Key name for the value being stored
 * @param val  Value to be stored
 */
void eAdd_value(eurephiaCTX *ctx, eurephiaVALUES *vls, const char *key, const char *val)
{
        eurephiaVALUES *ptr = NULL;

        DEBUG(ctx, 31, "Function call: eAdd_value(ctx, vls(%i), '%s', '%s')",
              (vls != NULL ? vls->evgid : -1), key, val);

        assert(vls != NULL);

        // Allocate buffer and save values
        ptr = eCreate_value_space(ctx, vls->evid);
        if( ptr == NULL ) {
                eurephia_log(ctx, LOG_FATAL, 0, "Could not add the new value");
                return;
        }
        ptr->key = strdup_nullsafe(key);
        ptr->val = strdup_nullsafe(val);
        ptr->evgid = vls->evgid;

        // Add value struct to the chain
        eAdd_valuestruct(ctx, vls, ptr);
}

/**
 * Removes the key/value pair identified by evgid and evid from the given eurephiaVALUES chain
 *
 * @param ctx    eurephiaCTX
 * @param vls    Pointer to an eurephiaVALUES chain with the data
 * @param evgid  Group ID of the chain
 * @param evid   Element ID of the chain element to be removed
 *
 * @return Returns a pointer to the chain.  The pointer is only changed if the first element in the
 *         chain is deleted
 */
eurephiaVALUES *eRemove_value(eurephiaCTX *ctx, eurephiaVALUES *vls, unsigned int evgid, unsigned int evid) {
        eurephiaVALUES *ptr = NULL, *prev_ptr = NULL;
        int found = 0;

        DEBUG(ctx, 31, "Function call: eRemove_valuestruct(ctx, vls(%i), %i, %i)",
              (vls != NULL ? vls->evgid : -1), evgid, evid);

        // Find the value element
        for( ptr = vls; ptr != NULL; ptr = ptr->next ) {
                if( (ptr->evgid == evgid) && (ptr->evid == evid) ) {
                        found = 1;
                        break;
                }
                prev_ptr = ptr;
        }

        if( !found ) {
                return vls;
        }

        if( ptr != vls ) {
                prev_ptr->next = ptr->next;
                ptr->next = NULL;
                eFree_values_func(ctx, ptr);
                return vls;
        } else {
                prev_ptr = ptr->next;
                ptr->next = NULL;
                eFree_values_func(ctx, ptr);
                return prev_ptr;
        }
}
