extends Node
class_name game

var player_gold: int = 10
var shop_scene = preload("res://scenes/Shop.tscn")
var shop: Node2D
var purchased_pieces: Array = []
var purchased_upgrades: Array = []

@export var tile_size = 64
@export var dark = Color(0,0,0,1)
@export var light =Color(1,1,1,1)

@onready var dark_tile_shader: ShaderMaterial = ShaderMaterial.new()
@onready var light_tile_shader: ShaderMaterial = ShaderMaterial.new()

@onready var opponent_shader: ShaderMaterial = ShaderMaterial.new()

@onready var background = $Background
@onready var tile_container = $TileContainer
@onready var shadow_container = $ShadowContainer
@onready var piece_container = $PieceContainer
@onready var board = []

@onready var player_pieces = $PlayerPieces
@onready var player_piece_count = 0
@onready var opponent_pieces = $OpponentPieces
@onready var opponent_piece_count = 0
@onready var gold_display: Label = $GoldDisplay

var selected_piece
var selected_piece_value
var selected_piece_position
var valid_moves = []
@onready var selected_shader: ShaderMaterial = ShaderMaterial.new()

var board_width = 8
var board_height = 8
var target_position
var moving: bool
var sprite: Sprite2D

var player_turn : bool = true

@onready var explosion_effect = $Explosion


func _ready() -> void:
	shop = shop_scene.instantiate()
	add_child(shop)
	var viewport_size = get_viewport().get_visible_rect().size

	shop.position = (viewport_size / 2) - Vector2(570, 650) / 2 # pretty much centered, not sure on the exact values / how to scale it to screen size better
	shop.visible = false  # Start hidden
	update_gold_display()  # Initialize gold display
	set_gold_display_position() # Set the anchors and margins for the gold display
	shop.purchase_attempted.connect(_on_purchase_attempted)
	shop.next_round_requested.connect(_on_next_round_requested)
	
	# Connect the resize signal
	get_viewport().connect("size_changed", Callable(self, "_on_viewport_size_changed"))

	DisplayServer.window_set_min_size(Vector2(1152, 648))  # Set minimum window size
	
	#creates 2D array for board, with empty (null) spaces
	for x in range(board_width): 
		board.append([])
		for y in range(board_height): 
			board[x].append(null)
	
	
	#hard-coded setup of pieces
	board[0][7] = Rook.new(true, Vector2(0, 7))
	board[1][6] = Wizard.new(true, Vector2(0, 7))
	board[1][7] = Bishop.new(true, Vector2(1, 7))
	board[2][7] = King.new(true, Vector2(2, 7))
	board[3][6] = Queen.new(true, Vector2(3, 6))
	board[4][2] = Pawn.new(true, Vector2(4, 7))
	board[5][7] = Knight.new(true, Vector2(5, 7))
	board[6][7] = Assassin.new(true, Vector2(6, 7))
	board[7][6] = Mage.new(true, Vector2(7, 6))
	board[7][7] = Rook.new(true, Vector2(7, 7))

	board[0][4] = Rook.new(false, Vector2(0, 4))
	board[1][5] = Bishop.new(false, Vector2(1, 5))
	board[3][2] = Queen.new(false, Vector2(3, 2))
	board[3][5] = Pawn.new(false, Vector2(3, 5))
	board[4][4] = Assassin.new(false, Vector2(4, 4))
	board[3][4] = Pawn.new(false, Vector2(3, 4))
	board[4][3] = Mage.new(false, Vector2(4, 3))
	board[7][5] = Rook.new(false, Vector2(7, 5))
	
	#potential 'holes', not quite working yet
	#board[3][3] = '/'
	
	draw_board(board_width,board_height)
	
	#set up color for opponent's shader, currently set to black
	opponent_shader.shader = preload("res://assets/shaders/color.gdshader").duplicate()
	opponent_shader.set_shader_parameter('r', 0.2)
	opponent_shader.set_shader_parameter('g', 0.2)
	opponent_shader.set_shader_parameter('b', 0.2)
	
	#set up color for selected piece's shader, currently set to red
	selected_shader.shader = preload("res://assets/shaders/color.gdshader").duplicate()
	selected_shader.set_shader_parameter('r', 1.0)
	selected_shader.set_shader_parameter('g', 0.5)
	selected_shader.set_shader_parameter('b', 0.5)
	draw_pieces()


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	#dynamically translates loaction of board and pieces to remain in corrent location
	tile_container.position = translate()
	shadow_container.position = translate()
	piece_container.position = translate()
	
	#attempt at linear interpolation for smooth movement of pieces#

	#lerp for smooth movement of pieces
	if moving:
		#if piece is not at target position, linearly interpolate to target
		if selected_piece.position != selected_piece_position:
			#if within threshold, snap to position to avoid floating point error
			if selected_piece.position.distance_to(target_position) < 0.1:
				selected_piece.position = selected_piece_position
				unselect_piece()
				moving = false
			# Snap to exact position
			else:  
				selected_piece.position = selected_piece.position.lerp(selected_piece_position, 10 * delta)
		else:
			moving = false
			unselect_piece()

	if selected_piece != null:
		# If the selected piece is a pawn and it reaches the end of the board, promote it to a queen
		if selected_piece_position.y == 0:
			if selected_piece_value is Pawn:
					if selected_piece_value.is_white:
						# draw_pieces()
						selected_piece.queue_free()
						board[selected_piece_position.x][selected_piece_position.y] = Queen.new(true, selected_piece_position)
						var piece_scene = preload("res://scenes/Queen.tscn")
						var piece_instance = piece_scene.instantiate()
						piece_instance.position = Vector2((selected_piece_position.x * tile_size) + tile_size/2.0, (selected_piece_position.y * tile_size) + tile_size/2.0)
						piece_instance.z_index = 3
						piece_instance.scale = Vector2(1.25, 1.25)  # Scale other pieces by 25%
						piece_container.add_child(piece_instance)
						explosion_effect.position = piece_instance.position + translate()
						explosion_effect.restart()

						# remove_piece(selected_piece_position.x, selected_piece_position.y)


