extends Node3D
## Tower Defense — Lecture 27 (Starter)
##
## Main game world with enemy spawning, wave management,
## player health tracking, money system, and tower placement.
## Supports both instant-hit and projectile towers.

# ============================================================
# CONSTANTS
# ============================================================

const PLAYER_START_HP := 100

# Enemy types and wave definitions
const WAVE_1 := ["slime", "slime", "slime"]
const WAVE_2 := ["slime", "slime", "goblin", "slime"]
const WAVE_3 := ["goblin", "goblin", "slime", "goblin", "slime"]
const WAVE_4 := ["goblin", "goblin", "goblin", "orc"]
const WAVE_5 := ["orc", "goblin", "goblin", "orc", "slime"]

const SPAWN_DELAY := 1.0  # Seconds between each enemy in a wave
const WAVE_DELAY := 5.0   # Seconds between waves

# Money and rewards
const START_MONEY := 500
const KILL_REWARD_SLIME := 25
const KILL_REWARD_GOBLIN := 40
const KILL_REWARD_ORC := 75

# ============================================================
# STATE
# ============================================================

var player_hp: int = PLAYER_START_HP
var current_wave: int = 0
var enemies_remaining_in_wave: int = 0
var is_spawning: bool = false
var money: int = START_MONEY

# Tower placement
var selected_tower_type: int = 0  # 0 = instant, 1 = projectile
var preview_tower: Node3D = null
var hover_cell_indicator: MeshInstance3D = null
var placed_towers: Array[Node3D] = []
var occupied_cells: Array[Vector2i] = []

# Wave list
var waves: Array[Array] = [WAVE_1, WAVE_2, WAVE_3, WAVE_4, WAVE_5]

# ============================================================
# NODES
# ============================================================

@onready var hp_label: Label = $UI/HPLabel
@onready var wave_label: Label = $UI/WaveLabel
@onready var money_label: Label = $UI/MoneyLabel
@onready var tower_info_label: Label = $UI/TowerInfoLabel
@onready var spawn_timer: Timer = $SpawnTimer
@onready var wave_timer: Timer = $WaveTimer
@onready var enemy_path: Path3D = $EnemyPath
@onready var camera: Camera3D = $Camera3D

# Enemy and tower scene preload
var enemy_scene := preload("res://scenes/enemy.tscn")
var tower_instant_scene := preload("res://scenes/tower_instant.tscn")
var tower_projectile_scene := preload("res://scenes/tower_projectile.tscn")

# ============================================================
# LIFECYCLE
# ============================================================

func _ready() -> void:
	_create_hover_cell_indicator()
	_update_ui()
	_start_next_wave()


func _process(_delta: float) -> void:
	_update_tower_preview()


func _input(event: InputEvent) -> void:
	# === TODO 1: Implement input handling ===
	# Tower type selection:
	#   If event.is_action_pressed("ui_text_backspace"): (Key 1)
	#     Set selected_tower_type to 0
	#     Call _destroy_preview_tower()
	#     Call _update_ui()
	#   Elif event is InputEventKey and event.pressed and event.keycode == KEY_2:
	#     Set selected_tower_type to 1
	#     Call _destroy_preview_tower()
	#     Call _update_ui()
	#
	# Tower placement:
	#   If event.is_action_pressed("left_click"):
	#     Call _try_place_tower()
	pass


# ============================================================
# WAVE MANAGEMENT
# ============================================================

## Starts the next wave if available
func _start_next_wave() -> void:
	if current_wave >= waves.size():
		_game_won()
		return
	
	current_wave += 1
	var wave_enemies := waves[current_wave - 1]
	enemies_remaining_in_wave = wave_enemies.size()
	
	_update_ui()
	
	# Start spawning enemies
	is_spawning = true
	_spawn_next_enemy_in_wave(wave_enemies, 0)


