extends Node3D
class_name Building

@export var hologram_material: Material = preload("res://shaders/hologram_material.tres")
@export var hologram_build_min: float = -1
@export var hologram_build_max: float = 1

@export_group("Construction", "build")
@export var build_time: float = 5.0 ## Worker-time taken for 1 citizen to finish the building, in citizen-seconds.
@export var build_materials_needed: Dictionary[Item, int] = {}

@export_group("Defence")
@export var max_hp: int = 100 ## Amount of damage this building can sustain before being destroyed.

@export_group("Stacking")
@export var can_stack: bool = true ## Whether this building can be stacked on top of other buildings.
@export var can_be_stacked_on: bool = true ## Whether this building will accept other buildings being stacked on it.
@export var stack_position: Vector3 = Vector3(0,2,0) ## The local position that stacked buildings will be placed at.

@onready var build_position: Vector3 = global_position

@onready var nav_obstacle: NavObstable = $NavObstacle
@onready var consumer: Consumer = $Consumer
@onready var producer: Producer = $Producer
@onready var collision_shape: CollisionShape3D = $CollisionShape3D

signal functional_changed(functional: bool)

enum BuildState {
	UNPLACED,
	BUILDING,
	READY,
	DESTROYED
}

const PLACEMENT_POSITION_OK: int = 			0b0001
const PLACEMENT_FOUNDATION_REQUIRED: int = 	0b0010
const PLACEMENT_PART_ADDED: int = 			0b0100
const PLACEMENT_COMPLETED: int = 			0b1000

var hp: int = max_hp
var build_progress: float = 0.0:
	set(val):
		build_progress = val
		if build_progress >= build_time:
			build_state = BuildState.READY
		var build_percent: float = build_progress / build_time
		set_visual_build_progress(build_percent)

var stacked_buildings: Array[Building] = []

var build_state: BuildState = BuildState.READY:
	set(state):
		if state != build_state:
			build_state = state
			functional_changed.emit(is_functional())
			
			if build_state == BuildState.BUILDING:
				for item in build_materials_needed.keys():
					for i in range(build_materials_needed.get(item)):
						var task: Task.FetchItemTask = Task.FetchItemTask.new()
						task.building = self
						task.item = item
						CitizenManager._instance.add_task(task)
				check_buildable()
				set_visual_build_progress(0.0)
			elif build_state == BuildState.READY:
				set_visual_build_progress(1.0)
		
var build_materials_used: Dictionary[Item, int] = {}
		
func _ready() -> void:
	if nav_obstacle != null:
		functional_changed.connect(func(enable:bool): nav_obstacle.enabled = enable)
	if consumer != null:
		functional_changed.connect(func(enable:bool): consumer.enabled = enable)
	if producer != null:
		functional_changed.connect(func(enable:bool): producer.enabled = enable)
	if collision_shape != null:
		functional_changed.connect(func(enable:bool): collision_shape.disabled = !enable)
		
func _process(delta: float) -> void:
	if build_state == BuildState.UNPLACED:
		global_position = global_position.lerp(build_position, 0.4)
		
	var debug_text: String = ""
	debug_text += "State: %s\n" % build_state
	if consumer != null:
		var consumer_state: String
		if consumer.enabled:
			consumer_state = "OK"
		else:
			consumer_state = "Off"
		debug_text += "Input: %s \nStorage: %d\n" % [consumer_state, consumer.storage_total]
	if producer != null:
		var producer_state = "OK"
		if !producer.enabled:
			producer_state = "Off"
		elif !producer.can_produce():
			producer_state = "Blocked"
		debug_text += "Output: %s\n" % producer_state
		
	$Label3D.text = debug_text

func is_functional() -> bool:
	return build_state == BuildState.READY

func _start_placement() -> void:
	build_state = BuildState.UNPLACED
	
func _end_placement() -> void:
	build_state = BuildState.BUILDING
	
func _placement_select_building(building: Building, confirmed: bool) -> int:
	while !building.stacked_buildings.is_empty():
		building = building.stacked_buildings[0]
	
	var stack_ok: bool = can_stack and building.can_be_stacked_on
	var ret: int = 0
	
	if stack_ok:
		build_position = building.global_position + building.stack_position
		ret |= PLACEMENT_POSITION_OK
	
	if confirmed and stack_ok:
		global_position = build_position
		building.stacked_buildings.append(self)
		
		ret |= PLACEMENT_COMPLETED
		
	return ret

func _placement_select_position(pos: Vector3, confirmed: bool) -> int:
	var ret: int = 0
	
	ret |= PLACEMENT_POSITION_OK
	build_position = Vector3(roundf(pos.x), ceilf(pos.y-0.01), roundf(pos.z))
	if confirmed:
		build_state = BuildState.BUILDING
		ret |= PLACEMENT_COMPLETED
	
	return ret
	
func check_buildable() -> void:
	if can_build():
		var task: Task.BuildTask = Task.BuildTask.new()
		task.building = self
		CitizenManager._instance.add_task(task)
	
func _add_build_material(item: Item) -> void:
	build_materials_used.set(item, build_materials_used.get(item, 0) + 1)
	check_buildable()
	
func can_build() -> bool:
	for item in build_materials_needed.keys():
		if build_materials_used.get(item,0) < build_materials_needed.get(item,0):
			return false
	return true
	
func set_visual_build_progress(ratio: float) -> void:
	var geometries: Array[Node] = find_children("", "GeometryInstance3D", true)
	for node in geometries:
		var geometry: GeometryInstance3D = node as GeometryInstance3D
		var mesh_instance: MeshInstance3D = node as MeshInstance3D
		if geometry != null:
			if ratio >= 1.0:
				geometry.material_override = null
			else:
				if mesh_instance:
					var mesh_material: StandardMaterial3D = mesh_instance.mesh.surface_get_material(0)
					if mesh_material != null:
						var mesh_colour: Color = mesh_material.albedo_color
						geometry.set_instance_shader_parameter("albedo_color", mesh_colour)
					
				geometry.material_override = hologram_material
				var built_amount: float = remap(ratio, 0, 1, hologram_build_min, hologram_build_max)
				geometry.set_instance_shader_parameter("built_amount", built_amount)
		#var opacity: float = 0.5 + (ratio * 0.5)
		#geometry.transparency = 1.0 - opacity