func draw_pieces():
	#iterate through all tiles, if tile has a piece, 
	#add corresponding piece with correct sprite to piece_container
	for x in range(board_width):
		for y in range(board_height):
			if !is_empty(x, y):
				var piece_scene
				if board[x][y] is Pawn:
					piece_scene = preload("res://scenes/Pawn.tscn")
				elif board[x][y] is Rook:
					piece_scene = preload("res://scenes/Rook.tscn")
				elif board[x][y] is Bishop:
					piece_scene = preload("res://scenes/Bishop.tscn")
				elif board[x][y] is Queen:
					piece_scene = preload("res://scenes/Queen.tscn")
				elif board[x][y] is King:
					piece_scene = preload("res://scenes/King.tscn")
				elif board[x][y] is Knight:
					piece_scene = preload("res://scenes/Knight.tscn")
				elif board[x][y] is Assassin:
					piece_scene = preload("res://scenes/Assassin.tscn")
				elif board[x][y] is Mage:
					piece_scene = preload("res://scenes/Mage.tscn")
				elif board[x][y] is Wizard:
					piece_scene = preload("res://scenes/Wizard.tscn")
				
				if piece_scene:
					var piece_instance = piece_scene.instantiate()
					piece_instance.position = Vector2((x * tile_size) + tile_size/2.0, (y * tile_size) + tile_size/2.0)
					piece_instance.z_index = 3

					# Scale the sprite by 25% for all pieces except the king
					if board[x][y] is King:
						piece_instance.scale = Vector2(2.0, 2.0)  # Scale the king by 50%
					else:
						piece_instance.scale = Vector2(1.25, 1.25)  # Scale other pieces by 25%

					# If piece is opponent, add a black shader to piece
					if board[x][y].is_white == false:
						var piece_sprite = piece_instance.get_node("Sprite2D") as Sprite2D
						if piece_sprite:
							piece_sprite.material = opponent_shader
					
					piece_container.add_child(piece_instance)


#translates 'home' coordinates ((0,0)) to screen location 
#used to center board, shadow and pieces at 2/3 of x axis and 1/2 of y axis
func translate() -> Vector2:
	var screen_size = get_viewport().get_visible_rect().size
	var board_x_offset = float(board_width * tile_size) / 2
	var board_y_offset = float(board_height * tile_size) / 2
	#places top left corner at 2/3 of x axis and 1/2 of y axis, then translates to center 
	return Vector2(screen_size.x * 2 / 3 - board_x_offset, screen_size.y / 2 - board_y_offset)

