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

#include "entity.h"


GHashTable *userrend_tag_nodes_ht;

static void
userrend_passthru_parenter (ENode * parent_node, ENode * child_node)
{
    ENode *renderer_node;
    char *onparent;

    renderer_node = g_hash_table_lookup (userrend_tag_nodes_ht,
					 parent_node->element->str);
    if (!renderer_node)
	return;

    onparent = enode_attrib_str (renderer_node, "onparent", NULL);

    /* If they don't implement the parenting, we have to mimic the behavior
     * of the real element parenter */
    if (!onparent) {
	erend_short_curcuit_parent (parent_node, child_node);
	return;
    }

    enode_call_ignore_return (renderer_node, onparent, "nn", parent_node,
			      child_node);
}

static void
userrend_passthru_renderer (ENode * node)
{
    ENode *renderer_node;
    char *onrender;

    renderer_node =
	g_hash_table_lookup (userrend_tag_nodes_ht, node->element->str);
    if (!renderer_node)
	return;

    onrender = enode_attrib_str (renderer_node, "onrender", NULL);
    if (!onrender)
	return;

    EDEBUG (("renderers", "node = %s", node->element->str));

    enode_call_ignore_return (renderer_node, onrender, "n", node);
    /* enode_attribs_sync (node); */
}

static void
userrend_passthru_destroy (ENode * node)
{
    ENode *renderer_node;
    char *ondestroy;

    renderer_node =
	g_hash_table_lookup (userrend_tag_nodes_ht, node->element->str);
    if (!renderer_node)
	return;

    ondestroy = enode_attrib_str (renderer_node, "ondestroy", NULL);
    if (!ondestroy)
	return;

    enode_call_ignore_return (renderer_node, ondestroy, "n", node);
}

static gint
userrend_passthru_attr_set (ENode * node, EBuf * attr, EBuf * value)
{
    ENode *renderer_node;
    ENode *attrib_node;
    GHashTable *attrib_ht;

    char *onset;

    EDEBUG (("renderers", "userrend_passthru_attr_set"));

    renderer_node = g_hash_table_lookup (userrend_tag_nodes_ht,
					 node->element->str);
    if (!renderer_node)
	return FALSE;
    attrib_ht = enode_get_kv (renderer_node, "userrend-renderer-attrib-ht");

    attrib_node = g_hash_table_lookup (attrib_ht, attr->str);
    if (!attrib_node)
	return FALSE;

    onset = enode_attrib_str (attrib_node, "onset", NULL);
    if (!onset)
	return FALSE;

    EDEBUG (("renderers", "node = %s", node->element->str));

    /* Call the onset function. */
    enode_call_ignore_return (renderer_node, onset, "nee", node, attr, value);
    return TRUE;
}

/* TODO: Think about ref'ing these nodes.. */

static void
userrend_renderer_parenter (ENode * parent_node, ENode * child_node)
{
    Element *element;
    ElementAttr *e_attr;

    char *tag;

    char *name;
    char *description;
    char *value_desc;
    char *values;
    ENode *renderer_node;
    GHashTable *attrib_ht;


    EDEBUG (("userrend", "userrend_renderer_parenter"));

    tag = enode_attrib_str (parent_node, "tag", NULL);
    if (!tag)
	return;

    renderer_node = g_hash_table_lookup (userrend_tag_nodes_ht, tag);
    if (!renderer_node)
	return;

    attrib_ht = enode_get_kv (renderer_node, "userrend-renderer-attrib-ht");
    if (!attrib_ht)
	return;

    element = enode_get_kv (parent_node, "userrend-renderer-element");
    if (!element)
	return;

    if (!ebuf_equal_str (child_node->element, "attrib"))
	return;

    name = enode_attrib_str (child_node, "name", NULL);
    if (!name)
	return;

    description = enode_attrib_str (child_node, "description", NULL);
    if (!description)
	g_warning
	    ("Element <%s tag=%s>'s attrib <%s> doesn't have a description.",
	     parent_node->element->str, tag, child_node->element->str);

    value_desc = enode_attrib_str (child_node, "value_desc", NULL);
    values = enode_attrib_str (child_node, "values", NULL);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = name;
    e_attr->description = description;
    e_attr->value_desc = value_desc;
    e_attr->possible_values = values;
    e_attr->set_attr_func = userrend_passthru_attr_set;
    element_register_attrib (element, e_attr);

    g_hash_table_insert (attrib_ht, name, child_node);
}

