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

#include "entity.h"

static EBuf *
expand_homedir (EBuf *str)
{
    EBuf *realfile;
    char *p;

    realfile = ebuf_new ();
    /* Expand ~ */
    for (p = str->str; *p; p++) {
	if (*p == '~') {
	    ebuf_append_str (realfile, g_get_home_dir());
	} else {
	    ebuf_append_char (realfile, *p);
	}
    }
    return (realfile);
}

void
rend_configfile_sync (ENode *node)
{
    EBuf *filename;
    EBuf *realfile;
    EBuf *xml;
    ENode *confignode;
    FILE *fp;
    
    if (ebuf_equal_str (enode_type (node), "configfile")) 
	confignode = node;
    else
	confignode = enode_parent (node, "configfile");

    if (!confignode) {
	EDEBUG (("configfilerend", 
		"Umm, we have no configfile node above us ?"));
	return;
    }
    
    filename = enode_attrib (confignode, "file", NULL);
    
    EDEBUG (("configfilerend", "file attrib of node %s.%s is %s", 
	    confignode->element->str,
	    enode_attrib_str (confignode, "name", NULL), filename->str));

    realfile = expand_homedir (filename);
   
    EDEBUG (("configfilerend", "Writing out config file to %s",
	    realfile->str));

    /* We just dump stuff to it regardless of whether it exists
     * or not */
    fp = fopen (realfile->str, "w");
    if (!fp) {
	/* TODO: Think about how to actually do this.. should we signal an
	 * error, or ? */
	g_warning ("Unable to open configuration file '%s' for writing: %s",
		   realfile->str, g_strerror (errno));
	ebuf_free (realfile);
	return;
    }

    xml = enode_get_child_xml (confignode);
    fwrite (xml->str, sizeof (char), xml->len, fp);
    fclose (fp);

    ebuf_free (realfile);
}

static gint
rend_configfile_file_attr_set (ENode * node, EBuf * attr, EBuf * value)
{
    EBuf *realfile;
    
    if (ebuf_not_empty (value)) {
	
	realfile = expand_homedir (value);
	
	if (eutils_file_exists (realfile->str)) {
	    EDEBUG (("configfilerend",
		   "Config file %s exists, reading in..",
		   realfile->str));
	    /* If the file exists, we clean out whatever is currently there,
	     * and load up the config file into its place */
	    enode_destroy_children (node);
	    xml_parse_file (node, realfile->str);
	} else {
	    EDEBUG (("configfilerend", 
		     "Config file %s does not exists, will write on change",
		     realfile->str));
	}

	ebuf_free (realfile);
    }


    /* At this point, the contents under the node, are sync'd with the
     * contents of the config file (if there is one, if not, the contents
     * of the node should be in that file anyway).
     *
     * So, we can safely now setup our callback to start keeping track
     * of config changes and writing them to disk when they happen */
   
    enode_event_watch_child_attrib (node, 
	(EventAttribChange) rend_configfile_sync, NULL);
    /* enode_event_watch_child_data (node,
     *    (EventDataChange) rend_configfile_sync, NULL); */
    /* enode_event_watch_child_delete (node,
     *    (EventDelete) rend_configfile_sync, NULL); */
    enode_event_watch_child_new (node,
	(EventNewChild) rend_configfile_sync, NULL);
    
    return (TRUE);
}

static void
rend_configfile_render (ENode * node)
{
    enode_attribs_sync (node);
}

static void
rend_configfile_parent (ENode * parent_node, ENode * child_node)
{
    /* We have to reparent .. */
    erend_short_curcuit_parent (parent_node, child_node);
}


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

    element = g_new0 (Element, 1);
    element->parent_func = rend_configfile_parent;
    element->render_func = rend_configfile_render;
    element->tag = "configfile";
    element->description =
	"Used to store config file information.  XML in file is replaced under this node, if the file exists.  Any changes to nodes under this are written to that file.";
    element_register (element);

    e_attr = g_new0 (ElementAttr, 1);
    e_attr->attribute = "file";
    e_attr->description = "Path to XML configuration file.";
    e_attr->value_desc = "string";
    e_attr->set_attr_func = rend_configfile_file_attr_set;
    element_register_attrib (element, e_attr);
}




syntax highlighted by Code2HTML, v. 0.9.1