#color rect version
func draw_board(width, height):
	for x in range(width):
		for y in range(height):
			var tile = ColorRect.new()
			if (x + y)%2 == 0:
				tile.color = light
			else:
				tile.color = dark
			tile.size = Vector2(tile_size, tile_size)
			tile.position = Vector2(x * tile_size, y * tile_size)
			
			tile_container.add_child(tile)
			
			var shadow_shader = ShaderMaterial.new()
			shadow_shader.shader = preload("res://assets/shaders/shadow.gdshader")
			
			var shadow = ColorRect.new()
			shadow.size = Vector2(tile_size, tile_size)
			shadow.position = Vector2(x * tile_size + 15, y * tile_size + 15)
			
			shadow.material = shadow_shader
			shadow_container.add_child(shadow)

#textured rect version
func draw_board_texture_rect(width, height):
	dark_tile_shader.shader = preload("res://assets/shaders/color.gdshader").duplicate()
	dark_tile_shader.set_shader_parameter('r', 0.1)
	dark_tile_shader.set_shader_parameter('g', 0.25)
	dark_tile_shader.set_shader_parameter('b', 0.6)
	
	light_tile_shader.shader = preload("res://assets/shaders/color.gdshader").duplicate()
	light_tile_shader.set_shader_parameter('r', 0.85)
	light_tile_shader.set_shader_parameter('g', 0.85)
	light_tile_shader.set_shader_parameter('b', 0.85)
	
	for x in range(width):
		for y in range(height):
			if board[x][y] == '/':
				pass
			else:
				var tile = TextureRect.new()
				tile.texture = preload("res://assets/sprites/tile.png")
				if (x + y)%2 == 0:
					tile.material = light_tile_shader
				else:
					tile.material = dark_tile_shader
				tile.size = Vector2(tile_size, tile_size)
				tile.position = Vector2(x * tile_size, y * tile_size)
				
				tile_container.add_child(tile)
				var shadow_shader = ShaderMaterial.new()
				shadow_shader.shader = preload("res://assets/shaders/shadow.gdshader")
				
				var shadow = ColorRect.new()
				shadow.size = Vector2(tile_size, tile_size)
				shadow.position = Vector2(x * tile_size + 15, y * tile_size + 15)
				
				shadow.material = shadow_shader
				shadow_container.add_child(shadow)

#detects if mouse events occur within the board
func is_on_board(position : Vector2) -> bool:
	if position.x < (board_width * tile_size) && position.x > 0:
		if position.y < (board_height * tile_size) && position.y > 0:
			return true
	return false

func _input(event):
	if event is InputEventMouseButton:
		#handles only mouse clicks
		if !moving:
			if event.pressed:
				#translate the mouse click position to 'home' coordinates (start at 0,0)
				var translated_postition = event.position - translate()
				#if the click is within the board, proceed
				if is_on_board(translated_postition):
					#translates mouse position to array index. 
					#example: (66.5, 99.9) = (1, 1) , (120.0, 12.0) = (2, 0)
					var mouse_position = Vector2(int(translated_postition.x / tile_size), int(translated_postition.y / tile_size))
					
					var x = mouse_position.x
					var y = mouse_position.y
					
					#if mouse click is a left click, proceed
					if event.button_index == MOUSE_BUTTON_LEFT:
						if player_turn:
							#if the tile is a player's piece, select or deselect it
							if !is_empty(x,y) && !(is_opponent(x,y)):
								#if there is a currently selected piece that is on the selected tile, deselect that piece
								if selected_piece != null:
									if board[x][y] == selected_piece_value:
										unselect_piece()
									else:
										select_piece(x,y)
								#otherwise, select the piece
								else:
									select_piece(x,y)
							#if the tile is empty or an opponents piece, move the selected piece to that tile
							else:
								move_selected_piece(x,y)
						else:
							print("It's not your turn!")
							if !is_empty(x,y) && is_opponent(x,y):
								if selected_piece!=null:
									if board[x][y] == selected_piece_value:
										unselect_piece()
									else:
										select_piece(x,y)
								else:
									select_piece(x,y)
							else:
								move_selected_piece(x,y)



