class_name CardBoard extends PanelContainer

#var area_dict = {
#    "dropzone_content": [],
#    "cards": [],
#    "sticky_notes_in_list": [],
#    "sticky_note_panels": []
#}
enum {NAVIGATE, ASSIGN, DRAG}

var focus_stickies:bool = true:
	set(stickies):
		if stickies and sticky_note_container.get_child_count() == 0: return
		
		focus_stickies = stickies
		
		if not current_context == ASSIGN:
			if stickies:
				current_sticky_note_id = current_sticky_note_id
			else:
				current_dropzone_id = current_dropzone_id    

var has_stage = false:
	set(focus):
		if focus:
			has_stage = true
			get_tree().call_group("interactables", "collapse")
		else:
			has_stage = false
		if is_node_ready():
			if focus:
				process_mode = Node.PROCESS_MODE_INHERIT
			else:
				process_mode = Node.PROCESS_MODE_DISABLED
		visible = has_stage

@onready var dropzone = $HBoxContainer/dropzone
var dropzone_size: Vector2
@export var dropzone_padding = 100
@onready var sticky_note_container = $HBoxContainer/ScrollContainer/VBoxContainer
@onready var board_of_devs = $"board of devs"
var base_sticky_note_panel: Panel
@onready var current_context:int = NAVIGATE:
	set(context):
		if current_context == ASSIGN and !context == ASSIGN:
			sticky_note_container.get_child(current_sticky_note_id).clear_if_empty()
		#match context:
		#    NAVIGATE:
		#        _return_sticky_notes_to_panels()
		#    DRAG:
		#        pass
		#    ASSIGN:
		#        pass
		current_context = context
@onready var instructions = $instructions_panel/HBoxContainer/cards_remaining

var mementos_collected: int = 0:
	set(mementos):
		mementos_collected = mementos
		match mementos:
			1:
				instructions.text = "There are three Mementos left to find."
			2:
				instructions.text = "You have collected half of the mementos."
			3:
				instructions.text = "Find the last Memento to complete the Board."
			4:
				instructions.text = "Combine cards to order your thoughts."

@onready var currently_active_node: Area2D = null:
	set(new_node):
		if not currently_active_node == null:
			currently_active_node.highlighted = false
		currently_active_node = new_node
		if not currently_active_node == null:
			currently_active_node.highlighted = true

@onready var current_dropzone_id: int = 0:
	set(new_id):
		if new_id > dropzone.get_child_count() - 1: current_dropzone_id = 0
		elif new_id < 0: current_dropzone_id = dropzone.get_child_count() - 1
		else: current_dropzone_id = new_id
		if current_context == ASSIGN:
			while not dropzone.get_child(current_dropzone_id) is Card: current_dropzone_id = (current_dropzone_id + 1) % dropzone.get_child_count()
			dropzone.get_child(current_dropzone_id).preview_sticky_note(currently_active_node)

		elif not focus_stickies:
			currently_active_node = dropzone.get_child(current_dropzone_id)
		
@onready var current_sticky_note_id: int = 0:
	set(new_id):
		if sticky_note_container.get_child_count() <= 1: return
		elif new_id > sticky_note_container.get_child_count() - 1: current_sticky_note_id = 0
		elif new_id < 0: current_sticky_note_id = sticky_note_container.get_child_count() - 1
		else: current_sticky_note_id = new_id
		if current_context == ASSIGN:
			_return_sticky_notes_to_panels()
			currently_active_node.preview_sticky_note(sticky_note_container.get_child(current_sticky_note_id).attached_sticky_note)
		else:
			currently_active_node = sticky_note_container.get_child(current_sticky_note_id).get_child(1)

var cache: Array = []

signal board_completed

# Called when the node enters the scene tree for the first time.
func _ready():
	base_sticky_note_panel = $HBoxContainer/ScrollContainer/VBoxContainer/Panel    
	sticky_note_container.remove_child(base_sticky_note_panel)
	
	dropzone_size = get_viewport_rect().size - Vector2(dropzone_padding + base_sticky_note_panel.custom_minimum_size.x, dropzone_padding)
	
	if get_parent() == get_tree().root:
		populate_board(["c_void", 'c_joy', "p_wet", "p_thomas"])
		populate_board(["c_fighting", 'c_hit', "p_girly", "p_vent"])
		mementos_collected = 2

	has_stage = has_stage

#func _process(delta):
#    # drops dragged area when Mouse is no longer pressed.
#    if has_stage and !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) and current_context == DRAGGING:
#        currently_active_node.is_dragged = false

