#ifndef HARE_AST_H
#define HARE_AST_H
#include <stdbool.h>
#include <stdint.h>
#include "expr.h"
#include "identifier.h"
#include "lex.h"
#include "types.h"

struct ast_type;

enum ast_import_mode {
	IMPORT_NORMAL,   // use foo::bar;
	IMPORT_ALIAS,    // use foo = bar::baz;
	IMPORT_MEMBERS,  // use foo::{bar, baz};
	IMPORT_WILDCARD, // use foo::bar::*;
};

struct ast_import_members {
	struct location loc;
	struct ident *name;
	struct ast_import_members *next;
};

struct ast_imports {
	enum ast_import_mode mode;
	struct ident *ident;
	union {
		const char *alias;
		struct ast_import_members *members;
	};
	struct ast_imports *next;
};

struct ast_array_type {
	struct ast_expression *length; // NULL for unbounded arrays
	struct ast_type *members;
	bool contextual;
};

struct ast_slice_type {
	struct ast_type *members;
};

struct ast_enum_field {
	struct location loc;
	struct ident *name;
	struct ast_expression *value;
	struct ast_enum_field *next;
};

struct ast_enum_type {
	enum type_storage storage;
	struct ast_enum_field *values;
};

struct ast_function_parameters {
	struct location loc;
	struct ident *name;
	struct ast_type *type;
	struct ast_expression *default_value;
	struct ast_function_parameters *next;
};

struct ast_function_type {
	struct ast_type *result;
	struct ast_function_parameters *params;
	enum variadism variadism;
};

struct ast_pointer_type {
	struct ast_type *referent;
	bool nullable;
};

struct ast_tagged_union_type {
	struct ast_type *type;
	bool unwrap;
	struct ast_tagged_union_type *next;
};

struct ast_tuple_type {
	struct ast_type *type;
	struct ast_tuple_type *next;
};

struct ast_struct_union_field {
	struct ast_struct_union_field *next;
	const char *name; // null if embed, may be "_"
	struct ast_type *type;
};

struct ast_struct_union_type {
	struct ast_struct_union_field fields;
	bool packed;
};

struct ast_type {
	struct location loc;
	enum type_storage storage;
	union {
		struct ast_array_type array;
		struct ast_type *error;
		struct ast_function_type func;
		struct ast_pointer_type pointer;
		struct ast_slice_type slice;
		struct ast_struct_union_type struct_union;
		struct ast_tagged_union_type tagged;
		struct ast_tuple_type tuple;
		struct {
			struct ident *alias;
			// Only valid for enums
			struct ast_enum_type _enum;
		};
	};
};

struct ast_types {
	const struct ast_type *type;
	struct ast_types *next;
};

struct ast_expression_list {
	struct ast_expression *expr;
	struct ast_expression_list *next;
};

struct ast_expression_access {
	enum access_type type;
	union {
		struct ident *ident;
		struct {
			struct ast_expression *array;
			struct ast_expression *index;
		};
		struct {
			struct ast_expression *_struct;
			const char *field;
		};
		struct {
			struct ast_expression *tuple;
			struct ast_expression *value;
		};
	};
};

struct ast_expression_alloc {
	enum alloc_kind kind;
	struct ast_expression *init;
	struct ast_expression *cap;
};

struct ast_expression_append {
	struct ast_expression *object;
	struct ast_expression *value;
	struct ast_expression *length;
	bool is_static, is_multi;
};

struct ast_expression_assert {
	struct ast_expression *cond;
	struct ast_expression *message;
	bool is_static;
};

struct ast_expression_assign {
	enum binarithm_operator op;
	// object == NULL for discarding assignment (`_ = foo`)
	struct ast_expression *object, *value;
};

struct ast_expression_binarithm {
	enum binarithm_operator op;
	struct ast_expression *lvalue, *rvalue;
};

struct ast_binding_names {
	struct ident *name; // NULL for _
	struct ast_binding_names *next;
};

struct ast_expression_binding {
	// more than one name means tuple unpacking,
	// otherwise it's a regular binding
	struct ast_binding_names names;
	struct ast_type *type;
	bool is_static;
	struct ast_expression *initializer;
	struct ast_expression_binding *next;
};

struct ast_expression_call {
	struct ast_expression *lvalue;
	struct ast_expression_list *args;
	bool variadic; // last argument is a variadic argument list
};

struct ast_expression_cast {
	enum cast_kind kind;
	struct ast_expression *value;
	struct ast_type *type;
};

struct ast_expression_literal {
	enum type_storage storage;
	union {
		int64_t ival;
		uint64_t uval;
		double fval;
		uint32_t rune;
		bool bval;
		struct {
			size_t len;
			char *value;
		} string;
		struct {
			struct ast_expression_list *exprs;
			bool expand;
		} array;
	};
};

struct ast_expression_control {
	const char *label; // Never set for return.
	struct ast_expression *value; // Never set for continue
};

