/* 
   elmo - ELectronic Mail Operator

   Copyright (C) 2003 rzyjontko

   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.

   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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  

   ----------------------------------------------------------------------

   this module defines operations on current configuration
   
*/
/****************************************************************************
 *    IMPLEMENTATION HEADERS
 ****************************************************************************/

#include <stdarg.h>
#include <string.h>
#include <stdlib.h>

#include "confhold.h"
#include "confread.h"
#include "hash.h"
#include "rstring.h"
#include "memblock.h"
#include "xmalloc.h"
#include "file.h"

/****************************************************************************
 *    IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
 ****************************************************************************/

#define CONF_SIZE 3

/****************************************************************************
 *    IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
 ****************************************************************************/

/**
 * this is how I store information about available options
 * example:
 *    name   - pop_acc
 *    fields - server, username, password, port
 * fields == NULL iff variable is simple (no fields)
 */
typedef struct citem {
        char      *name;
        rstring_t *fields;
} citem_t;

/**
 * this is how I store values for options
 * the hashing table stores a list of values associated with an option
 *
 * m_value is not a usual rstring.  It may have some NULLs inside.
 */
typedef struct vitem {
        char         *s_value;
        rstring_t    *m_value;
        struct vitem *next;
} vitem_t;


typedef struct conf {
        int      unused;
        citem_t *citem;
        vitem_t *vitem;
        vitem_t *selected;
} conf_t;

/****************************************************************************
 *    IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE DATA
 ****************************************************************************/

static conf_t conf_table[CONF_SIZE];

static htable_t   *variable_table = NULL;
static htable_t   *values_table   = NULL;

static memblock_t *fields_block   = NULL;
static memblock_t *values_block   = NULL;

static citem_t *prepare_citem = NULL;
static vitem_t *prepare_vitem = NULL;

/****************************************************************************
 *    INTERFACE DATA
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTIONS
 ****************************************************************************/

static int
field_index (citem_t *item, const char *field)
{
        int i;
  
        for (i = 0; i < item->fields->count; i++){
                if (strcmp (field, item->fields->array[i]) == 0)
                        break;
        }

        return i;
}


static void
destroy_val (void *ptr)
{
        vitem_t *item = (vitem_t *) ptr;

        for (item = (vitem_t *) ptr; item; item = item->next)
                if (item->m_value)
                        rstring_delete (item->m_value);
}


static void
destroy_def (void *ptr)
{
        citem_t *item = (citem_t *) ptr;

        if (item->fields)
                rstring_delete (item->fields);
}


/****************************************************************************
 *    INTERFACE FUNCTIONS
 ****************************************************************************/

void
confhold_init (void)
{
        int i;
  
        if (variable_table){
                htable_destroy (variable_table, NULL);
        }
        if (values_table){
                htable_destroy (values_table, NULL);
        }
        if (fields_block){
                memblock_destroy (fields_block);
        }
        if (values_block){
                memblock_destroy (values_block);
        }

        variable_table = htable_create (5);
        values_table   = htable_create (6);
        fields_block   = memblock_create (100);
        values_block   = memblock_create (200);

        for (i = 0; i < CONF_SIZE; i++){
                conf_table[i].unused = 1;
        }
}



void
confhold_free_resources (void)
{
        if (variable_table){
                htable_destroy (variable_table, destroy_def);
                variable_table = NULL;
        }

        if (values_table){
                htable_destroy (values_table, destroy_val);
                values_table = NULL;
        }

        if (fields_block){
                memblock_destroy (fields_block);
                fields_block = NULL;
        }

        if (values_block){
                memblock_destroy (values_block);
                values_block = NULL;
        }
}



void
confhold_register (const char *name, int fieldcount, ...)
{
        citem_t *item;
        char    *field;
        va_list  ap;

        item       = memblock_malloc (&fields_block, sizeof (citem_t));
        item->name = memblock_strdup (&fields_block, name);
        if (fieldcount){
                item->fields = rstring_create_size (fieldcount + 1);
        }
        else {
                item->fields = NULL;
        }

        va_start (ap, fieldcount);
        while (fieldcount--){
                field = memblock_strdup (&fields_block, va_arg (ap, char *));
                rstring_add (item->fields, field);
        }
        va_end (ap);

        htable_insert (variable_table, name, item);
}



int
confhold_open (const char *name)
{
        int i;
        entry_t *c_entry;
        entry_t *v_entry;

        for (i = 0; i < CONF_SIZE; i++){
                if (conf_table[i].unused)
                        break;
        }

        if (i == CONF_SIZE)
                return -1;

        c_entry = htable_lookup (variable_table, name);
        if (c_entry == NULL || c_entry->content == NULL)
                return -1;

        v_entry = htable_lookup (values_table, name);

        if (v_entry == NULL || v_entry->content == NULL)
                return -1;

        conf_table[i].citem  = (citem_t *) c_entry->content;
        conf_table[i].vitem  = (vitem_t *) v_entry->content;
        conf_table[i].unused = 0;

        return i;
}



