#include <stddef.h>
#include <stdlib.h>

#include <posthaste/scope.h>
#include <posthaste/debug.h>
#include <posthaste/utils.h>
#include <posthaste/vec.h>

struct vec scopes = {0};

struct scope *create_scope()
{
	static size_t counter = 0;
	if (vec_uninit(scopes)) {
		scopes = vec_create(sizeof(struct scope *));
	}

	struct scope *scope = calloc(1, sizeof(struct scope));
	if (!scope)
		return NULL;

	scope->visible = vec_create(sizeof(struct ast *));
	scope->id = counter++;

	vect_append(struct scope *, scopes, &scope);
	return scope;
}

void scope_add_scope(struct scope *parent, struct scope *scope)
{
	scope->parent = parent;
	scope->fname = parent->fname;
	scope->buf = parent->buf;
}

void scope_set_file(struct scope *scope, const char *fname, const char *buf)
{
	scope->fname = fname;
	scope->buf = buf;
}

struct ast *scope_find(struct scope *scope, char *id)
{
	/* not particularly fast, but good enough for now */
	foreach_vec(vi, scope->visible) {
		struct ast *n = vect_at(struct ast *, scope->visible, vi);
		if (same_id(n->id, id))
			return n;
	}

	return NULL;
}

struct ast *file_scope_find(struct scope *scope, char *id)
{
	struct ast *exists = scope_find(scope, id);
	if (exists)
		return exists;

	if (scope->parent)
		return file_scope_find(scope->parent, id);

	return NULL;
}

int scope_add_var(struct scope *scope, struct ast *var)
{
	struct ast *exists = scope_find(scope, var_id(var));
	if (exists) {
		semantic_error(scope, var, "var redefined");
		semantic_error(scope, exists, "previously here");
		return -1;
	}

	vect_append(struct ast *, scope->visible, &var);
	return 0;
}

int scope_add_formal(struct scope *scope, struct ast *formal)
{
	struct ast *exists = scope_find(scope, formal_id(formal));
	if (exists) {
		semantic_error(scope, formal, "formal redefined");
		semantic_error(scope, exists, "previously here");
		return -1;
	}

	vect_append(struct ast *, scope->visible, &formal);
	return 0;
}

int scope_add_proc(struct scope *scope, struct ast *proc)
{
	struct ast *exists = scope_find(scope, proc_id(proc));
	if (exists) {
		semantic_error(scope, proc, "proc redefined");
		semantic_error(scope, exists, "previously here");
		return -1;
	}

	vect_append(struct ast *, scope->visible, &proc);
	return 0;
}

int scope_add_func(struct scope *scope, struct ast *func)
{
	struct ast *exists = scope_find(scope, func_id(func));
	if (exists) {
		semantic_error(scope, func, "func redefined");
		semantic_error(scope, exists, "previously here");
		return -1;
	}

	vect_append(struct ast *, scope->visible, &func);
	return 0;
}

static void destroy_scope(struct scope *scope)
{
	vec_destroy(&scope->visible);
	free(scope);
}

void destroy_scopes()
{
	foreach_vec(si, scopes) {
		struct scope *s = vect_at(struct scope *, scopes, si);
		destroy_scope(s);
	}

	vec_destroy(&scopes);
}