@tool
extends MeshInstance3D

@export var backface_culling: bool
@export var tex_div = Vector2(1.0/4.0, 1.0/3.0)

var chunk_size: int
var array_mesh = ArrayMesh.new()
var vertices = PackedVector3Array()
var indices = PackedInt32Array()
var uvs = PackedVector2Array()

var face_count: int

var blocks = []

var first_load = true

const AXIS := [
	Vector3i(1, 0, 0),
	Vector3i(0, 1, 0),
	Vector3i(0, 0, 1),
]

func _ready() -> void:
	pass
	
func _process(_delta: float) -> void:
	pass

func init_blocks(_chunk_size: int, pos: Vector3i):
	chunk_size = _chunk_size
	
	blocks.resize(chunk_size)
	for x in range(chunk_size):
		blocks[x] = []
		for y in range(chunk_size):
			blocks[x].append([])
			for z in range(chunk_size):
				var block_type = get_parent().GetBlock(Vector3i(pos.x + x, pos.y + y, pos.z + z), first_load)
				blocks[x][y].append(block_type)
	first_load = false

func SetBlockAtPosition(pos: Vector3i, block_type: Block.BlockType):
	blocks[pos.x][pos.y][pos.z] = block_type
	
func GetBlockAtPosition(pos: Vector3i):
	return blocks[pos.x][pos.y][pos.z]

func generate_chunk():
	array_mesh = ArrayMesh.new()
	vertices = PackedVector3Array()
	indices = PackedInt32Array()
	uvs = PackedVector2Array()
	face_count = 0
	for x in range(chunk_size):
		for y in range(chunk_size):
			for z in range(chunk_size):
				if blocks[x][y][z] == Block.BlockType.Air:
					pass
				else:
					generate_cube_mesh(Vector3i(x, y, z))
				
	
	if not vertices.size() == 0:
		var array = []
		array.resize(Mesh.ARRAY_MAX)
		array[Mesh.ARRAY_VERTEX] = vertices
		array[Mesh.ARRAY_INDEX] = indices
		array[Mesh.ARRAY_TEX_UV] = uvs
		array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array)
		var trimesh_collision = array_mesh.create_trimesh_shape()
		var collisionShape3D: CollisionShape3D = find_child("StaticBody3D").find_child("CollisionShape3D")
		collisionShape3D.set_shape(trimesh_collision)
	mesh = array_mesh

func add_uvs(x, y):
	uvs.append(Vector2(tex_div.x * x, tex_div.y * y))
	uvs.append(Vector2(tex_div.x * x + tex_div.x, tex_div.y * y))
	uvs.append(Vector2(tex_div.x * x + tex_div.x, tex_div.y * y + tex_div.y))
	uvs.append(Vector2(tex_div.x * x, tex_div.y * y + tex_div.y))

func add_triangles():
	indices.append(face_count * 4 + 0)
	indices.append(face_count * 4 + 1)
	indices.append(face_count * 4 + 2)
	indices.append(face_count * 4 + 0)
	indices.append(face_count * 4 + 2)
	indices.append(face_count * 4 + 3)
	face_count += 1
	
#func generate_smooth_mesh(pos: Vector3i):
	

func generate_cube_mesh(pos: Vector3):
	var is_top_block = false
	
	# face +y
	if is_block_air(pos + Vector3(0, 1, 0)) || not backface_culling:
		vertices.append(pos + Vector3(-0.5, 0.5, -0.5))
		vertices.append(pos + Vector3(0.5, 0.5, -0.5))
		vertices.append(pos + Vector3(0.5, 0.5, 0.5))
		vertices.append(pos + Vector3(-0.5, 0.5, 0.5))
		add_triangles()
		add_uvs(1, 0)
		is_top_block = true

	# face +x
	if is_block_air(pos + Vector3(1, 0, 0)) || not backface_culling:
		vertices.append(pos + Vector3(0.5, 0.5, 0.5))
		vertices.append(pos + Vector3(0.5, 0.5, -0.5))
		vertices.append(pos + Vector3(0.5, -0.5, -0.5))
		vertices.append(pos + Vector3(0.5, -0.5, 0.5))
		add_triangles()
		if is_top_block:
			add_uvs(1, 1)
		else:
			add_uvs(1, 2)
	
	# face +z
	if is_block_air(pos + Vector3(0, 0, 1)) || not backface_culling:
		vertices.append(pos + Vector3(-0.5, 0.5, 0.5))
		vertices.append(pos + Vector3(0.5, 0.5, 0.5))
		vertices.append(pos + Vector3(0.5, -0.5, 0.5))
		vertices.append(pos + Vector3(-0.5, -0.5, 0.5))
		add_triangles()
		if is_top_block:
			add_uvs(0, 1)
		else:
			add_uvs(1, 2)
	
	# face -y
	if is_block_air(pos + Vector3(0, -1, 0)) || not backface_culling:
		vertices.append(pos + Vector3(0.5, -0.5, 0.5))
		vertices.append(pos + Vector3(0.5, -0.5, -0.5))
		vertices.append(pos + Vector3(-0.5, -0.5, -0.5))
		vertices.append(pos + Vector3(-0.5, -0.5, 0.5))
		add_triangles()
		add_uvs(1, 2)
	
	# face -x
	if is_block_air(pos + Vector3(-1, 0, 0)) || not backface_culling:
		vertices.append(pos + Vector3(-0.5, 0.5, -0.5))
		vertices.append(pos + Vector3(-0.5, 0.5, 0.5))
		vertices.append(pos + Vector3(-0.5, -0.5, 0.5))
		vertices.append(pos + Vector3(-0.5, -0.5, -0.5))
		add_triangles()
		if is_top_block:
			add_uvs(2, 1)
		else:
			add_uvs(1, 2)
	
	# face -z
	if is_block_air(pos + Vector3(0, 0, -1)) || not backface_culling:
		vertices.append(pos + Vector3(0.5, 0.5, -0.5))
		vertices.append(pos + Vector3(-0.5, 0.5, -0.5))
		vertices.append(pos + Vector3(-0.5, -0.5, -0.5))
		vertices.append(pos + Vector3(0.5, -0.5, -0.5))
		add_triangles()
		if is_top_block:
			add_uvs(0, 1)
		else:
			add_uvs(1, 2)
	
	
func is_block_air(pos: Vector3):
	# outside of chunk
	if pos.x < 0 or pos.y < 0 or pos.z < 0:
		var block = get_parent().GetBlock(pos + position, first_load)
		return block == Block.BlockType.Air
	elif pos.x >= chunk_size or pos.y >= chunk_size or pos.z >= chunk_size:
		var block = get_parent().GetBlock(pos + position, first_load)
		return block == Block.BlockType.Air
	else:
		return blocks[pos.x][pos.y][pos.z] == Block.BlockType.Air
