from __future__ import annotations
from typing import Iterator, List, Tuple
import random

import tcod

import tile_types
import entity_types
from floor_map import FloorMap

#utils
def make_corridor(start: Tuple[int, int], end: Tuple[int, int]) -> Iterator[Tuple[int,int]]:
	#simplistic, but it works
	x1, y1 = start
	x2, y2 = end

	if random.random() < 0.5:
		corner_x, corner_y = x2, y1
	else:
		corner_x, corner_y = x1, y2

	for x, y in tcod.los.bresenham((x1, y1), (corner_x, corner_y)).tolist():
		yield x, y
	for x, y in tcod.los.bresenham((corner_x, corner_y), (x2, y2)).tolist():
		yield x, y

class RectangularRoom:
	def __init__(self, x: int, y: int, width: int, height: int):
		self.x1 = x
		self.y1 = y
		self.x2 = x + width
		self.y2 = y + height

	@property
	def center(self) -> Tuple[int, int]:
		center_x = (self.x1 + self.x2) // 2
		center_y = (self.y1 + self.y2) // 2

		return center_x, center_y

	@property
	def inner(self) -> Tuple[slice, slice]:
		return slice(self.x1 + 1, self.x2), slice(self.y1 + 1, self.y2)

	def intersects(self, other: RectangularRoom) -> bool:
		return (
			self.x1 <= other.x2 and
			self.x2 >= other.x1 and
			self.y1 <= other.y2 and
			self.y2 >= other.y1
		)

def spawn_monsters(floor_map: FloorMap, room: RectangularRoom, room_monsters_max: int) -> None:
	monster_count = random.randint(0, room_monsters_max)

	#There can only be one
	if "gobbo_red" not in floor_map.procgen_cache:
		floor_map.procgen_cache["gobbo_red"] = False

	for i in range(monster_count):
		#admittedly weird layout here, because player isn't in the entities list yet
		x, y = floor_map.player.x, floor_map.player.y
		while x == floor_map.player.x and y == floor_map.player.y:
			x = random.randint(room.x1 + 1, room.x2 - 1)
			y = random.randint(room.y1 + 1, room.y2 - 1)

		#if there's no entity at that position
		if not any(entity.x == x and entity.y == y for entity in floor_map.entities):
			#there's never more than one red gobbo, but there can be none at all
			if not floor_map.procgen_cache["gobbo_red"] and random.random() < 0.2:
				floor_map.procgen_cache["gobbo_red"] = True
				entity_types.gobbo_red.spawn(x, y, floor_map)
			else:
				entity_types.gobbo.spawn(x, y, floor_map)

#generators
def generate_floor_map(
	map_width: int,
	map_height: int,
	room_width_max: int,
	room_height_max: int,
	room_width_min: int = 6,
	room_height_min: int = 6,
	room_count_max: int = 20,
	room_monsters_max: int = 2
) -> FloorMap:
	#simplistic floor generator
	floor_map: FloorMap = FloorMap(map_width, map_height)

	rooms: List[RectangularRoom] = []

	for r in range(room_count_max):
		room_width = random.randint(room_width_min, room_width_max)
		room_height = random.randint(room_height_min, room_height_max)

		x = random.randint(0, floor_map.width - room_width - 1)
		y = random.randint(0, floor_map.height - room_height - 1)

		new_room = RectangularRoom(x, y, room_width, room_height)

		if any(new_room.intersects(other_room) for other_room in rooms):
			continue #nope.jpg

		floor_map.tiles[new_room.inner] = tile_types.floor

		if len(rooms) == 0:
			x, y = new_room.center
			floor_map.player = entity_types.player.spawn(x, y, floor_map)
		else:
			for x, y in make_corridor(rooms[-1].center, new_room.center):
				floor_map.tiles[x, y] = tile_types.floor

		spawn_monsters(floor_map, new_room, room_monsters_max)

		rooms.append(new_room)

	return floor_map