#ifndef HAREC_SCOPE_H
#define HAREC_SCOPE_H
#include "expr.h"
#include "identifier.h"

#define SCOPE_BUCKETS 4096

enum object_type {
	O_BIND,
	O_CONST,
	O_DECL,
	O_SCAN,
	O_TYPE,
};

enum scope_object_flags {
	SO_THREADLOCAL = 1 << 0,
	SO_FOR_EACH_SUBJECT = 1 << 1,
};

struct scope_object {
	enum object_type otype;
	// name is the name of the object within this scope (for lookups)
	// ident is the global identifier (these may be different in some cases)
	struct ident *name;
	struct ident *ident;
	enum scope_object_flags flags;

	union {
		const struct type *type;
		struct expression *value; // For O_CONST
	};
	 // Cannot be in union because type and idecl are needed at the same time
	struct incomplete_decl *idecl;

	struct scope_object *lnext; // Linked list
	struct scope_object *mnext; // Hash map
};

enum scope_class {
	SCOPE_COMPOUND,
	SCOPE_DEFER,
	SCOPE_ENUM,
	SCOPE_FUNC,
	SCOPE_LOOP,
	SCOPE_MATCH,
	SCOPE_SUBUNIT,
	SCOPE_UNIT,
	SCOPE_DEFINES,
};

struct yield { // and break
	struct expression **expression;
	struct yield *next;
};

struct scope {
	enum scope_class class;
	const char *label;
	struct scope *parent;

	const struct type *hint;
	struct type_tagged_union results;
	struct yield *yields;

	// Linked list in insertion order
	// Used for function parameters and enum values, where order matters
	struct scope_object *objects;
	struct scope_object **next;

	// Hash map in reverse insertion order
	// Used for lookups, and accounts for shadowing
	struct scope_object *buckets[SCOPE_BUCKETS];
};

struct scopes {
	struct scope *scope;
	struct scopes *next;
};

struct scope *scope_push(struct scope **stack, enum scope_class class);
struct scope *scope_pop(struct scope **stack);

struct scope *scope_lookup_class(struct scope *scope, enum scope_class class);
struct scope *scope_lookup_label(struct scope *scope, const char *label);

void scope_free(struct scope *scope);
void scope_free_all(struct scopes *scopes);

struct scope_object *scope_insert(struct scope *scope,
	enum object_type otype, struct ident *ident, struct ident *name,
	const struct type *type, struct expression *value);

struct scope_object *scope_lookup(struct scope *scope, struct ident *ident);

#endif
