extends Node
class_name game

@export var X : int
@export var Y : int
@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 = []


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

var target_position
var moving: bool
var sprite: Sprite2D

@onready var explosion_effect = $Explosion


func _ready() -> void:
	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(X): 
		board.append([])
		for y in range(Y): 
			board[x].append(null)
	
	
	#hard-coded setup of pieces
	board[0][7] = Rook.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][7] = Pawn.new(true, Vector2(4, 7))
	board[5][7] = Knight.new(true, Vector2(5, 7))
	board[6][7] = Pawn.new(true, Vector2(6, 7))
	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] = Pawn.new(false, Vector2(4, 4))
	board[3][4] = Pawn.new(false, Vector2(3, 4))
	board[4][3] = Pawn.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(X,Y)
	
	#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()


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(X):
		for y in range(Y):
			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")
				
				if piece_scene:
					var piece_instance = piece_scene.instantiate()
					piece_instance.position = Vector2((x * tile_size) + tile_size/2, (y * tile_size) + tile_size/2)
					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 sprite = piece_instance.get_node("Sprite2D") as Sprite2D
						if sprite:
							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 = X * tile_size / 2
	var board_y_offset = Y * 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(X, Y):
	for x in range(X):
		for y in range(Y):
			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 screen_size = get_viewport().get_visible_rect().size
			var board_x_offset = X * tile_size / 2
			var board_y_offset = Y * tile_size / 2
			
			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(X, Y):
	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(X):
		for y in range(Y):
			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 screen_size = get_viewport().get_visible_rect().size
				var board_x_offset = X * tile_size / 2
				var board_y_offset = Y * tile_size / 2
				
				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 < (X * tile_size) && position.x > 0:
		if position.y < (Y * 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 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)


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_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
	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()
			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()
	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 tile has opponent piece, remove piece
			if is_opponent(x,y):
				print("Capturing opponent piece")
				remove_piece(x,y)
				
			#clear the selected_piece's previous position
			board[selected_piece_position.x][selected_piece_position.y] = null

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

			board[x][y] = selected_piece_value
			
			target_position = selected_piece_position
			moving = true
			valid_moves = []
			
		
		else:
			unselect_piece()
		
	remove_highlight()