void
confhold_close (int cd)
{
        conf_table[cd].unused = 1;
}



rstring_t *
confhold_get_fields (int cd, const char *field)
{
        int        index;
        vitem_t   *vitem;
        rstring_t *result;

        if (conf_table[cd].unused)
                return NULL;
  
        index  = field_index (conf_table[cd].citem, field);
        result = rstring_create_size (2);
  
        for (vitem = conf_table[cd].vitem; vitem; vitem = vitem->next){
                if (vitem->m_value && vitem->m_value->array[index])
                        rstring_add (result, vitem->m_value->array[index]);
        }
        return result;
}



void
confhold_select (int cd, int index)
{
        if (conf_table[cd].unused)
                return;
  
        for (conf_table[cd].selected = conf_table[cd].vitem;
             index && conf_table[cd].selected; index--){
                conf_table[cd].selected = conf_table[cd].selected->next;
        }
}



void
confhold_select_where (int cd, const char *field, const char *desired)
{
        int      index;
        vitem_t *old_selected;

        if (conf_table[cd].unused)
                return;

        old_selected = conf_table[cd].selected;

        index = field_index (conf_table[cd].citem, field);
        for (conf_table[cd].selected = conf_table[cd].vitem;
             conf_table[cd].selected;
             conf_table[cd].selected = conf_table[cd].selected->next){
                if (! strcmp (conf_table[cd].selected->m_value->array[index],
                              desired))
                        break;
        }

        if (conf_table[cd].selected == NULL)
                conf_table[cd].selected = old_selected;
}



char *
confhold_field_from_selected (int cd, const char *field)
{
        int index;
  
        if (conf_table[cd].unused)
                return NULL;

        if (conf_table[cd].vitem == NULL || conf_table[cd].citem == NULL)
                return NULL;

        if (conf_table[cd].selected == NULL)
                return NULL;

        index = field_index (conf_table[cd].citem, field);
        return conf_table[cd].selected->m_value->array[index];
}



char *
confhold_get_first (const char *name, const char *field)
{
        int      i;
        entry_t *d_entry = htable_lookup (variable_table, name);
        entry_t *v_entry;
        citem_t *def;
        vitem_t *val;

        if (d_entry == NULL && field)
                return NULL;

        v_entry = htable_lookup (values_table, name);

        if (v_entry == NULL)
                return NULL;

        def = (d_entry) ? (citem_t *) d_entry->content : NULL;
        val = (vitem_t *) v_entry->content;
  
        if (field == NULL)
                return val->s_value;

        if (def == NULL)
                return NULL;

        i = field_index (def, field);
        return val->m_value->array[i];
}



rstring_t *
confhold_get_all (const char *name)
{
        entry_t   *v_entry;
        vitem_t   *vitem;
        rstring_t *result;

        v_entry = htable_lookup (values_table, name);

        if (v_entry == NULL || v_entry->content == NULL)
                return NULL;

        result = rstring_create_size (2);

        for (vitem = (vitem_t *) v_entry->content;
             vitem;
             vitem = vitem->next){
                if (vitem->s_value)
                        rstring_add (result, vitem->s_value);
        }
        return result;
}



int
confhold_prepare_to_insert (const char *name)
{
        entry_t *c_entry;
        entry_t *v_entry;
  
        c_entry = htable_lookup (variable_table, name);

        if (c_entry == NULL){
                prepare_citem = NULL;
                return 1;
        }
        else
                prepare_citem = c_entry->content;

        v_entry = htable_insert (values_table, name, NULL);

        prepare_vitem = memblock_malloc (&values_block, sizeof (vitem_t));
        prepare_vitem->next    = (vitem_t *) v_entry->content;
        prepare_vitem->s_value = NULL;
        prepare_vitem->m_value = NULL;

        if (prepare_citem && prepare_citem->fields)
                prepare_vitem->m_value =
                        rstring_create_size (prepare_citem->fields->count + 1);

        v_entry->content = prepare_vitem;
  
        return 0;
}



int
confhold_insert_value (const char *field, const char *val)
{
        int index;
  
        if (prepare_vitem == NULL)
                return 0;

        if (prepare_citem && prepare_citem->fields && field){
                index = field_index (prepare_citem, field);
                if (index == prepare_citem->fields->count)
                        return 1;
                prepare_vitem->m_value->array[index] =
                        memblock_strdup (&values_block, val);
        }
        else if ((prepare_citem == NULL || prepare_citem->fields == NULL)
                 && field == NULL){
                prepare_vitem->s_value = memblock_strdup (&values_block, val);
        }
        return 0;
}

/****************************************************************************
 *    INTERFACE CLASS BODIES
 ****************************************************************************/
/****************************************************************************
 *
 *    END MODULE confhold.c
 *
 ****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1