/********************************************************************\
 * qofclass.c -- provide QOF parameterized data objects             *
 * Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>                *
 *                                                                  *
 * 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; either version 2 of   *
 * the License, or (at your option) any later version.              *
 *                                                                  *
 * 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, contact:                        *
 *                                                                  *
 * Free Software Foundation           Voice:  +1-617-542-5942       *
 * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
 * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
 *                                                                  *
\********************************************************************/

extern "C"
{
#include <config.h>
#include <glib.h>
}

#include "qof.h"
#include "qofclass-p.h"

static QofLogModule log_module = QOF_MOD_CLASS;

static GHashTable *classTable = NULL;
static GHashTable *sortTable = NULL;
static gboolean initialized = FALSE;

static gboolean clear_table (gpointer key, gpointer value, gpointer user_data)
{
    g_hash_table_destroy (static_cast<GHashTable*>(value));
    return TRUE;
}

/* *******************************************************************/
/* PRIVATE FUNCTIONS */

static gboolean check_init (void)
{
    if (initialized) return TRUE;

    PERR("You must call qof_class_init() before using qof_class.");
    return FALSE;
}

void
qof_class_init(void)
{
    if (initialized) return;
    initialized = TRUE;

    classTable = g_hash_table_new (g_str_hash, g_str_equal);
    sortTable = g_hash_table_new (g_str_hash, g_str_equal);
}

void
qof_class_shutdown (void)
{
    if (!initialized) return;
    initialized = FALSE;

    g_hash_table_foreach_remove (classTable, clear_table, NULL);
    g_hash_table_destroy (classTable);
    g_hash_table_destroy (sortTable);
}

QofSortFunc
qof_class_get_default_sort (QofIdTypeConst obj_name)
{
    if (!obj_name) return NULL;
    return reinterpret_cast<QofSortFunc>(g_hash_table_lookup (sortTable,
							      obj_name));
}

/* *******************************************************************/
/* PUBLISHED API FUNCTIONS */

void
qof_class_register (QofIdTypeConst obj_name,
                    QofSortFunc default_sort_function,
                    const QofParam *params)
{
    GHashTable *ht;
    int i;

    if (!obj_name) return;
    if (!check_init()) return;

    if (default_sort_function)
    {
        g_hash_table_insert (sortTable, (char *)obj_name,
			     reinterpret_cast<void*>(default_sort_function));
    }

    ht = static_cast<GHashTable*>(g_hash_table_lookup (classTable, obj_name));

    /* If it doesn't already exist, create a new table for this object */
    if (!ht)
    {
        ht = g_hash_table_new (g_str_hash, g_str_equal);
        g_hash_table_insert (classTable, (char *)obj_name, ht);
    }

    /* At least right now, we allow dummy, parameterless objects,
     * for testing purposes.  Although I suppose that should be
     * an error..  */
    /* Now insert all the parameters */
    if (params)
    {
        for (i = 0; params[i].param_name; i++)
            g_hash_table_insert (ht,
                                 (char *)params[i].param_name,
                                 (gpointer)&(params[i]));
    }
}

gboolean
qof_class_is_registered (QofIdTypeConst obj_name)
{
    if (!obj_name) return FALSE;
    if (!check_init()) return FALSE;

    if (g_hash_table_lookup (classTable, obj_name)) return TRUE;

    return FALSE;
}

const QofParam *
qof_class_get_parameter (QofIdTypeConst obj_name,
                         const char *parameter)
{
    GHashTable *ht;

    g_return_val_if_fail (obj_name, NULL);
    g_return_val_if_fail (parameter, NULL);
    if (!check_init()) return NULL;

    ht = static_cast<GHashTable*>(g_hash_table_lookup (classTable, obj_name));
    if (!ht)
    {
        PWARN ("no object of type %s", obj_name);
        return NULL;
    }

    return static_cast<QofParam*>(g_hash_table_lookup (ht, parameter));
}

QofAccessFunc
qof_class_get_parameter_getter (QofIdTypeConst obj_name,
                                const char *parameter)
{
    const QofParam *prm;

    g_return_val_if_fail (obj_name, NULL);
    g_return_val_if_fail (parameter, NULL);

    prm = qof_class_get_parameter (obj_name, parameter);
    if (prm)
        return prm->param_getfcn;

    return NULL;
}

QofSetterFunc
qof_class_get_parameter_setter (QofIdTypeConst obj_name,
                                const char *parameter)
{
    const QofParam *prm;

    g_return_val_if_fail (obj_name, NULL);
    g_return_val_if_fail (parameter, NULL);

    prm = qof_class_get_parameter (obj_name, parameter);
    if (prm)
        return prm->param_setfcn;

    return NULL;
}

QofType
qof_class_get_parameter_type (QofIdTypeConst obj_name,
                              const char *param_name)
{
    const QofParam *prm;

    if (!obj_name || !param_name) return NULL;

    prm = qof_class_get_parameter (obj_name, param_name);
    if (!prm) return NULL;

    return (prm->param_type);
}

/* ================================================================ */

struct class_iterate
{
    QofClassForeachCB   fcn;
    gpointer            data;
};

static void
class_foreach_cb (gpointer key, gpointer item, gpointer arg)
{
    struct class_iterate *iter = static_cast<class_iterate*>(arg);
    QofIdTypeConst id = static_cast<QofIdTypeConst>(key);

    iter->fcn (id, iter->data);
}

void
qof_class_foreach (QofClassForeachCB cb, gpointer user_data)
{
    struct class_iterate iter;

    if (!cb) return;
    if (!classTable) return;

    iter.fcn = cb;
    iter.data = user_data;

    g_hash_table_foreach (classTable, class_foreach_cb, &iter);
}

/* ================================================================ */

struct parm_iterate
{
    QofParamForeachCB   fcn;
    gpointer            data;
};

static void
param_foreach_cb (gpointer key, gpointer item, gpointer arg)
{
    struct parm_iterate *iter = static_cast<parm_iterate*>(arg);
    QofParam *parm = static_cast<QofParam*>(item);

    iter->fcn (parm, iter->data);
}

void
qof_class_param_foreach (QofIdTypeConst obj_name,
                         QofParamForeachCB cb, gpointer user_data)
{
    struct parm_iterate iter;
    GHashTable *param_ht;

    if (!obj_name || !cb) return;
    if (!classTable) return;
    param_ht = static_cast<GHashTable*>(g_hash_table_lookup (classTable, obj_name));
    if (!param_ht) return;

    iter.fcn = cb;
    iter.data = user_data;

    g_hash_table_foreach (param_ht, param_foreach_cb, &iter);
}

struct param_ref_list
{
    GList *list;
};

static void
find_reference_param_cb(QofParam *param, gpointer user_data)
{
    struct param_ref_list *b;

    b = (struct param_ref_list*)user_data;
    if ((param->param_getfcn == NULL) || (param->param_setfcn == NULL))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_STRING))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_NUMERIC))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_DATE))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_CHAR))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_DEBCRED))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_GUID))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_INT32))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_INT64))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_DOUBLE))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_KVP))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_TYPE_BOOLEAN))
    {
        return;
    }
    if (0 == g_strcmp0(param->param_type, QOF_ID_BOOK))
    {
        return;
    }
    b->list = g_list_append(b->list, param);
}

GList*
qof_class_get_referenceList(QofIdTypeConst type)
{
    GList *ref_list;
    struct param_ref_list b;

    ref_list = NULL;
    b.list = NULL;
    qof_class_param_foreach(type, find_reference_param_cb, &b);
    ref_list = g_list_copy(b.list);
    return ref_list;
}


/* ============================= END OF FILE ======================== */
