/* 
   elmo - ELectronic Mail Operator

   Copyright (C) 2004 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.  

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

   MODULE_DESCRIPTION
   
*/
/****************************************************************************
 *    IMPLEMENTATION HEADERS
 ****************************************************************************/

#include <sys/types.h>
#include <regex.h>
#include <string.h>

#include "property.h"
#include "str.h"
#include "rstring.h"
#include "hash.h"
#include "ask.h"
#include "xmalloc.h"
#include "error.h"
#include "misc.h"
#include "gettext.h"
#include "eprintf.h"

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

#define MAX_CONDITIONS 10

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


struct condition {
        struct condition *next;
        int               count;
        regex_t           conds[MAX_CONDITIONS];
        char             *value;
};


struct property {
        char               *name;
        rstring_t          *args;
        struct condition   *conditions;
        char             *(*fallback)(const char *);
};

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

static htable_t *properties = NULL;

static struct property  *prop_new = NULL;
static struct condition *cond_new = NULL;

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

static struct property *
lookup_property (const char *name)
{
        str_t   *str;
        char    *seek;
        entry_t *entry = htable_lookup (properties, name);

        if (entry != NULL)
                return (struct property *) entry->content;
        
        seek = strchr (name, '.');
        if (seek == NULL)
                return NULL;

        str = str_create ();
        str_put_string_len (str, name, seek - name);

        entry = htable_lookup (properties, str->str);
        str_destroy (str);

        if (entry == NULL)
                return NULL;

        return (struct property *) entry->content;
}



static int
match_property (regex_t *re, const char *property)
{
        int   ret;
        char *txt = property_get (property);
        char *str;

        if (txt == NULL)
                str = "_";
        else
                str = txt;
        
        ret = regexec (re, str, 0, NULL, 0);
        if (ret && ret != REG_NOMATCH){
                error_regex (ret, re, NULL);
        }
        if (txt)
                xfree (txt);
        return ! ret;
}


static char *
eval_condition (struct condition *cond, rstring_t *args)
{
        int   i;
        char *result;
        
        if (cond == NULL)
                return NULL;

        result = eval_condition (cond->next, args);

        if (result)
                return result;

        for (i = 0; i < cond->count; i++){
                if (! match_property (cond->conds + i, args->array[i]))
                        return NULL;
        }

        return eprintf_rstring (cond->value, args);
}



static char *
eval_property (struct property *prop, const char *name)
{
        char *result = NULL;
        
        if (prop->args)
                result = eval_condition (prop->conditions, prop->args);
        
        if (result)
                return result;
        
        if (prop->fallback)
                return prop->fallback (name);
        
        return NULL;
}


static void
destroy_condition (struct condition *cond)
{
        int i;
        
        if (cond == NULL)
                return;

        destroy_condition (cond->next);

        for (i = 0; i < cond->count; i++)
                regfree (cond->conds + i);

        if (cond->value)
                xfree (cond->value);

        xfree (cond);
}


static void
destroy_property (void *data)
{
        struct property *property = (struct property *) data;

        if (property->name)
                xfree (property->name);

        if (property->args)
                rstring_delete (property->args);

        destroy_condition (property->conditions);

        xfree (property);
}


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

void
property_init (void)
{
        properties = htable_create (5);
}



void
property_free_resources (void)
{
        htable_destroy (properties, destroy_property);
}



void
property_register (const char *name, char *(*fallback)(const char *))
{
        struct property *prop = xmalloc (sizeof (struct property));

        prop->name       = xstrdup (name);
        prop->args       = NULL;
        prop->conditions = NULL;
        prop->fallback   = fallback;

        htable_insert (properties, prop->name, prop);
}



void
property_new (const char *name)
{
        entry_t *entry;

        entry = htable_insert (properties, name, NULL);
        
        if (entry->content == NULL){
                prop_new             = xmalloc (sizeof (struct property));
                prop_new->name       = xstrdup (name);
                prop_new->args       = NULL;
                prop_new->conditions = NULL;
                prop_new->fallback   = NULL;

                entry->content       = prop_new;
        }
        else {
                prop_new = entry->content;
        }
}



void
property_add_arg (const char *arg)
{
        if (prop_new->args == NULL){
                prop_new->args                = rstring_create ();
                prop_new->args->allocated_all = 1;
        }

        if (prop_new->args->count >= MAX_CONDITIONS){
                error_ (0, _("too many conditions for property %s"),
                        prop_new->name);
                return;
        }
        
        rstring_add (prop_new->args, xstrdup (arg));
}



void
property_add_cond_re (const char *cond)
{
        int ret;
        
        if (cond_new == NULL){
                cond_new        = xmalloc (sizeof (struct condition));
                cond_new->next  = prop_new->conditions;
                cond_new->count = 0;
                cond_new->value = NULL;
        }

        if (cond_new->count >= MAX_CONDITIONS){
                error_ (0, _("too many conditions for property %s"),
                        prop_new->name);
                return;
        }

        ret = regcomp (& cond_new->conds[cond_new->count], cond,
                       REG_NOSUB | REG_NEWLINE);

        if (ret){
                error_regex (ret, & cond_new->conds[cond_new->count], cond);
                return;
        }

        cond_new->count++;
}



void
property_add_cond_str (const char *cond)
{
        char *re = misc_re_from_str (cond);

        property_add_cond_re (re);
        xfree (re);
}



void
property_add_value (char *value)
{
        cond_new->value      = value;

        prop_new->conditions = cond_new;

        cond_new             = NULL;
}



char *
property_get (const char *name)
{
        struct property *property;

        property = lookup_property (name);

        if (property == NULL)
                return NULL;

        return eval_property (property, name);
}

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


syntax highlighted by Code2HTML, v. 0.9.1