## Recursively spawns enemies with delay
func _spawn_next_enemy_in_wave(wave_enemies: Array, index: int) -> void:
	if index >= wave_enemies.size():
		is_spawning = false
		return
	
	_spawn_enemy(wave_enemies[index])
	
	# Schedule next enemy
	await get_tree().create_timer(SPAWN_DELAY).timeout
	_spawn_next_enemy_in_wave(wave_enemies, index + 1)


## Spawns a single enemy of the given type
func _spawn_enemy(enemy_type: String) -> void:
	var enemy := enemy_scene.instantiate()
	enemy_path.add_child(enemy)
	
	# Set enemy type
	enemy.enemy_type = enemy_type
	enemy.initialize()
	
	# Connect signals
	enemy.reached_end.connect(_on_enemy_reached_end)
	enemy.died.connect(_on_enemy_died)


# ============================================================
# ENEMY CALLBACKS
# ============================================================

## Called when enemy reaches end of path
func _on_enemy_reached_end(damage: int) -> void:
	player_hp -= damage
	player_hp = maxi(player_hp, 0)
	_update_ui()
	
	enemies_remaining_in_wave -= 1
	_check_wave_complete()
	
	if player_hp <= 0:
		_game_over()


## Called when enemy is killed by tower
func _on_enemy_died(enemy_type: String) -> void:
	# === TODO 2: Award money based on enemy type ===
	# Initialize reward to 0
	# Use match statement on enemy_type:
	#   "slime": reward = KILL_REWARD_SLIME
	#   "goblin": reward = KILL_REWARD_GOBLIN
	#   "orc": reward = KILL_REWARD_ORC
	# Add reward to money
	# Call _update_ui()
	# Decrement enemies_remaining_in_wave
	# Call _check_wave_complete()
	pass


## Checks if wave is complete and starts next wave
func _check_wave_complete() -> void:
	if enemies_remaining_in_wave <= 0 and not is_spawning:
		# Wait before next wave
		wave_timer.start(WAVE_DELAY)
		await wave_timer.timeout
		_start_next_wave()


# ============================================================
# UI
# ============================================================

func _update_ui() -> void:
	hp_label.text = "HP: %d" % player_hp
	wave_label.text = "Wave: %d / %d" % [current_wave, waves.size()]
	money_label.text = "Money: $%d" % money
	
	# Tower info
	var tower_name := "Instant Tower" if selected_tower_type == 0 else "Projectile Tower"
	var tower_cost := 100 if selected_tower_type == 0 else 150
	tower_info_label.text = "[1] %s ($%d) | [2] Projectile ($150)" % [tower_name, tower_cost]


# ============================================================
# GAME OVER / WIN
# ============================================================

func _game_over() -> void:
	print("Game Over!")
	get_tree().paused = true
	# TODO: Show game over screen in Lecture 28


func _game_won() -> void:
	print("Victory! All waves defeated!")
	get_tree().paused = true
	# TODO: Show victory screen in Lecture 28


# ============================================================
# TOWER PLACEMENT
# ============================================================

## Updates tower preview position
func _update_tower_preview() -> void:
	# === TODO 3: Update tower preview and hover cell ===
	# Get mouse_pos from viewport
	# Project ray from camera using camera.project_ray_origin() and project_ray_normal()
	# Create PhysicsRayQueryParameters3D from ray origin to far point (origin + normal * 1000)
	# Cast ray using get_world_3d().direct_space_state.intersect_ray()
	#
	# If result exists:
	#   Get world_pos from result.position
	#   Snap to grid using _snap_to_grid(world_pos)
	#   Convert to cell using _world_to_cell(snapped_pos)
	#   Check validity:
	#     is_occupied = cell in occupied_cells
	#     is_on_road = enemy_path.is_cell_on_road(cell)
	#     is_valid = not is_occupied and not is_on_road
	#
	#   Call _update_hover_cell(snapped_pos, is_valid)
	#
	#   If preview_tower doesn't exist: call _create_preview_tower()
	#   If preview_tower exists:
	#     Set preview_tower.global_position to snapped_pos
	#     Call _update_preview_color(is_valid)
	# Else (no raycast hit):
	#   Hide hover_cell_indicator if it exists
	pass


