extends Node
class_name CitizenManager

@export var task_assignment_rate: float = 100

var task_assignment_budget: float = 0

#signal task_queue_updated(task_queue: Array[Task])

var task_queue: Array[Task] = []

const FLOAT64_MAX: float = 2.0**1023 * (2 - 2.0**-52) 

func _process(delta: float) -> void:
	task_assignment_budget += task_assignment_rate * delta
	if task_assignment_budget >= task_assignment_rate:
		task_assignment_budget = task_assignment_rate
	
	var idle_workers: Array[Citizen] = []
	for worker in get_tree().get_nodes_in_group("Citizens"):
		if worker.is_idle():
			idle_workers.append(worker)
			
	#idle_workers.shuffle()
	
	while !idle_workers.is_empty() and !task_queue.is_empty() and task_assignment_budget > 0:
		var task: Task = task_queue.pop_front()
		task_assignment_budget -= 1
		if task.is_ready():
			# TODO: this currently assigns to the first worker found
			var worker: Citizen = idle_workers.pop_front()
			worker.assign_task(task)
		else:
			add_task(task)

func remove_task(task: Task):
	var idx: int = task_queue.find(task)
	if idx >= 0:
		task_queue.remove_at(idx)

func add_task(task: Task):
	var idx: int = task_queue.bsearch_custom(task, sort_tasks, false)
	task_queue.insert(idx, task)
	
func sort_tasks(a: Task,b: Task) -> bool:
	return a.priority > b.priority
	
const TASK_PRIORITY_ANYTIME = 0
const TASK_PRIORITY_BY_WAVE_START = 10
const TASK_PRIORITY_ASAP = 20
	
class Task:
	var priority: int = TASK_PRIORITY_ANYTIME
	func get_task_name() -> String:
		return "Task"
	func is_ready() -> bool:
		return true
	func get_location() -> Vector2:
		return Vector2()
	func execute(worker: Citizen) -> bool:
		return false
	func interrupt(worker: Citizen) -> void:
		pass
	func cancel(worker: Citizen) -> void:
		pass

class BuildTask extends Task:
	var building: Building
	func _init() -> void:
		priority = 1
	func get_task_name() -> String:
		return "Build " + building.name
	func is_ready() -> bool:
		return super() and building.can_build() 
	func get_location() -> Vector2:
		return building.global_position
	func execute(worker: Citizen) -> bool:
		var ok: bool = true
		ok = await worker.go_to_destination(building.position)
		if !ok:
			return false
		building.build()
		return true
		
class FetchItemTask extends Task:
	var building: Building
	var item: Item
	func get_task_name() -> String:
		return "Transfer " + item.name + " to " + building.name
	func execute(worker: Citizen) -> bool:
		# find the item from nearby buildings
		# TODO: also look for items on the floor?
		var storage_building: Machine = null
		var closest: float = FLOAT64_MAX
		
		for machine in worker.get_tree().get_nodes_in_group("Buildings"):
			if machine is Machine:
				if machine.stored_items.has(item):
					var distance_sqr: float = machine.position.distance_squared_to(worker.position)
					if distance_sqr < closest:
						storage_building = machine
						closest = distance_sqr
						
		if storage_building == null: return false
		# go to the storage
		if !await worker.go_to_destination(storage_building.global_position): return false
		# pick up the item
		if !await worker.pickup_item_from_building(item, storage_building): return false
		# carry the item to the construction site
		if !await worker.go_to_destination(building.global_position): return false
		# store the item in the construction site
		if !await worker.store_item_in_contruction_site(building): return false
		return true