#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <glib.h>
#include "entity.h"

/* The way an raw-io tag can look in the xml. * <raw-io *    name="thisio" *
 *   fd="1" *    oncanread="get_part" *    oncanwrite="can_write" *
 * onerror="got_messed" * /> */

static void
rendio_raw_io_do (ENode * node, int source, char *function)
{
    enode_call_ignore_return (node, function, "");
}

static int
rendio_raw_io_ready_callback (gint source, EIOCond cond, ENode * node)
{
    gchar *oncanread;
    gchar *oncanwrite;
    gchar *onerror;

    EDEBUG (
	    ("raw-io-renderer", "in rendio_raw_io_ready_callback, cond = %i",
	     cond));

    if (cond & EIO_READ) {
	oncanread = enode_attrib_str (node, "oncanread", NULL);
	rendio_raw_io_do (node, source, oncanread);
    }
    if (cond & EIO_WRITE) {
	oncanwrite = enode_attrib_str (node, "oncanwrite", NULL);
	rendio_raw_io_do (node, source, oncanwrite);
    }
    if (cond & EIO_ERROR) {
	onerror = enode_attrib_str (node, "onerror", NULL);
	rendio_raw_io_do (node, source, onerror);
    }

    return FALSE;
}

static EIOCond
rendio_raw_io_get_condition (ENode * node)
{
    EIOCond cond = EIO_READ;
    char *oncanread;
    char *oncanwrite;
    char *onerror;

    oncanread = enode_attrib_str (node, "oncanread", NULL);
    oncanwrite = enode_attrib_str (node, "oncanwrite", NULL);
    onerror = enode_attrib_str (node, "onerror", NULL);

    if (oncanread && strlen (oncanread)) {
	cond |= EIO_READ;
    }

    if (oncanwrite && strlen (oncanwrite)) {
	cond |= EIO_WRITE;
    }

    if (onerror && strlen (onerror)) {
	cond |= EIO_READ;
    }

    EDEBUG (("raw-io-renderer", "cond = %i, read = %i", cond, EIO_READ));
    return cond;
}

static int
rendio_raw_io_fd_attr_set (ENode * node, EBuf * attr, EBuf * value)
{
    EIOCond cond;
    int source;
    gpointer tag;
    gpointer old_tag;

    old_tag = enode_get_kv (node, "rendio-raw-io-tag");

    source = erend_get_integer (value);
    if (source < 0) {
	/* need to delete the old tag and callback connection. */
	EDEBUG (("raw-io-renderer", "going to delete old tag"));
	if (old_tag) {
	    entity_mainloop->io_remove (old_tag);
	}
	return TRUE;		/* Source of <0 means disconnect completely. */
    }

    if (old_tag) {
	/* need to delete the old tag and callback connection. */
	entity_mainloop->io_remove (old_tag);
    }

    cond = rendio_raw_io_get_condition (node);

    /* No blocking io is alowed... */
    fcntl (source, F_SETFL, O_NONBLOCK);

    EDEBUG (("raw-io-renderer", "source = %i, cond = %i", source, cond));

    tag = entity_mainloop->io_add (source, cond,
				   (void *) rendio_raw_io_ready_callback, node);

    enode_set_kv (node, "rendio-raw-io-tag", tag);

    return TRUE;
}

static void
rendio_raw_io_render (ENode * node)
{
    enode_attribs_sync (node);
    enode_set_kv (node, "rendio-raw-io-tag", NULL);
}

static void
rendio_raw_io_destroy (ENode * node)
{
    gpointer tag;

    tag = enode_get_kv (node, "rendio-raw-io-tag");

    if (tag)
	entity_mainloop->io_remove (tag);
}

static void
rendio_raw_io_parent (ENode * parent_node, ENode * child_node)
{
    /* Short curcuit the node so that you can stick the timer node anywhere */
    parent_node->entity_data = child_node->entity_data;
}


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

    element = g_new0 (Element, 1);
    element->render_func = rendio_raw_io_render;
    element->destroy_func = rendio_raw_io_destroy;
    element->parent_func = rendio_raw_io_parent;
    element->tag = "raw-io";
    element->description =
	"Event driven IO watcher.  Feed it a file descriptor, and it'll call you when data arrives, or is ready to be sent.";
    element_register (element);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "fd";
    e_attr->description = "Sepcify the file descripter to watch";
    e_attr->value_desc = "integer";
    e_attr->possible_values = "-1,*";
    e_attr->set_attr_func = rendio_raw_io_fd_attr_set;
    element_register_attrib (element, e_attr);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "oncanread";
    e_attr->description = "Function to call when the fd is ready for reading.";
    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 = "oncanwrite";
    e_attr->description = "Function to call when the fd is ready for writing.";
    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 = "onerror";
    e_attr->description =
	"Function to call when an exception occurs on the fd.";
    e_attr->value_desc = "function";
    e_attr->set_attr_func = NULL;
    element_register_attrib (element, e_attr);

}


syntax highlighted by Code2HTML, v. 0.9.1