package main

import "core:fmt"
import "core:c/libc"

NodeKind :: enum {
	Integer,
	Float,
	Character,
	String,
	Identifier,
	Pointer,
	Array,

	//
	Block,
	BinaryExpression,
	UnaryExpression,
	FieldAccess,
	IndexAccess,
	FunctionCall,
	VariableDeclaration,
	Cast,
	BitwiseCast,
	Ret,

	//
	ExternFunction,
	Function,
	Struct,
	Enum,
	Union,
	Use,

	//
	If,
	For,

	// Done at type checking, should replace all instances of FieldAccess with these
	// The value will be the FULL PATH to the field, only for module access
	StructFieldAccess,
	EnumFieldAccess,
	UnionFieldAccess,
	ModuleFieldAccess, // ModuleField$Field

	// Example for std.println: std$println
}

Node :: struct {
	kind:             NodeKind,
	range:            TextRange,
	children:         [dynamic]^Node,
	value:            TokenValue,
	value_token_kind: TokenKind,
	return_type:      ^Type,
}

node_create_value :: proc(kind: NodeKind, range: TextRange, value: TokenValue) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind  = kind,
		range = range,
		value = value,
	}
	return
}

node_create_pointer :: proc(range: TextRange, value: ^Node) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind = .Pointer,
		range = range,
		children = {value},
	}
	return
}

node_create_array :: proc(range: TextRange, size: u64, value: ^Node) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind = .Array,
		range = range,
		children = {value},
		value = size,
	}
	return
}

node_create_block :: proc(range: TextRange, children: [dynamic]^Node) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind     = .Block,
		range    = range,
		children = children,
	}
	return
}

node_create_binary :: proc(
	kind: TokenKind,
	range: TextRange,
	left: ^Node,
	right: ^Node,
) -> (
	ret: ^Node,
) {
	ret = new(Node)
	ret^ = {
		kind = .BinaryExpression,
		range = range,
		children = {left, right},
		value_token_kind = kind,
	}
	return
}

node_create_unary :: proc(kind: TokenKind, range: TextRange, operand: ^Node) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind = .UnaryExpression,
		range = range,
		children = {operand},
		value_token_kind = kind,
	}
	return
}

node_create_field_access :: proc(range: TextRange, left: ^Node, right: ^Node) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind = .FieldAccess,
		range = range,
		children = {left, right},
	}
	return
}

node_create_index_access :: proc(range: TextRange, left: ^Node, right: ^Node) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind = .IndexAccess,
		range = range,
		children = {left, right},
	}
	return
}

node_create_function_call :: proc(
	range: TextRange,
	name: ^Node,
	args: [dynamic]^Node,
) -> (
	ret: ^Node,
) {
	ret = new(Node)
	ret^ = {
		kind = .FunctionCall,
		range = range,
		children = {name},
	}
	for arg in args {
		append(&ret.children, arg)
	}
	return
}

node_create_extern_function :: proc(
	range: TextRange,
	name: [dynamic]u8,
	return_type: ^Node,
	args: [dynamic]^Node,
) -> (
	ret: ^Node,
) {
	ret = new(Node)
	ret^ = {
		kind = .ExternFunction,
		range = range,
		children = {return_type},
		value = name,
	}
	for arg in args {
		append(&ret.children, arg)
	}
	return
}

node_create_function :: proc(
	range: TextRange,
	name: [dynamic]u8,
	return_type, body: ^Node,
	args: [dynamic]^Node,
) -> (
	ret: ^Node,
) {
	ret = new(Node)
	ret^ = {
		kind = .Function,
		range = range,
		children = {return_type, body},
		value = name,
	}
	for arg in args {
		append(&ret.children, arg)
	}
	return
}

node_print :: proc(node: ^Node, indent := 0) {
	for i in 0 ..< indent {
		fmt.printf("  ")
	}
	if node == nil {
		fmt.println("nil")
		return
	}

	fmt.printf("{}: {} ", node.kind, "TODO")
	data, ok := node.value.([dynamic]u8)
	if ok {
		fmt.printf("\"")
		for i in 0 ..< len(data) {
			fmt.printf("%c", data[i])
		}
		fmt.printf("\" ")
	} else {
		fmt.printf("{} ", node.value)
	}
	if node.value_token_kind != .Invalid {
		fmt.printf("{} ", node.value_token_kind)
	}
	if node.return_type != nil {
		fmt.printf("-> {} ", node.return_type)
	}
	fmt.println("")

	for child in node.children {
		node_print(child, indent + 1)
	}
}