static void			/* Silly name but thats what this function
				 * is. */
userrend_renderer_renderer (ENode * node)
{
    Element *element;
    GHashTable *attrib_ht;

    char *tag;

    EDEBUG (("userrend", "in userrend_renderer_renderer"));

    tag = enode_attrib_str (node, "tag", NULL);
    if (!tag)
	return;

    EDEBUG (("userrend", "rendering (%s)", tag));

    enode_set_kv (node, "userrend_renderer_renderer-do", "true");

    element = g_new0 (Element, 1);
    element->render_func = userrend_passthru_renderer;
    element->destroy_func = userrend_passthru_destroy;
    element->parent_func = userrend_passthru_parenter;
    element->tag = tag;
    element_register (element);

    enode_set_kv (node, "userrend-renderer-element", element);

    attrib_ht = g_hash_table_new (g_str_hash, g_str_equal);
    enode_set_kv (node, "userrend-renderer-attrib-ht", attrib_ht);

    g_hash_table_insert (userrend_tag_nodes_ht, tag, node);

    /* xml_node_set_all_attr (node); */
}

static void
userrend_attrib_renderer (ENode * node)
{
    enode_set_kv (node, "userrend-attrib-renderer-do", "true");

    /* xml_node_set_all_attr (node); */
}


void
userrend_renderer_register (void)
{
    Element *element;
    ElementAttr *e_attr;

    userrend_tag_nodes_ht = g_hash_table_new (g_str_hash, g_str_equal);

    element = g_new0 (Element, 1);
    element->render_func = userrend_renderer_renderer;
    /* element->destroy_func = userrend_renderer_destroy; */
    element->parent_func = userrend_renderer_parenter;
    element->tag = "renderer";
    element->description = "Define a new element in any supported langauge.";
    element_register (element);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "tag";
    e_attr->description = "The name of the tag for the new renderer.";
    e_attr->value_desc = "string";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "lang";
    e_attr->description = "The default language of the renderer.";
    e_attr->value_desc = "string";
    e_attr->possible_values = "language";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "onrender";
    e_attr->description = "The function or method used to render the tag.";
    e_attr->value_desc = "function";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "ondestroy";
    e_attr->description = "The function or method used to remove the tag.";
    e_attr->value_desc = "function";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "onparent";
    e_attr->description = "The function or method used to parent child tags.";
    e_attr->value_desc = "function";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);


    /* Attrib tag element. */
    element = g_new0 (Element, 1);
    element->render_func = userrend_attrib_renderer;
    /* element->destroy_func = userrend_attrib_destroy; */
    /* element->parent_func = userrend_attrib_parenter; */
    element->tag = "attrib";
    element->description = "Define an attribute of a new renderer.";
    element_register (element);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "name";
    e_attr->description = "The attribute's name.";
    e_attr->value_desc = "string";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "description";
    e_attr->description = "The description of the attribute.";
    e_attr->value_desc = "string";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "value_desc";
    e_attr->description = "The type of value accepted.";
    e_attr->value_desc = "string";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "values";
    e_attr->description = "The possible values for this attribute.";
    e_attr->value_desc = "string";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "onset";
    e_attr->description =
	"The function or method to call when this attribute is set.";
    e_attr->value_desc = "string";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

}


syntax highlighted by Code2HTML, v. 0.9.1