struct ast_expression_defer {
	struct ast_expression *deferred;
};

struct ast_expression_delete {
	struct ast_expression *expr;
	bool is_static;
};

struct ast_expression_for {
	enum for_kind kind;
	const char *label;
	struct ast_expression *bindings;
	struct ast_expression *cond;
	struct ast_expression *afterthought;
	struct ast_expression *body;
	struct ast_expression *else_branch;
};

struct ast_expression_free {
	struct ast_expression *expr;
};

struct ast_expression_if {
	struct ast_expression *cond;
	struct ast_expression *true_branch, *false_branch;
};

struct ast_expression_compound {
	const char *label;
	struct location label_loc;
	struct ast_expression_list list;
};

struct ast_match_case {
	struct ident *name; // May be null
	struct ast_type *type;
	struct ast_expression_list exprs;
	struct ast_match_case *next;
};

struct ast_expression_match {
	const char *label;
	struct ast_expression *value;
	struct ast_match_case *cases;
};

enum measure_operator {
	M_ALIGN,
	M_LEN,
	M_SIZE,
	M_OFFSET,
};

struct ast_expression_measure {
	enum measure_operator op;
	union {
		struct ast_expression *value;
		struct ast_type *type;
		// TODO: Field selection
	};
};

struct ast_expression_propagate {
	struct ast_expression *value;
	bool abort;
};

struct ast_expression_slice {
	struct ast_expression *object;
	struct ast_expression *start, *end;
};

struct ast_case_option {
	struct ast_expression *value;
	struct ast_case_option *next;
};

struct ast_switch_case {
	struct ast_case_option *options; // NULL for *
	struct ast_expression_list exprs;
	struct ast_switch_case *next;
};

struct ast_expression_switch {
	const char *label;
	struct ast_expression *value;
	struct ast_switch_case *cases;
};

struct ast_field_value {
	const char *name;
	struct ast_type *type;
	struct ast_expression *initializer;
	struct ast_field_value *next;
};

struct ast_expression_struct {
	bool autofill;
	bool undefined;
	struct ident *type;
	struct ast_field_value *fields;
};

struct ast_expression_tuple {
	struct ast_expression *expr;
	struct ast_expression_tuple *next;
};

struct ast_expression_unarithm {
	enum unarithm_operator op;
	struct ast_expression *operand;
};

struct ast_expression_vaarg {
	struct ast_expression *ap;
	struct ast_type *type;
};

struct ast_expression {
	struct location loc;
	enum expr_type type;
	union {
		struct ast_expression_access access;
		struct ast_expression_alloc alloc;
		struct ast_expression_append append; // also insert
		struct ast_expression_assert assert;
		struct ast_expression_assign assign;
		struct ast_expression_binarithm binarithm;
		struct ast_expression_binding binding;
		struct ast_expression_call call;
		struct ast_expression_cast cast;
		struct ast_expression_compound compound;
		struct ast_expression_control control;
		struct ast_expression_defer defer;
		struct ast_expression_delete delete;
		struct ast_expression_for _for;
		struct ast_expression_free free;
		struct ast_expression_if _if;
		struct ast_expression_literal literal;
		struct ast_expression_match match;
		struct ast_expression_measure measure;
		struct ast_expression_propagate propagate;
		struct ast_expression_slice slice;
		struct ast_expression_struct _struct;
		struct ast_expression_switch _switch;
		struct ast_expression_tuple tuple;
		struct ast_expression_unarithm unarithm;
		struct ast_expression_vaarg vaarg;
	};
};

struct ast_global_decl {
	const char *symbol;
	bool threadlocal;
	struct ident *ident;
	struct ast_type *type;
	struct ast_expression *init;
};

struct ast_type_decl {
	struct ident *ident;
	struct ast_type *type;
};

enum func_decl_flags {
	FN_FINI = 1 << 0,
	FN_INIT = 1 << 1,
	FN_TEST = 1 << 2,
};

struct ast_function_decl {
	const char *symbol;
	struct ident *ident;
	struct ast_function_type prototype;
	struct ast_expression *body;
	enum func_decl_flags flags;
};

enum ast_decl_type {
	ADECL_FUNC,
	ADECL_TYPE,
	ADECL_GLOBAL,
	ADECL_CONST,
	ADECL_ASSERT,
};

struct ast_decl {
	struct location loc;
	enum ast_decl_type decl_type;
	bool exported;
	union {
		struct ast_global_decl global;
		struct ast_global_decl constant;
		struct ast_type_decl type;
		struct ast_function_decl function;
		struct ast_expression_assert assert;
	};
};

struct ast_decls {
	struct ast_decl decl;
	struct ast_decls *next;
};

struct ast_subunit {
	struct ast_imports *imports;
	struct ast_decls *decls;
	struct ast_subunit *next;
};

struct ast_unit {
	struct ident *ns;
	struct ast_subunit subunits;
};

#endif