# Will be used later to spawn Cards and Post-Its and remember them in the dictionary 
func populate_board(card_names: Array[String]):
	mementos_collected += 1
	
	var all_new:Dictionary = board_of_devs.get_cards_by_name_array(card_names)
	
	var new_cards:Array = all_new["cards"]
	var new_sticky_notes:Array = all_new["sticky_notes"]
	
	# spawning the cards and adding them to the dictionary
	for new_card in all_new["cards"]:
		add_card(new_card)
	for new_sticky_note in all_new["sticky_notes"]:   # spawning a sticky note
		add_sticky_note(new_sticky_note)
	
	#currently_active_node = area_dict["dropzone_content"][0]  # set first Card as currently selected node by default
	currently_active_node = dropzone.get_child(0)

func add_card(card: Card):
	card.reparent(self)
	card.position = Vector2(randi_range(dropzone_padding, dropzone_size.x), randi_range(dropzone_padding, dropzone_size.y))
	insert_area(dropzone, card)
	card.set_owner(self)
	card.is_dragable = true
	
func add_sticky_note(sticky: StickyNote):
	var new_panel = base_sticky_note_panel.duplicate()
	sticky_note_container.add_child(new_panel)
	new_panel.set_owner(self)
	new_panel.attatch_sticky_note(sticky, false)
	
# Checks if a Node is currently inside the dropzone
func is_in_dropzone(to_check: Node) -> bool:
	return dropzone.get_rect().has_point(to_check.global_position)

# called if a mouse button is pressed
func handle_mouse_button(to_handle: Area2D, input: InputEvent):
	# No two areas can be dragged at the same time.
	# Make sure that only the same area is dragged.
	# Otherwise overlapping areas are dragged at the same time. 
	if current_context == DRAG and to_handle != currently_active_node:
		return
	
	currently_active_node = to_handle  # update currently selected
	if input.is_action_pressed("mouse_left"):
		to_handle.is_dragged = true
		if to_handle is StickyNote:
			if not to_handle.on_board:
				to_handle.reparent(dropzone)
				to_handle.on_board = true
				to_handle.owner = self
		current_context = DRAG
	
	# when Drag stops ...
	if input.is_action_released("mouse_left"):
		to_handle.is_dragged = false
		if to_handle is StickyNote:
			if is_in_dropzone(to_handle):
				if to_handle.has_overlapping_areas():
					for area in to_handle.get_overlapping_areas():
						if area is Card:
							focus_stickies = false
							if area.has_sticky_note_attached():
								to_handle = area.exchange_sticky_note_with(to_handle)
								to_handle.reparent(dropzone)
								to_handle.on_board = true
								sticky_note_container.get_child(current_sticky_note_id).attached_sticky_note = to_handle
								to_handle.attached_to = sticky_note_container.get_child(current_sticky_note_id)
								to_handle.reset_drag()
								current_context = NAVIGATE
								return
							else:
								area.attach_sticky_note(to_handle)
								if not sticky_note_container.get_child_count() == 0:
									sticky_note_container.get_child(current_sticky_note_id).clear_if_empty()
								current_context = NAVIGATE
								if is_board_complete(): emit_signal("board_completed")
								return
			else:
				_return_sticky_notes_to_panels()
				return
		
		## Dropping Cards and Sticky Notes not causing a return condition above.
		insert_area(dropzone, to_handle)
		current_context = NAVIGATE
		focus_stickies = false
		current_dropzone_id = dropzone.get_children().find(to_handle)
		if to_handle is StickyNote:
			to_handle.rotation = to_handle.base_rotation
			to_handle.scale = to_handle.base_scale

		if input.is_action_pressed("mouse_right") and current_context == DRAG:
			to_handle.reset_drag()

func _return_sticky_notes_to_panels():
	for panel in sticky_note_container.get_children():
		panel.reclaim_sticky_note()
	
func is_board_complete() -> bool:
	if mementos_collected == 4:
		for card in dropzone.get_children():
			if card is Card:
				if not card.has_sticky_note_attached():
					return false
		return true
	return false

func is_board_lore() -> bool:
	for card in dropzone.get_children():
		if card.has_sticky_note_attached():
			if not card.current_sticky_note.is_in_group(card.name): return false
	return true
		
# Mark area that was hovered over as currently selected
func handle_hover(to_handle: Area2D):
	if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): return
	currently_active_node = to_handle
	
	if is_in_dropzone(to_handle) or to_handle is Card:
		if not (to_handle is StickyNote and !to_handle.on_board):
			current_dropzone_id = dropzone.get_children().find(to_handle)
		focus_stickies = false
	else:
		current_sticky_note_id = sticky_note_container.get_children().find(to_handle.attached_to)
		focus_stickies = true
		
# Adds a child at the correct child indext in an area
func insert_area(parent: Control, node: Area2D):
	var children:Array = parent.get_children()    
	var i = 0
	
	if not node in parent.get_children():
		node.reparent(parent)
		if node is StickyNote:
			node.on_board = true
		node.owner = self
	
	if children.size() > 0:
		children.erase(node)
		while children[i].global_position.y < node.global_position.y and i+1 < children.size():
			i+=1    
		parent.move_child(node, i)
	
