class_name CombatMap
extends Node2D

@export var tile_set: DLTileset
@export var map_layout: MapLayout
@onready var tile_map: TileMapLayer = %TerrainLayer
@onready var highlight_map: GridOverlay = %OverlayLayer
@onready var wall_renderer: WallRenderer = %WallRenderer
@onready var fog_renderer: FogRenderer = %FogRenderer

const DEPLOYED_UNIT_SCENE = preload("res://prefabs/deployed_unit.tscn")
const SOURCE_ID: int = 0

var _pending_layout: String
var _pending_units: Array[Dictionary] = []


func _ready() -> void:
	if _pending_layout:
		_apply_layout(_pending_layout)
	for entry in _pending_units:
		_apply_deploy(entry.deployed, entry.coords)
	_pending_units.clear()
	if map_layout:
		apply_layout(map_layout)


func draw_wall(coords: Vector2i) -> void:
	draw_custom(coords, tile_set.wall_tile_coords)

func draw_floor(coords: Vector2i) -> void:
	draw_custom(coords, tile_set.floor_tile_coords)
	
func draw_custom(coords: Vector2i, tile_coords: Vector2i) -> void:
	tile_map.set_cell(coords, SOURCE_ID, tile_coords)

func load_map(layout: String) -> void:
	if is_node_ready():
		_apply_layout(layout)
	else:
		_pending_layout = layout


func deploy_unit(unit: Unit, coords: Vector2i) -> void:
	var deployed: DeployedUnit = DEPLOYED_UNIT_SCENE.instantiate()
	deployed.unit = unit
	if is_node_ready():
		_apply_deploy(deployed, coords)
	else:
		_pending_units.append({deployed = deployed, coords = coords})


func _apply_layout(layout: String) -> void:
	var rows := layout.split("\n")
	for y in rows.size():
		for x in rows[y].length():
			var coords := Vector2i(x, y)
			match rows[y][x]:
				"#":
					draw_wall(coords)
				".":
					draw_floor(coords)


func _apply_deploy(deployed: DeployedUnit, coords: Vector2i) -> void:
	deployed.position = BattleMapHelper.coords_to_world(coords)
	add_child(deployed)


func remove_unit(deployed: DeployedUnit) -> void:
	if deployed.get_parent() == self:
		remove_child(deployed)


func target_tile(coords: Vector2i) -> void:
	highlight_map.target_tile(coords)


func apply_layout(layout: MapLayout) -> void:
	map_layout = layout
	map_layout.initialize()
	load_from_layout()
	draw_room_walls()
	draw_fog()


func is_tile_passable(from: Vector2i, to: Vector2i) -> bool:
	assert(map_layout != null, "CombatMap.is_tile_passable called before map_layout was set")
	return map_layout.is_passable(from, to)


func is_tile_valid(coords: Vector2i) -> bool:
	assert(map_layout != null, "CombatMap.is_tile_valid called before map_layout was set")
	return map_layout.is_tile_valid(coords)


func draw_room_walls() -> void:
	if not map_layout:
		return
	wall_renderer.draw_walls_for_layout(map_layout)


func draw_fog() -> void:
	if not map_layout:
		return
	fog_renderer.draw_fog_for_layout(map_layout)


func get_map_rect() -> Rect2:
	if not map_layout:
		return Rect2()
	return Rect2(Vector2.ZERO, Vector2(map_layout.size) * BattleMapHelper.TILE_SIZE)


func load_from_layout() -> void:
	if not map_layout:
		return
	for room in map_layout.rooms:
		for tile in room.tiles:
			draw_floor(tile)
