extends Spatial

# TODO: create something like Vector3i

var building_destruction_scene = preload("res://scenes/destruction.tscn")

onready var characters = get_node("/root/Level/Map/Characters")
onready var money = get_node("/root/Level/Map/Money")
onready var utils = get_node("/root/Level/Map/Utils")
onready var tiles : GridMap = get_node("/root/Level/Map/Tiles")
onready var buildings : GridMap = get_node("/root/Level/Map/Buildings")

export var EMPTY_TILE = 0

export var PLAYERS_COUNT = 2
export var DEFAULT_PLAYER_TILES = [1, 2, 3, 4, 5]
export var ACTIVE_PLAYER_TILES = [6, 7, 8, 9, 10]

var player_tiles = []

export var HOUSE_CELLS = [0, 1, 2, 11, 12, 13, 14, 16, 19] # 14, 19 - mills
export var TOWER_CELLS = [2, 18]

export var HOUSE_DEFENCE_LEVEL = 1
export var TOWER_DEFENCE_LEVEL = 2

const directions = [
	Vector3(2, 0, 0), 
	Vector3(-2, 0, 0), 
	Vector3(1, 0, 2), 
	Vector3(-1, 0, 2), 
	Vector3(1, 0, -2), 
	Vector3(-1, 0, -2)
]
	
func get_tile_cell(position : Vector3):
	return tiles.get_cell_item(int(round(position.x)), int(round(position.y)), int(round(position.z)))
	
func set_tile_cell(position : Vector3, cell : int):
	tiles.set_cell_item(int(round(position.x)), int(round(position.y)), int(round(position.z)), cell)

func get_building_cell(position : Vector3):
	return buildings.get_cell_item(int(round(position.x)), int(round(position.y)), int(round(position.z)))
	
func set_building_cell(position : Vector3, cell : int):
	buildings.set_cell_item(int(round(position.x)), int(round(position.y)), int(round(position.z)), cell)

func has_neighbour_tile_cell(position : Vector3, required_neighbour_tile_cell : int):
	for direction in directions:
		var neighbour_position = direction + position
		if get_tile_cell(neighbour_position) == required_neighbour_tile_cell:
			return true 
	
	return false
	
func has_building_defence(position : Vector3, building_cell : int, skipped_player : int):
	var current_cell = get_tile_cell(position)
	for direction in directions:
		var neighbour_position = direction + position
		var neighbour_cell = get_tile_cell(neighbour_position)
		
		if current_cell == neighbour_cell and \
		   neighbour_cell != player_tiles[skipped_player] and \
		   get_building_cell(neighbour_position) == building_cell:
			return true 
	
	return get_building_cell(position) == building_cell and current_cell != player_tiles[skipped_player]
	
func is_neighbour_tiles(position : Vector3, other_position : Vector3):
	for direction in directions:
		if direction.is_equal_approx(other_position - position):
			return true
	return false

func start_player_turn(player : int):
	money.update_houses_money(player)
	money.pay_tower_salaries(player)
	money.pay_character_salaries(player)

func are_tiles_connected(start_position : Vector3, end_position : Vector3, between_tile_cell : int):
	if start_position.is_equal_approx(end_position):
			return true
	
	var visited = {}
	
	var to_visit = [start_position]
	visited[utils.position_to_string(start_position)] = null
	
	for current_position in to_visit:
		for direction in directions:
			if (current_position + direction).is_equal_approx(end_position):
				return true
			if get_tile_cell(current_position + direction) == between_tile_cell and \
			   not visited.has(utils.position_to_string(current_position + direction)):
				visited[utils.position_to_string(current_position + direction)] = null
				to_visit.append(current_position + direction)
	
	return false

func is_connected_to_house(start_position : Vector3, tile_cell : int):
	var visited = {}
	
	var to_visit = [start_position]
	visited[utils.position_to_string(start_position)] = null
	
	for current_position in to_visit:
		if HOUSE_CELLS.has(get_building_cell(current_position)):
			return true
		
		for direction in directions:
			if get_tile_cell(current_position + direction) == tile_cell and \
			   not visited.has(utils.position_to_string(current_position + direction)):
				visited[utils.position_to_string(current_position + direction)] = null
				to_visit.append(current_position + direction)
	
	return false

func tile_block_level(position : Vector3, inviding_player : int):
	var block_level = -1
	
	block_level = characters.characters_tile_block_level(position, inviding_player)
	
	for house_cell in HOUSE_CELLS:
		if has_building_defence(position, house_cell, inviding_player):
			block_level = max(block_level, HOUSE_DEFENCE_LEVEL)	
			break
	
	for tower_cell in TOWER_CELLS:
		if has_building_defence(position, tower_cell, inviding_player):
			block_level = max(block_level, TOWER_DEFENCE_LEVEL)	

	return block_level

func move_to_tile(position : Vector3):
	var tile_cell = get_tile_cell(position)
	var building_cell = get_building_cell(position)
	var world_position = utils.grid_to_world_position(position)
	
	# tile to move should be connected with current character tile
	if not are_tiles_connected(utils.world_to_grid_position(characters.current_character_obj().translation), position, current_player_tile()):
		return
	
	# can't go on tile, blocked with >= level, except characters with MAX_LEVEL
	if tile_block_level(position, characters.current_player) >= characters.current_character_obj().level and \
	   characters.current_character_obj().level != characters.current_character_obj().MAX_LEVEL:
		return
	
	if tile_cell == GridMap.INVALID_CELL_ITEM:
		return
	
	# move without action (to current player tile)
	if tile_cell == current_player_tile() and \
	   building_cell == GridMap.INVALID_CELL_ITEM:
		
		if characters.destroy_other_player_characters(position, characters.current_player) > 0:
			utils.spawn_on_position(building_destruction_scene, position)
		
		characters.current_character_obj().translation = world_position
	
	# move with action (to tile of other player)
	elif tile_cell != GridMap.INVALID_CELL_ITEM and \
		 tile_cell != current_player_tile() and \
		 has_neighbour_tile_cell(position, current_player_tile()):
		
		set_tile_cell(position, current_player_tile())
		
		if building_cell != GridMap.INVALID_CELL_ITEM or \
		   characters.destroy_other_player_characters(position, characters.current_player) > 0:
			utils.spawn_on_position(building_destruction_scene, position)
		
		if building_cell != GridMap.INVALID_CELL_ITEM:
			money.destroy_house(position)
			
		set_building_cell(position, GridMap.INVALID_CELL_ITEM)
		
		characters.current_character_obj().translation = world_position
		
		characters.turn_characters_into_rogues()
		
		characters.set_next_character()

func current_player_tile():
	return player_tiles[characters.current_player]
	
func remove_active_player_color(player : int):
	print("Player tile: ", player_tiles[player])
	for tile_position in tiles.get_used_cells():
		if get_tile_cell(tile_position) == ACTIVE_PLAYER_TILES[player]:
			print("Active tile removed")
			set_tile_cell(tile_position, DEFAULT_PLAYER_TILES[player])	

func set_active_player_color(player : int):
	for tile_position in tiles.get_used_cells():
		if get_tile_cell(tile_position) == DEFAULT_PLAYER_TILES[player]:
			set_tile_cell(tile_position, ACTIVE_PLAYER_TILES[player])

func _ready():
	if characters.is_current_character_exist():
		characters.current_character_obj().active = true
	
	player_tiles = DEFAULT_PLAYER_TILES.duplicate()
	
	set_active_player_color(characters.current_player)
	player_tiles[characters.current_player] = ACTIVE_PLAYER_TILES[characters.current_player]
		
	money.init_houses()