# Takes the inputs for control inputs
func _input(event):
	
	if event.is_action_pressed("ui_cancel"):
		State.leave_stage(self)
	
	# Return, if the input is a mouse event (mouse events are handled separately)
	if event is InputEventMouse or !has_stage or not is_instance_valid(currently_active_node): return
	
	if current_context != DRAG:    
		if event.is_action_pressed("ui_up"):
			if focus_stickies:
				current_sticky_note_id -= 1
			else:
				current_dropzone_id -= 1
	
		elif event.is_action_pressed("ui_down"):       # down to select an element beneath
			if focus_stickies:
				current_sticky_note_id += 1
			else:
				current_dropzone_id += 1
				
		elif event.is_action_pressed("ui_right"):        # left to switch context to the left
			if not focus_stickies:
				if current_context == NAVIGATE:
					focus_stickies = true
				elif current_context == ASSIGN:
					current_context = NAVIGATE
			
		elif event.is_action_pressed("ui_left"):       # right to switch context to the right
			if focus_stickies:
				if current_context == NAVIGATE:
					focus_stickies = false
				elif current_context == ASSIGN:
					current_context = NAVIGATE
			
		elif event.is_action_pressed("ui_accept"):              # select the selected note it
			var card:Card
			if dropzone.get_child(current_dropzone_id) is Card:
				card = dropzone.get_child(current_dropzone_id)
			if current_context == ASSIGN:     # to assign it to a card
				if card.has_sticky_note_attached():
					currently_active_node = card.exchange_sticky_note_with(currently_active_node)
					current_dropzone_id = find_first_free_card()
				else:
					card.attach_sticky_note(sticky_note_container.get_child(current_sticky_note_id).attached_sticky_note)
					current_context = NAVIGATE
					focus_stickies = false
					if is_board_complete(): emit_signal("board_completed")
			else:
				if !focus_stickies and card.has_sticky_note_attached():
					currently_active_node = card.remove_sticky_note()
					add_sticky_note(currently_active_node)
					current_dropzone_id = -1
				else: current_dropzone_id = find_first_free_card()
				
				current_context = ASSIGN
				focus_stickies = !focus_stickies
				if focus_stickies:
					current_sticky_note_id = current_sticky_note_id
				else:
					current_dropzone_id = current_dropzone_id
				

# move the note it so it floats next to the card where it should be attached
func _select_card_for_assigning(sticky_note: Area2D, card: Area2D):
	sticky_note.tween_transform_to(card.get_child(3).global_position)

func on_scene_skipped(i: int):
	mementos_collected += i

func claim_focus():
	State.pass_stage_to(self)

func find_first_free_card() -> int:
	for i in range(dropzone.get_child_count()):
		# start searching at the current location, use modulo to avoid getting out of array bounds
		if !dropzone.get_child((i+current_dropzone_id)%dropzone.get_child_count()).has_sticky_note_attached():
			return (i+current_dropzone_id)%dropzone.get_child_count()
	return -1

func on_sticky_panel_cleared():
	if current_sticky_note_id == sticky_note_container.get_child_count() - 1:
		current_sticky_note_id -= 1


func get_save_dict() -> Dictionary:
	var cards: Dictionary = {}
	var stickies: Dictionary = {}
	
	for child in dropzone.get_children():
		if child is Card:
			if child.has_sticky_note_attached():
				stickies[child.get_attached_sticky_note().name] = child.name
			cards[child.name] = child.transform.origin
		elif child is StickyNote:
			cards[child.name] = child.transform.origin
	
	for child in sticky_note_container.get_children():
		if child is PostItPanel:
			stickies[child.attached_sticky_note.name] = -1
	return {
		"cards": cards,
		"stickies": stickies
	} if cards != {} and stickies != {} else {}

func initialise_from_save(savegame: SaveGame):
	if savegame.board_state == {}: return
	var cards: Dictionary = savegame.board_state["cards"]
	var stickies: Dictionary = savegame.board_state["stickies"]
	
	var card_pile = board_of_devs.get_cards_by_name_array(cards.keys() + (stickies.keys()))
	
	for card:Card in card_pile["cards"]:
		add_card(card)
		card.transform.origin = cards[card.name]
		cards[card.name] = card
	for sticky:StickyNote in card_pile["sticky_notes"]:
		if stickies[sticky.name] == -1:
			add_sticky_note(sticky)
		if stickies[sticky.name] is String:
			cards[stickies[sticky.name]].attach_sticky_note(sticky)
		else:
			insert_area(dropzone, sticky)
			sticky.transform.origin = stickies[sticky.name]
