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

# dungeon.gd - the dungeon 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 TileMap
class_name Dungeon


var astar:AStarGrid2D
var bounds:Rect2i
var game:Node2D
var is_dungeon = true
var grid_position:Vector2i = Vector2i(5, 5)
var level := 0
var map_off := Vector2(5, 5)
var open_spaces:Array = [ ]
var open_spaces_dic:Dictionary = { }
var param:Dictionary
var player  # ??
var terr_map:Dictionary
var terr_name:Dictionary
var tile_size:Vector2i


# Called when the node enters the scene tree for the first time.
func _ready():
	game = get_node('/root/Game')

	var mm = MapMaker.new()
	add_child(mm)

	tile_size = tile_set.tile_size

	# mm.build(7414521855)
	mm.build()

	astar = AStarGrid2D.new()
	astar.region = bounds
	astar.update()

	for y in bounds.size.y:
		for x in bounds.size.x:
			var v = Vector2i(x, y) + bounds.position
			if not terr_map[get_tile(v)].walkable:
				astar.set_point_solid(v, true)

	texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
	visible = true


func add_player():
	player = Player.new()
	player.map = self
	add_child(player)

	player.place(get_open_space())

	####################################
	for i in 200:
		var species = Creature.species_names.pick_random()
		var cr = Creature.species_classes[species].new()
		add_child(cr)
		assert(cr.map)
		assert(cr.faction)
		cr.place(get_open_space())
	####################################

	return player


func entity_at(pos:Vector2i):
	for ent in get_tree().get_nodes_in_group('entities'):
		if ent.grid_position == pos:
			return ent
	return


func get_open_space(ibound := bounds) -> Vector2i:
	var all = (ibound == bounds)
	var bound := bounds.intersection(ibound)
	var spaces = open_spaces
	var p := Vector2i.ZERO

	###########################################
	if not all:
		spaces = [ ]
		for y in range(bound.position.y, bound.end.y + 1):
			for x in range(bound.position.x, bound.end.x + 1):
				var v = Vector2i(x, y)
				if open_spaces_dic.get(v):
					spaces.append(v)
		spaces.shuffle()
	###########################################

	for i in len(spaces) + 1:
		if i == len(spaces):
			G.error('no open space left')
			break

		p = spaces.pop_front()
		if entity_at(p):
			spaces.push_back(p)
		else:
			break

	return p


func get_tile(pos:Vector2i):
	return terr_name.get(get_cell_atlas_coords(0, pos))


func make_mini_map(preview:=false):
	G.stopwatch()

	var trec := TextureRect.new()
	trec.size = bounds.size

	var sz = bounds.size + Vector2i.ONE * 2
	var img := Image.create(sz.x, sz.y, false, Image.FORMAT_RGBA8)

	for y in range(-1, bounds.size.y + 1):
		for x in range(-1, bounds.size.x + 1):
			var v := Vector2i(x, y) + bounds.position
			if (not preview) and v == player.grid_position:
				img.set_pixel(x+1, y+1, Color.WHITE)
			# elif preview or known_loc(v):
			elif true:
				var col
				# if map_items.get_tile(v) == ITEM_CHEST:
				# 	col = Color(COLOR_WOOD_LIGHT)
				# elif map_items.get_tile(v) == ITEM_STAIR_DOWN \
				# or map_items.get_tile(v) == ITEM_STAIR_UP:
				# 	col = Color('c000c0')
				# else:
				if true:
					var ter = get_tile(v)
					# print(terr_map[ter].minimap_color)
					col = Color(terr_map[ter].minimap_color)
				img.set_pixel(x+1, y+1, col)
			else:
				img.set_pixel(x+1, y+1, Color.BLACK)

	var tex := ImageTexture.create_from_image(img)
	trec.texture = tex
	trec.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST

	trec.name = 'MiniMap'

	G.stopwatch('minimap time', true)

	return trec


func remove_entity(ent:Entity):
	# if entity_map.get(ent.grid_position):
	# 	entity_map.erase(ent.grid_position)

	# entity_ids.erase(ent.uid)

	if not ent.get('is_player'):
		ent.visible = false
		astar.set_point_weight_scale(ent.grid_position, 1.0)
		ent.queue_free()

	return true


func set_tile(pos:Vector2i, tile:String):
	set_cell(0, pos, 0, terr_map[tile].atlas_coords)


var ttt = 0
var tts = 0
# 295ms, 273ms, 276ms
func turn(game_turn:int) -> void:
	tts += 1
	G.debug('dungeon turn')

	var t = Time.get_ticks_msec()
	var smt = 0
	get_tree().call_group('creatures', 'turn')
	for ent in get_tree().get_nodes_in_group('creatures'):
		if ent.smt:
			smt += 1

	var t2 = Time.get_ticks_msec() - t
	ttt += t2
	G.debug('all entity turns: %dms' % [t2])
	G.debug(' average: %dms' % [ttt / tts])
	G.debug('successful move_towards: %d' % [smt])

	#######################################################
	var d = {}
	for ent in get_tree().get_nodes_in_group('creatures'):
		if d.has(ent.grid_position):
			G.error('colocated mobs')
		d[ent.grid_position] = true
	#######################################################

	# var bound:Rect2 = G.player.act_rect
	# bound = bound.clip(bounds)
	# var circ:float = CIRCLE_SPAWN * bound.get_area() / 10000.0

	# while randf() < circ:
	# 	circ -= 1
	# 	spawn_circle(bound)

	# handle_items(bound)

	# for c in get_entities():
	# 	c.visible = unshaded(c.grid_position) or G.DEBUG

	# for c in get_entities():
	# 	if not c.is_player:
	# 		c.acted = false

	# for c in get_entities():
	# 	if not c.is_player:
	# 		c.turn(game_turn)

	get_tree().call_group('creatures', 'release_if_dead')

	# status.turn(game_turn)

	# for c in get_entities():
	# 	c.visible = unshaded(c.grid_position) or G.DEBUG