func is_opponent(x,y):
	if board[x][y] == null:
		return false
	else:
		# If the value of board[x][y] is an opponent piece
		if selected_piece != null:
			return board[x][y].is_white != selected_piece_value.is_white
		else:
			return !board[x][y].is_white

func is_opposite(x,y):
	if board[x][y] == null:
		return false
	else:
		if selected_piece != null:
			return board[x][y].is_white == selected_piece_value.is_white
		else:
			return board[x][y].is_white


func is_empty(x, y):
	return board[x][y] == null

#test funciton to add pieces to board
#func add_pawn(x, y):
	#var piece = TextureRect.new()
	#piece.position = Vector2(x*tile_size, y*tile_size)
	#piece.size = Vector2(64,64)
	#piece.z_index = 3
	#piece.texture = preload("res://pawn.png")
	#piece_container.add_child(piece)


func remove_piece(x,y):
	#iterate through the pieces in piece_container, if that piece is at location x,y, remove it
	# TODO: Fix bug with promoted Queen, when capturing other pieces, they are not visually removed.
	for child in piece_container.get_children():
		#translate x,y index values to screen coordinates
		if child.position == Vector2(x*tile_size + 32, y*tile_size + 32):
			explosion_effect.position = child.position + translate()
			explosion_effect.restart()

			var piece = child.duplicate()
			if is_opponent(x, y):
				opponent_piece_count += 1
				if opponent_piece_count <= 4:
					piece.position = Vector2(opponent_piece_count - 1, 0) * tile_size
				elif opponent_piece_count <= 8:
					piece.position = Vector2(opponent_piece_count - 5, 1) * tile_size
				elif opponent_piece_count <= 12:
					piece.position = Vector2(opponent_piece_count - 9, 2) * tile_size
				else:
					piece.position = Vector2(opponent_piece_count - 13, 3) * tile_size
				opponent_pieces.add_child(piece)
			else:
				player_piece_count += 1
				if player_piece_count <= 4:
					piece.position = Vector2(player_piece_count - 1, 0) * tile_size
				elif player_piece_count <= 8:
					piece.position = Vector2(player_piece_count - 5, 1) * tile_size
				elif player_piece_count <= 12:
					piece.position = Vector2(player_piece_count - 9, 2) * tile_size
				else:
					piece.position = Vector2(player_piece_count - 13, 3) * tile_size
				player_pieces.add_child(piece)


			piece_container.remove_child(child)
			child.queue_free()
			break


func select_piece(x,y):
	unselect_piece()

	#iterate through the pieces in piece_container, if that piece is at location x,y, select it
	for child in piece_container.get_children():
		if child.position == Vector2((x*tile_size)+32, (y*tile_size)+32):
			selected_piece_position = Vector2(x,y)
			selected_piece = child
			selected_piece_value = board[x][y]
			#add a red coloured shader to the selected piece
			child.material = selected_shader
			
			#highlight the valid moves the selected piece can make
			highlight_tiles()

func unselect_piece():
	if selected_piece != null:
		selected_piece.material = null
		selected_piece = null
		selected_piece_value = null
	remove_highlight()

func highlight_tiles():
	# Clear the previously selected valid moves
	remove_highlight()
	if selected_piece_value is Mage || selected_piece_value is Wizard:
		valid_moves = selected_piece_value.get_valid_moves(board, selected_piece_position)
		valid_moves += selected_piece_value.get_valid_shots(board, selected_piece_position)
	else:
		valid_moves = selected_piece_value.get_valid_moves(board, selected_piece_position)


	for child in tile_container.get_children():
		if child.position / tile_size in valid_moves:
			child.modulate = Color(1, 0.5, 0.5)
		else:
			child.modulate = Color(1, 1, 1)



func remove_highlight():
	#remove all highlights from tiles in tile_container
	for child in tile_container.get_children():
		child.modulate = Color(1,1,1)
	valid_moves = []



