# Duane's Dungeon - a rogue-like game
# Copyright (C) 2023 Duane Robertson

# entity.gd - anything on the map

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.


extends Node2D
class_name Entity


const ACT_RAD = 20.0
const SPRITE_TEXT_SHRINK = 4.0


var act_rect := Rect2i(0, 0, 0, 0)
var back_cover:ColorRect
var body_area:Area2D
var description := 'an entity of some sort'
var ename:String
var icon_fg := 'ffffff'
var icon_name := 'player'
var is_dead := false
var kills := 0
var level := 0
var map:TileMap
var grid_position:Vector2i = Vector2i(10, 10)
var name_label:Label
var reloaded := false
var species:StringName = ''
var sprite:Sprite2D
var sprite_sheet:SpriteSheet
var sprite_text:VBoxContainer
var status:RefCounted
var target_pos:Vector2
var tile_size:Vector2i
var uid := 0
var unliving := false
var vision_area:Area2D
var vision := 5


func _init(in_data=null):
	# uid = G.make_uid()

	set_species_attributes()
	# assert(species != 'null')

	status = Status.new(self)

	if in_data is Dictionary:
		pass
	# 	reloaded = true
	# 	load_data(in_data)

	elif in_data is Vector2:
		place(in_data)


# Called when the node enters the scene tree for the first time.
func _ready():
	add_to_group('entities')

	if not map:
		map = get_parent()

	assert(map)

	sprite_sheet = G.sprite_sheet
	tile_size = sprite_sheet.tile_size

	make_sprite(icon_name, icon_fg)
	if not is_in_group('player'):
		place(grid_position)


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
	pass


func add_sprite_text(color := Color.GREEN, text := '') -> void:
	if not close_to_player():
		return

	var lab := VanishingLabel.new(text, color, 2, \
		20, HORIZONTAL_ALIGNMENT_LEFT)
	sprite_text.add_child(lab)


func close_to_player() -> bool:
	var player = get_tree().get_nodes_in_group('player')[0]

	return player.act_rect.has_point(grid_position)


func make_sprite(icon:String, sp_color:String):
	# Is this used with anything other than icon_name?

	var spr = Sprite2D.new()
	var tex:ImageTexture = G.sprite_sheet.texture(icon, sp_color)

	back_cover = ColorRect.new()
	back_cover.color = Color.BLACK
	back_cover.visible = false
	add_child(back_cover)

	spr.texture = tex
	spr.centered = false
	sprite = spr
	add_child(sprite)

	# When loaded, status sprites end up under
	#  the main sprite, since they're loaded first.
	move_child(sprite, 0)

	vision_area = Area2D.new()
	vision_area.collision_mask = 2
	var coll = CollisionShape2D.new()
	coll.shape = RectangleShape2D.new()
	coll.shape.size = Vector2(tile_size * vision * 2)
	coll.position = Vector2(tile_size) / 2.0
	vision_area.add_child(coll)
	add_child(vision_area)

	body_area = Area2D.new()
	body_area.collision_layer = 2
	coll = CollisionShape2D.new()
	coll.shape = RectangleShape2D.new()
	coll.shape.size = Vector2(tile_size / 2)
	coll.position = Vector2(tile_size) / 2.0
	body_area.add_child(coll)
	add_child(body_area)

	sprite_text = VBoxContainer.new()
	sprite_text.custom_minimum_size = tile_size * SPRITE_TEXT_SHRINK
	sprite_text.scale = Vector2.ONE / SPRITE_TEXT_SHRINK
	sprite_text.add_theme_constant_override('separation', -10)
	sprite_text.mouse_filter = Control.MOUSE_FILTER_IGNORE
	sprite.add_child(sprite_text)

	var nl = Label.new()
	nl.scale = Vector2.ONE / SPRITE_TEXT_SHRINK / 3.0
	nl.mouse_filter = Control.MOUSE_FILTER_IGNORE
	nl.text = name
	nl.custom_minimum_size = Vector2(16 * SPRITE_TEXT_SHRINK * 3.0, 0)
	nl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
	nl.add_theme_font_override('font', G.font)
	nl.add_theme_font_size_override('font_size', 40)
	sprite.add_child(nl)
	name_label = nl


# func move_to(pos:Vector2i):
# 	grid_position = pos
# 	position = Vector2(grid_position * tile_size)
# 	target_pos = position


func place(pos: Vector2i, force:=false) -> bool:
	if not walkable(pos):
		return false

	if not force and map.entity_at(pos):
		return false

	# map.move_entity(self, pos)
	grid_position = pos

	if is_in_group('player'):
		# position = map.map_to_local(grid_position)
		position = Vector2(grid_position * tile_size)
		# move_map(false)
		# map.unshade(grid_position)
	else:
		# position = map.map_to_local(grid_position)
		position = Vector2(grid_position * tile_size)

	target_pos = position

	return true


func release_if_dead():
	if is_dead:
		Creature.release_name(name)
		map.remove_entity(self)


func set_species_attributes():
	pass


func show_message(_text: String, _color := Color.YELLOW) -> void:
	if close_to_player():
		G.debug('%s (%s): %s' % [species, name, _text])


func turn():
#	G.debug('entity turn')
	pass


func vary(n: int) -> int:
	return int(round(max(1, n * ((randi() % 100) + 51) / 100.0)))


func walkable(p:Vector2i) -> bool:
	return map.terr_map[map.get_tile(p)].walkable
