/*
    c99tree - input layer for line based text formats
    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 "tree.h"
#include "tree_load.h"
#include "c99tree.h"
#include "escaping.h"
#include "tree_load_uid.h"

FILE *open_or_stdin(const char *fn)
{
	if (fn == NULL)
		return stdin;
	return fopen(fn, "r");
}

node_t *tree_load_ind(const char *fn)
{
	char *old_loc_format;
	FILE *f;
	char line[1024];
	char *type, *name, *loc, *uids, *uend;
	int level, uid, twin_uid;
	int lineno;
	node_t *root = NULL;
	node_t *n;
	node_t *last[LOADER_MAXLEVEL];
	uid2node_t *uid2node;

	f = open_or_stdin(fn);
	if (f == NULL)
		return NULL;

	uid2node = uid2node_alloc();

	/* workaround for FILE node */
	lineno = 1;
	old_loc_format = loc_format;
	loc_format = NULL;
	fgets(line, sizeof(line), f);
	if (strncmp(line, "FILE", 4) != 0) {
		fprintf(stderr, "Error loading ind %s:%d: first node is not FILE\n", fn, lineno);
		goto err;
	}
	loc = strrchr(line, '(');
	if (loc == NULL) {
		fprintf(stderr, "Error loading ind %s:%d: FILE node doesn't have location format\n", fn, lineno);
		goto err;
	}
	loc[-1] = '\0';
	loc++;
	loc[strlen(loc)-2] = '\0';
	loc_format = strdup(loc);

	while(!(feof(f))) {
		/* read line */
		lineno++;
		*line = '\0';
		fgets(line, sizeof(line), f);

		if ((*line == '\0') || (*line == '\n'))
			continue;

		/* split in 4 fields and tell level */
		type = line;
		level = -1; /* workaround for FILE node */
		while(*type == ' ') {
			type++;
			level++;
		}
		if (level >= LOADER_MAXLEVEL) {
			fprintf(stderr, "Error loading ind %s:%d: nesting too deep(%d)\n", fn, lineno, level);
			goto err;
		}
		name = strchr(type, ' ');
		if (name == NULL) {
			fprintf(stderr, "Error loading ind %s:%d: no name\n", fn, lineno);
			goto err;
		}
		*name = '\0';
		name++;

		/* get the uid part: the first [ after the first space after the name */
		uids = strchr(name, ' ');
		uids = strchr(uids, '[');
		if (uids == NULL) {
			fprintf(stderr, "Error loading ind %s:%d: no uid\n", fn, lineno);
			goto err;
		}
		uids[-1] = '\0';
		uids++;

		/* get the location part */
		loc = strrchr(uids, '(');
		if (loc == NULL) {
			fprintf(stderr, "Error loading ind %s:%d: wrong or missing location\n", fn, lineno);
			goto err;
		}

		/* cut at space before opening '(', truncate closing ')' */
		loc[-1] = '\0';
		loc++;
		loc[strlen(loc)-2] = '\0';

		/* create node */
		uid = strtol(uids, &uend, 10);
		if (*uend != ':') {
			fprintf(stderr, "Error loading ind %s:%d: uid format (no colon)\n", fn, lineno);
			goto err;
		}
		twin_uid = strtol(uend+1, NULL, 10);

		loc_current = loc_scan(loc);
		if (loc_current == NULL) {
			fprintf(stderr, "Error loading ind %s:%d: bad location '%s'\n", fn, lineno, loc);
			goto err;
		}
		n = Nalloc(Ntype(type), unescape_dup(name), NULL);
		loc_cpy(&(n->loc), loc_current);
		n->uid = uid;
		n->loc.twin_parent_uid = twin_uid;
		uid2node_save(uid2node, n);

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

	free(loc_format);
	loc_format = old_loc_format;
	loc_current = NULL;
	fclose(f);
	uid2node_free(uid2node);
	return root;

err:;
	if (loc_format != NULL)
		free(loc_format);
	loc_format = old_loc_format;
	loc_current = NULL;
	fclose(f);
	uid2node_free(uid2node);
	if (root != NULL)
		Nfree(root);
	return NULL;
}
