/*
    c99tree - input layer, xml parser using libexpat
    Copyright (C) 2010, 2015  Tibor Palinkas

    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, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <expat.h>
#include "tree.h"
#include "tree_load.h"
#include "escaping.h"

#include "scope.h"
#include "c99tree.h"
#include "tree_load_uid.h"

#define BUFFLEN 1024

/* todo: these should not be global */
static node_t *root;
static node_t *last[LOADER_MAXLEVEL];
static uid2node_t *uid2node;

static void xml_start(void *user_data, const XML_Char *type, const XML_Char **atts)
{
	int *level = user_data;
	node_t *n;
	char **a;
	char *name = NULL, *loc = NULL, *twin_parent = NULL, *uid = NULL;

	if (strcmp(type, "FILE") == 0) {
		for(a = atts; *a != NULL; a += 2)
			if (strcmp(a[0], "location_format") == 0) loc_format = unescape_dup_xml(a[1]);
		return;
	}

	for(a = atts; *a != NULL; a += 2) {
		if (strcmp(a[0], "name") == 0) name = a[1];
		else if (strcmp(a[0], "location") == 0) loc = a[1];
		else if (strcmp(a[0], "twin_parent") == 0) twin_parent = a[1];
		else if (strcmp(a[0], "uid") == 0) uid = a[1];
	}
	loc_current = loc_scan(loc == NULL ? "" : loc);


	n = Nalloc(Ntype(type), name == NULL ? NULL : unescape_dup_xml(name), NULL);

	/* bind new node */
	if (*level == 0) {
		if (root == NULL)
			root = n;
		else
			Nappend(root, n);
	}
	else {
		Nchappend(last[*level-1], n);
	}
	last[*level] = n;

	n->uid = -1;

	if (uid != NULL) {
		int i;
		char *end;
		i = strtol(uid, &end, 10);
		if (*end == '\0') {
			n->uid = i;
			uid2node_save(uid2node, n);
		}
	}

	n->loc.twin_parent_uid = -1;
	n->loc.twin_parent = NULL;
	n->loc.is_local = 1;
	if (twin_parent != NULL) {
		int i;
		char *end;
		i = strtol(twin_parent, &end, 10);
		if (*end == '\0')
			n->loc.twin_parent_uid = i;
	}

	(*level)++;
	assert(*level < LOADER_MAXLEVEL);
}

static void xml_end(void *user_data, const XML_Char *name)
{
	int *level = user_data;
	(*level)--;
}


node_t *tree_load_xml(const char *filename)
{
	char buff[BUFFLEN];
	int len;
	void *parser;
	int fd;
	int level;
	char *old_loc_format;

	old_loc_format = loc_format;
	loc_format = NULL;
	root = NULL;
	if (filename != NULL) {
		fd = open(filename, O_RDONLY);
	}
	else {
		filename = "<stdin>";
		fd = 0;
	}

	uid2node = uid2node_alloc();
	parser = XML_ParserCreate(NULL);
	assert(parser != NULL);

	level = 0;
	XML_SetUserData(parser, &level);
  XML_SetElementHandler(parser, xml_start, xml_end);

	for(;;) {
		len = read(0, buff, BUFFLEN);
		if (len <= 0)
			break;
		if (XML_Parse(parser, buff, len, len < BUFFLEN) == XML_STATUS_ERROR) {
			fprintf(stderr, "Error loading xml: %s:%lu\n",
				XML_ErrorString(XML_GetErrorCode(parser)),
				XML_GetCurrentLineNumber(parser));
			abort();
		}
	}
	XML_ParserFree(parser);

	if (fd != 0)
		close(fd);

	if (loc_format != NULL)
		free(loc_format);
	loc_format = old_loc_format;
	loc_current = NULL;

	uid2node_resolve(uid2node, root);
	uid2node_free(uid2node);
	return root;
}