## Creates preview tower
func _create_preview_tower() -> void:
	var scene := tower_instant_scene if selected_tower_type == 0 else tower_projectile_scene
	preview_tower = scene.instantiate()
	add_child(preview_tower)
	preview_tower.is_preview = true  # Disable AI for preview
	preview_tower.show_range()


## Destroys the preview tower
func _destroy_preview_tower() -> void:
	if preview_tower:
		preview_tower.queue_free()
		preview_tower = null


## Updates preview tower color
func _update_preview_color(is_valid: bool) -> void:
	# === TODO 4: Update preview tower color based on validity ===
	# Return early if preview_tower doesn't exist
	# Get the MeshInstance3D node from preview_tower
	# Create a StandardMaterial3D
	# Set albedo_color to GREEN if is_valid, else RED
	# Set transparency to TRANSPARENCY_ALPHA
	# Set albedo_color.a to 0.5 (semi-transparent)
	# Apply material to mesh using set_surface_override_material(0, material)
	pass


## Tries to place tower at current preview position
func _try_place_tower() -> void:
	# === TODO 5: Validate and place tower ===
	# Return early if preview_tower doesn't exist
	#
	# Get position and cell from preview_tower
	# Check if cell is occupied (in occupied_cells array):
	#   If so, print "Cell already occupied!" and return
	# Check if cell is on road (enemy_path.is_cell_on_road()):
	#   If so, print "Cannot place tower on road!" and return
	#
	# Get tower cost (100 for instant, 150 for projectile)
	# Check if enough money:
	#   If not, print "Not enough money!" and return
	#
	# Place tower:
	#   Deduct cost from money
	#   Append cell to occupied_cells
	#   Instantiate tower from appropriate scene
	#   Add tower as child
	#   Set tower.global_position to pos
	#   Append tower to placed_towers
	#   Call _update_ui()
	#   Print "Tower placed!"
	pass


## Snaps world position to grid
func _snap_to_grid(pos: Vector3) -> Vector3:
	const GRID_SIZE := 2.0
	return Vector3(
		roundi(pos.x / GRID_SIZE) * GRID_SIZE,
		0.0,
		roundi(pos.z / GRID_SIZE) * GRID_SIZE
	)


## Converts world position to grid cell
func _world_to_cell(pos: Vector3) -> Vector2i:
	const GRID_SIZE := 2.0
	return Vector2i(
		roundi(pos.x / GRID_SIZE),
		roundi(pos.z / GRID_SIZE)
	)


# ============================================================
# HOVER CELL INDICATOR
# ============================================================

## Creates the hover cell indicator mesh
func _create_hover_cell_indicator() -> void:
	# === TODO 6: Create hover cell indicator mesh ===
	# Create MeshInstance3D
	# Create PlaneMesh with size Vector2(2.0, 2.0) (GRID_SIZE)
	# Assign mesh to hover_cell_indicator
	#
	# Create StandardMaterial3D:
	#   transparency = TRANSPARENCY_ALPHA
	#   shading_mode = SHADING_MODE_UNSHADED
	#   cull_mode = CULL_DISABLED (visible from both sides)
	# Set as material_override
	#
	# Rotate hover_cell_indicator.rotation.x = -PI/2 (lie flat)
	# Set position.y = 0.02 (slightly above ground)
	# Set visible to false
	# Add as child
	pass


## Updates hover cell color based on validity
func _update_hover_cell(snapped_pos: Vector3, is_valid: bool) -> void:
	# === TODO 7: Update hover cell position and color ===
	# Return early if hover_cell_indicator doesn't exist
	# Set visible to true
	# Set global_position to snapped_pos with y = 0.02
	# Get the material_override as StandardMaterial3D
	# Set albedo_color to green (0.2, 1.0, 0.2, 0.3) if valid,
	#   or red (1.0, 0.2, 0.2, 0.3) if invalid
	pass