parse_use_path :: proc(path: [dynamic]u8) -> (ret: [dynamic]u8) {
	ret = path
	// Check if the path ends with ".cat", if not, append it.
	len_path := len(path)
	if len(path) < 4 ||
	   path[len_path - 4] != '.' ||
	   path[len_path - 3] != 'c' ||
	   path[len_path - 2] != 'a' ||
	   path[len_path - 1] != 't' {
		append(&ret, '.')
		append(&ret, 'c')
		append(&ret, 'a')
		append(&ret, 't')
	}
	return
}

parse_use_path2 :: proc(path: [dynamic]u8) -> (ret: [dynamic]u8) {
	ret = path
	new_alias := [dynamic]u8{}
	for i in 0 ..< len(path) {
		if path[i] == '.' {
			break
		}
		if path[i] == '/' {
			clear(&new_alias)
			continue
		}
		if libc.isalnum(i32(path[i])) == 0 {
			append(&new_alias, '_')
		} else {
			append(&new_alias, path[i])
		}
	}
	return
}

node_create_use :: proc(range: TextRange, path, alias: [dynamic]u8) -> (ret: ^Node) {
	path_ := path
	// Check if the path ends with ".cat", if not, append it.
	path_ = parse_use_path(path)
	ret = new(Node)
	ret^ = {
		kind  = .Use,
		range = range,
		value = path_,
	}
	if len(alias) != 0 {
		append(&ret.children, node_create_value(.Identifier, range, alias))
	} else {
		// Get the filename, and trucate the extension, then replace any special characters with _
		new_alias := [dynamic]u8{}
		new_alias = parse_use_path2(path_)
		if len(new_alias) == 0 {
			panic("Invalid alias for use statement")
		}
		append(&ret.children, node_create_value(.Identifier, range, new_alias))
	}
	return
}

node_create_if :: proc(range: TextRange, condition, then, else_: ^Node) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind = .If,
		range = range,
		children = {condition, then},
	}
	else_mut := else_
	if else_mut != nil {
		if else_.kind == .If {
			else_mut = node_create_block(else_.range, {else_})
		}
		append(&ret.children, else_mut)
	}
	return
}

node_create_for :: proc(range: TextRange, init, condition, step, body: ^Node) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind = .For,
		range = range,
		children = {init, condition, step, body},
	}
	return
}

node_create_variable :: proc(
	range: TextRange,
	name, type_, value: ^Node,
	is_const: bool,
) -> (
	ret: ^Node,
) {
	ret = new(Node)
	ret^ = {
		kind = .VariableDeclaration,
		range = range,
		children = {name, type_, value},
		value = is_const,
	}
	return
}

node_create_cast :: proc(
	range: TextRange,
	value, type_: ^Node,
) -> (
	ret: ^Node,
) {
	ret = new(Node)
	ret^ = {
		kind = .Cast,
		range = range,
		children = {value, type_},
	}
	return
}

node_create_bitwise_cast :: proc(
	range: TextRange,
	value, type_: ^Node,
) -> (
	ret: ^Node,
) {
	ret = new(Node)
	ret^ = {
		kind = .BitwiseCast,
		range = range,
		children = {value, type_},
	}
	return
}

node_create_ret :: proc(range: TextRange, value: ^Node) -> (ret: ^Node) {
	ret = new(Node)
	ret^ = {
		kind = .Ret,
		range = range,
		children = {value},
	}
	return
}

node_create_struct_enum_or_union :: proc(
	range: TextRange,
	kind: NodeKind,
	name: [dynamic]u8,
	fields: [dynamic]^Node,
) -> (
	ret: ^Node,
) {
	ret = new(Node)
	ret^ = {
		kind  = kind,
		range = range,
		value = name,
	}
	for field in fields {
		append(&ret.children, field)
	}
	return
}
