extends Node
class_name PuzzleManager

@onready var letterManager : LetterManager = $"../LetterManager"
@onready var wordLibrary : WordLibrary = $"../WordLibrary"
@onready var hintLabel : Label = $"../PuzzleHint"
@onready var submitButton = $"../LetterManager/SubmitButton"
@onready var inputManager = $"../InputManager"
@onready var foundWordsLabel = $"../FoundWords"
@onready var hintButton = $"../HintButton"
@onready var tutorialPanel0 = $"../TutorialPanel_0"
@onready var victoryPanel = $"../VictoryPanel"
@onready var bonusWords = $"../BonusWords"
@onready var audioSource = $"../AudioSource" as AudioStreamPlayer2D

var incorrectSound : AudioStream = preload("res://Sounds/IncorrectWord.wav") as AudioStream
var correctSound : AudioStream = preload("res://Sounds/CorrectWord.wav") as AudioStream
var levelCompleteSound : AudioStream = preload("res://Sounds/LevelComplete.wav") as AudioStream

var wordsInCurrentPuzzle : Array = []
var foundWords : Array = []

var currentHintWord = ""
var currentHintLetterCount = 0

var LevelNumber = 1

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	var level = load("res://Levels/Level%s.tres" % LevelNumber) as LevelDefinition
	load_level(level)
	inputManager.waiting_for_input.connect(on_input_manager_returning_to_idle)
	hintButton.visible = false
	update_submit_button_validity()
	victoryPanel.visible = false
	
func letter_count_in_puzzle() -> int:
	return wordsInCurrentPuzzle[0].length()

func add_words_to_puzzle(words: Array[String]) -> void:
	wordsInCurrentPuzzle = words

	# iterate over every letter in the word
	for col in letter_count_in_puzzle():
		var lettersToAdd = MarbleBag.new()
		for word in wordsInCurrentPuzzle:
			# if the word is long enough, add the letter at the appropriate position
			if col < word.length():
				lettersToAdd.add([word[col]])

		# now empty out the marble bag
		for row in range(0, wordsInCurrentPuzzle.size()):
			letterManager.create_letter_at_position(lettersToAdd.next(), LogicalPosition.new(col, row))

		lettersToAdd.clear()

func advance_to_next_level() -> void:
	foundWords = []
	foundWordsLabel.text = ""

	bonusWords.text = ""

	letterManager.clear_all_hints()
	hintButton.visible = false

	LevelNumber += 1

	if (LevelNumber < 8):
		# we are out of levels
		# display victory screen
		victoryPanel.visible = true
		inputManager.transition_to_state(InputStrategy.State.HALT)
		return
	
	var level = load("res://Levels/Level%s.tres" % LevelNumber) as LevelDefinition
	load_level(level)

func load_level(level : LevelDefinition) -> void:
	tutorialPanel0.visible = level.TutorialVisible

	# Set the hint
	hintLabel.text = level.Hint

	# set up the words
	letterManager.set_logical_dimensions(level.Words[0].length(), level.Words.size())
	add_words_to_puzzle(level.Words)
	
func update_submit_button_validity():
	submitButton.visible = (wordLibrary.is_valid_word(letterManager.get_current_top_word()) or letterManager.get_current_top_word() in wordsInCurrentPuzzle) and (letterManager.get_current_top_word() not in foundWords)

func on_input_manager_returning_to_idle() -> void:
	if (wordsInCurrentPuzzle.size() == 0):
		advance_to_next_level()
	else:
		update_submit_button_validity()

func _on_submit_button_pressed() -> void:
	var submittedWord = letterManager.get_current_top_word()
	if (submittedWord in wordsInCurrentPuzzle):
		wordsInCurrentPuzzle.erase(submittedWord)
		foundWordsLabel.text = foundWordsLabel.text + submittedWord + " "
		foundWords.append(submittedWord)

		currentHintWord = ""
		currentHintLetterCount = 0

		if (wordsInCurrentPuzzle.size() == 0):
			# we have found all the words
			audioSource.stream = levelCompleteSound
		else:
			audioSource.stream = correctSound
		audioSource.play()


		letterManager.clear_top_row()
		update_submit_button_validity()
	else:
		if (wordLibrary.is_valid_word(submittedWord)) and (submittedWord not in foundWords):
			foundWords.append(submittedWord)
			add_bonus_word(submittedWord)
			hintButton.visible = true
			update_submit_button_validity()
			audioSource.stream = incorrectSound 
			audioSource.play()


func _on_hint_button_pressed() -> void:
	hintButton.visible = false
	choose_new_hint_word()

	letterManager.clear_all_hints()

	for col in range(0, currentHintLetterCount):
		letterManager.set_hint_for_column(currentHintWord[col], col)

func choose_new_hint_word() -> void:
	for i in range(1, letter_count_in_puzzle()):
		var count = number_of_words_that_match_letters_up_to(i)
		if (count >= 1):
			var hintStart = letterManager.get_current_top_word().substr(0, i)
			currentHintWord = puzzle_word_that_starts_with(hintStart)
			
			currentHintLetterCount = letter_count_in_puzzle() - 1
			# if all but the last letter match, then we should just give you all the letters
			if (currentHintWord.substr(0, currentHintLetterCount) == letterManager.get_current_top_word().substr(0, currentHintLetterCount)):
				currentHintLetterCount = letter_count_in_puzzle()

			return

func puzzle_word_that_starts_with(start : String) -> String:
	for word in wordsInCurrentPuzzle:
		if word.begins_with(start):
			return word
	return wordsInCurrentPuzzle[0]

func number_of_words_that_match_letters_up_to(charCount : int) -> int:
	var result = 0
	var matchSet = letterManager.get_current_top_word().substr(0, charCount)

	for word in wordsInCurrentPuzzle:
		if word.begins_with(matchSet):
			result += 1

	return result
		
func add_bonus_word(word : String) -> void:
	if (bonusWords.text == ""):
		bonusWords.text = "Extra Words: " + word + " "
	else:
		bonusWords.text = bonusWords.text + word + " "