func move_selected_piece(x,y):
	#can only move a piece if selected_piece exists
	if selected_piece != null:
		#can only move to tiles in valid_moves
		if Vector2(x,y) in valid_moves:
			
			if selected_piece_value is Mage || selected_piece_value is Wizard:
				if abs(selected_piece_position.x - x) <= 1 and abs(selected_piece_position.y - y) <= 1:
					if is_empty(x, y):
						move_piece(x, y)
					else:
						shoot_projectile(x, y)
				else:	
					shoot_projectile(x, y)

			# check for if assassin is moving behind the opponent's pieces
			elif selected_piece_value is Assassin:
				if selected_piece_value.is_white:
					if is_opponent(x, y + 1):
						remove_piece(x, y + 1)
				else:
					if is_opponent(x, y - 1):
						remove_piece(x, y - 1)
				move_piece(x, y)

			else:
				move_piece(x, y)
		else:
			unselect_piece()
	remove_highlight()

func move_piece(x, y):
	# If tile has opponent piece, remove piece
	if is_opponent(x, y):
		remove_piece(x, y)

	# Clear the selected_piece's previous position
	board[selected_piece_position.x][selected_piece_position.y] = null

	# Update the board with the new position
	board[x][y] = selected_piece_value

	# Update selected_piece's screen coordinate position
	selected_piece_position = Vector2((x * tile_size) + tile_size / 2.0, (y * tile_size) + tile_size / 2.0)

	target_position = selected_piece_position
	moving = true
	valid_moves = []

func shoot_projectile(x: int, y: int):
	remove_piece(x, y)
	board[x][y] = null
	unselect_piece()

	explosion_effect.color = Color(1, 0, 1)
	explosion_effect.position = Vector2(x, y) * tile_size + translate() + Vector2(tile_size / 2.0, tile_size / 2.0)
	explosion_effect.restart()

	# player_turn = !player_turn
	
	
# Shop Stuff
func _on_shop_button_pressed():
	shop.visible = !shop.visible
	update_gold_display()  # Pass current gold to shop
	piece_container.visible = !piece_container.visible
	shadow_container.visible = !shadow_container.visible
	tile_container.visible = !tile_container.visible

func _on_purchase_attempted(item_data: Dictionary, shop_item: Node):
	# Check gold here where we have access to the real value
	if player_gold >= item_data["price"]:
		# Deduct gold and mark the item as purchased
		player_gold -= item_data["price"]
		item_data["purchased"] = true

		
		# Show "Sold Out" overlay and disable button
		shop_item.get_node("MarginContainer/VBoxContainer/SoldOutLabel").visible = true
		shop_item.get_node("Overlay").visible = true
		shop_item.get_node("MarginContainer/VBoxContainer/BuyButton").disabled = true

		# Creates an array of piece / upgrade names.
		var piece_names = shop.shop_items["pieces"].map(func(item): return item["name"])
		var upgrade_names = shop.shop_items["upgrades"].map(func(item): return item["name"])
		
		# checks if the purchased item’s name exists in the list of pieces or upgrades.
		if item_data["name"] in piece_names:
			print("Purchased piece: ", item_data["name"])
			purchased_pieces.append(item_data)
		elif item_data["name"] in upgrade_names:
			print("Purchased upgrade: ", item_data["name"])
			purchased_upgrades.append(item_data)

		# Update UI and print debug info
		update_gold_display()
	else:
		# TODO: Play sound / animation for not enough gold
		print("Not enough gold!")

func _on_gold_spent(amount: int):
	player_gold -= amount
	gold_display.text = "Gold: %d" % player_gold  # Update the display
	print("Gold spent: %d. Remaining gold: %d" % [amount, player_gold])

func update_gold_display():
	gold_display.text = "Gold: %d" % player_gold

func _on_next_round_requested():
	print("Next round requested!")
	shop.visible = false  # Hide the shop
	piece_container.visible = true  # Show the board
	shadow_container.visible = true
	tile_container.visible = true
	print("Player items in purchased_pieces: ", purchased_pieces)
	print("Player upgrades in purchased_upgrades: ", purchased_upgrades)

func set_gold_display_position():
	# Set anchors to a percentage of the parent container's size
	gold_display.anchor_left = 0.2
	gold_display.anchor_top = 0.0
	gold_display.anchor_right = 1.0
	gold_display.anchor_bottom = 0.1

func _on_viewport_size_changed():
	if shop:
		var viewport_size = get_viewport().get_visible_rect().size
		shop.position = viewport_size / 2