extends Node

var panel_mapping = []
var painting_mapping = []
var steady_mapping = []
var gen_seed
var is_set_seed = false

var _gen_forms
var _gen_paintings
var _gen_wanderlust
var _gen_addition
var _gen_walls
var _gen_combos

var _answer_by_id
var _answers_by_type
var _answers_by_length
var _puzzle_by_id
var _rng
var _flags = 0
var _total_generated = 0
var _hint_by_path = {}
var _answer_by_path = {}
var _painting_puzzles = []
var _chain_cache = {}
var _short_answer_length = 4

var _unique_pool = ""
var _answers_by_unique_pool = {}
var _needs_reuse = []
var _possible_reuse = []

var _cur_panel = ""

const kOrangeMiddle = -2
const kWhiteMiddle = -1
const kWhiteTop = 0
const kWhiteBottom = 1
const kYellowTop = 2
const kYellowMiddle = 3
const kBlackTop = 4
const kBlackMiddle = 5
const kBlackBottom = 6
const kDoubleBlackBottom = 7
const kRedTop = 8
const kRedMiddle = 9
const kRedBottom = 10
const kBlueTop = 11
const kBlueMiddle = 12
const kBlueBottom = 13
const kPurpleTop = 14
const kPurpleMiddle = 15
const kColorIs = 16

const kPuzzleTypes = [
	kWhiteMiddle,
	kWhiteTop,
	kWhiteBottom,
	kYellowTop,
	kYellowMiddle,
	kBlackTop,
	kBlackMiddle,
	kBlackBottom,
	kDoubleBlackBottom,
	kRedTop,
	kRedMiddle,
	kRedBottom,
	kBlueTop,
	kBlueMiddle,
	kBlueBottom,
	kPurpleTop,
	kPurpleMiddle,
	kColorIs
]

const kMustReuseAnswer = 1
const kObscureHint = 2
const kShortAnswer = 4
const kForcePalindrome = 8

signal generator_status


func _init(gen_puzzles):
	_gen_forms = gen_puzzles[0]
	_gen_paintings = gen_puzzles[1]
	_gen_wanderlust = gen_puzzles[2]
	_gen_addition = gen_puzzles[3]
	_gen_walls = gen_puzzles[4]
	_gen_combos = gen_puzzles[5]


func generate(chosen_seed):
	emit_signal("generator_status", "Generating puzzles...")

	_flags = 0
	_total_generated = 0

	# Seed the random number generator.
	if chosen_seed == "":
		var seed_rng = RandomNumberGenerator.new()
		seed_rng.randomize()
		gen_seed = seed_rng.randi_range(0, 999999)
		is_set_seed = false
	else:
		gen_seed = int(chosen_seed)
		is_set_seed = true

	_rng = RandomNumberGenerator.new()
	_rng.seed = gen_seed

	# Create some helpful indexes.
	_answer_by_id = []
	_answers_by_type = {}
	_answers_by_length = {}
	_puzzle_by_id = []
	_hint_by_path = {}
	_answer_by_path = {}
	_painting_puzzles = _gen_paintings
	var openable_answers = []

	for puzzle_type in kPuzzleTypes:
		_answers_by_type[puzzle_type] = []

	var puzzle_id = 0
	for puzzle in _gen_forms:
		_answers_by_type[kWhiteMiddle].append(puzzle_id)

		match typeof(puzzle):
			TYPE_ARRAY:
				_answer_by_id.append(puzzle[0])
				if not _answers_by_length.has(puzzle[0].length()):
					_answers_by_length[puzzle[0].length()] = []
				_answers_by_length[puzzle[0].length()].append(puzzle_id)
				_puzzle_by_id.append(puzzle)

				for puzzle_type in puzzle[1].keys():
					_answers_by_type[puzzle_type].append(puzzle_id)

				if (
					puzzle[1].has(kYellowMiddle)
					and puzzle[1].has(kBlackBottom)
					and puzzle[1].has(kRedMiddle)
					and puzzle[1].has(kBlueMiddle)
					and puzzle[1].has(kPurpleMiddle)
				):
					openable_answers.append(puzzle_id)
			TYPE_STRING:
				_answer_by_id.append(puzzle)
				if not _answers_by_length.has(puzzle.length()):
					_answers_by_length[puzzle.length()] = []
				_answers_by_length[puzzle.length()].append(puzzle_id)
				_puzzle_by_id.append([puzzle, []])

		puzzle_id += 1

	# Choose the answer for the OPEN panels.
	# There is a hedge maze OPEN but we can't randomize those yet.
	var open_answer_id = _pickRandom(openable_answers)

	_setUniquePool("agreeable")
	_setPanel(
		"Antonym Room/Panel_close_open",
		_pickRandom(_puzzle_by_id[open_answer_id][1][kBlackBottom]),
		open_answer_id
	)

	_setUniquePool("seeker")
	_setFlag(kObscureHint)
	_setPanel("Heteronym Room/Panel_entrance_entrance", open_answer_id, open_answer_id)
	_clearFlag(kObscureHint)

	_setUniquePool("traveled")
	_setPanel("Synonym Room/Panel_open_open", open_answer_id, open_answer_id)

	_setUniquePool("discerning")
	_setPanel(
		"Sun Room/Panel_nope_open",
		_pickRandom(_puzzle_by_id[open_answer_id][1][kYellowMiddle]),
		open_answer_id
	)

	_setUniquePool("bold")
	_setPanel(
		"Truncate Room/Panel_unopened_open",
		_pickRandom(_puzzle_by_id[open_answer_id][1][kRedMiddle]),
		open_answer_id
	)

	_setUniquePool("undeterred")
	_setPanel(
		"Blue Room/Panel_pen_open",
		_pickRandom(_puzzle_by_id[open_answer_id][1][kBlueMiddle]),
		open_answer_id
	)

	_setUniquePool("initiated")
	_setPanel(
		"Rhyme Room/Panel_locked_knocked",
		_pickRandom(_puzzle_by_id[open_answer_id][1][kPurpleMiddle]),
		open_answer_id
	)

	_setUniquePool("scientific")
	_setPanel("Chemistry Room/Panel_open", open_answer_id, open_answer_id)
	_setPanel(
		"Chemistry Room/Panel_close",
		_pickRandom(_puzzle_by_id[open_answer_id][1][kBlackBottom]),
		open_answer_id
	)

	_setUniquePool("agreeable")
	_gSingle("Antonym Room/Panel_retool_looter", kBlackMiddle)
	_gSingle("Antonym Room/Panel_drawer_reward", kBlackMiddle)
	_gSingle("Antonym Room/Panel_stressed_desserts", kBlackMiddle)
	_gSingle("Antonym Room/Panel_star_rats", kBlackMiddle)

	_setUniquePool("seeker")
	#_setFlag(kObscureHint)
	_gSingle("Heteronym Room/Panel_bear_bear", kWhiteMiddle)
	_gSingle("Heteronym Room/Panel_bow_bow", kWhiteMiddle)
	_gSingle("Heteronym Room/Panel_does_does", kWhiteMiddle)
	_gDouble("Heteronym Room/Panel_mine_mine", "Heteronym Room/Panel_mine_mine_2", kWhiteMiddle)
	_gDouble(
		"Heteronym Room/Panel_mobile_mobile", "Heteronym Room/Panel_mobile_mobile_2", kWhiteMiddle
	)
	_gStack2(
		"Heteronym Room/Panel_desert_dessert",
		kWhiteTop,
		"Heteronym Room/Panel_desert_desert",
		kWhiteMiddle
	)
	_gStack2("Heteronym Room/Panel_sow_so", kWhiteTop, "Heteronym Room/Panel_sow_sow", kWhiteMiddle)
	#_clearFlag(kObscureHint)

	_setUniquePool("tenacious")
	_gSingle("Palindrome Room/Panel_slaughter_laughter", kRedMiddle)
	_gSingle("Palindrome Room/Panel_dread_dead", kRedMiddle)
	_gSingle("Palindrome Room/Panel_massacred_sacred", kRedMiddle)
	_gSingle("Palindrome Room/Panel_decay_day", kRedMiddle)

	_setFlag(kForcePalindrome)
	_gSingle("Palindrome Room/Panel_solos_solos", kWhiteMiddle)
	_gSingle("Palindrome Room/Panel_racecar_racecar", kWhiteMiddle)
	_gSingle("Palindrome Room/Panel_level_level", kWhiteMiddle)
	_gCopyHidden("Palindrome Room/Panel_solos_solos", "Palindrome Room/Panel_solos_solos_2")
	_gCopyHidden("Palindrome Room/Panel_racecar_racecar", "Palindrome Room/Panel_racecar_racecar_2")
	_gCopyHidden("Palindrome Room/Panel_level_level", "Palindrome Room/Panel_level_level_2")
	_clearFlag(kForcePalindrome)

	_setUniquePool("discerning")
	_gSingle("Sun Room/Panel_hits_this", kYellowMiddle)
	_gSingle("Sun Room/Panel_adder_dread", kYellowMiddle)
	_gSingle("Sun Room/Panel_laughters_slaughter", kYellowMiddle)
	_gSingle("Sun Room/Panel_rat_art", kYellowMiddle)
	_gSingle("Sun Room/Panel_dusty_study", kYellowMiddle)
	_gSingle("Sun Room/Panel_state_taste", kYellowMiddle)
	_gSingle("Sun Room/Panel_react_trace", kYellowMiddle)
	_gSingle("Sun Room/Panel_seam_same", kYellowMiddle)
	_gDouble("Sun Room/Panel_warred_drawer", "Sun Room/Panel_redraw_drawer", kYellowMiddle)
	_gDouble("Sun Room/Panel_stone_notes", "Sun Room/Panel_onset_notes", kYellowMiddle)
	_gDouble("Sun Room/Panel_arts_star", "Sun Room/Panel_tsar_star", kYellowMiddle)
	_gDouble("Sun Room/Panel_dear_read", "Sun Room/Panel_dare_read", kYellowMiddle)

	_setUniquePool("bearer")
	_setFlag(kShortAnswer)
	_short_answer_length = 6
	_gChain("Cross Room/Panel_part_rap", [kRedMiddle, kYellowMiddle])
	_gChain("Cross Room/Panel_heart_tar", [kRedMiddle, kYellowMiddle])
	_gChain("Cross Room/Panel_smile_lime", [kRedMiddle, kYellowMiddle])
	_gChain("Cross Room/Panel_snow_won", [kRedMiddle, kYellowMiddle])
	_gChain("Cross Room/Panel_warts_star", [kRedMiddle, kYellowMiddle])
	_gChain("Cross Room/Panel_pots_top", [kYellowMiddle, kRedMiddle])
	_gChain("Cross Room/Panel_silent_list", [kRedMiddle, kYellowMiddle])
	_gChain("Cross Room/Panel_silent_list_2", [kYellowMiddle, kRedMiddle])
	_gChain("Cross Room/Panel_tent_net", [kRedMiddle, kYellowMiddle])
	_gChain("Cross Room/Panel_peace_ape", [kRedMiddle, kYellowMiddle])
	_gChain("Cross Room/Panel_space_cape", [kRedMiddle, kYellowMiddle])
	_gChain("Cross Room/Panel_bowl_low", [kRedMiddle, kYellowMiddle])
	_clearFlag(kShortAnswer)

	_setUniquePool("bold")
	_gSingle("Truncate Room/Panel_sushi_hi", kRedMiddle)
	_gSingle("Truncate Room/Panel_thistle_this", kRedMiddle)
	_gDouble("Truncate Room/Panel_undead_dead", "Truncate Room/Panel_deadline_dead", kRedMiddle)
	_gDouble("Truncate Room/Panel_landmass_mass", "Truncate Room/Panel_massacred_mass", kRedMiddle)

	_gSingle("Challenge Room/Panel_welcome_welcome", kWhiteMiddle)
	_writePanel("Entry Room/Panel_return_return", "%s back" % _answer_by_path["Challenge Room/Panel_welcome_welcome"], "%s back" % _answer_by_path["Challenge Room/Panel_welcome_welcome"])

	_setUniquePool("steady")
	_gSteady("Rock Room/Panel_limelight_lime", kRedMiddle)
	_gSteady("Rock Room/Panel_blue_blueberry", kBlueMiddle)
	_gSteady("Rock Room/Panel_lump_plum", kYellowMiddle)
	_gSteady("Rock Room/Panel_hairy_cherry", kBlueTop)
	_gSteady("Rock Room/Panel_blue_orange", kBlackBottom)
	_gSteady("Rock Room/Panel_melon_lemon", kYellowMiddle)
	_gSteady("Rock Room/Panel_herald_emerald", kPurpleMiddle)
	_gSteady("Rock Room/Panel_sap_sapphire", kBlueMiddle)
	_gSteady("Rock Room/Panel_thistle_amethyst", kPurpleTop)
	_gSteady("Rock Room/Panel_bury_ruby", kYellowMiddle)
	_gSteady("Rock Room/Panel_antechamber_amber", kRedMiddle)
	_gSteady("Rock Room/Panel_top_topaz", kBlueMiddle)
	_gSteady("Rock Room/Panel_lielack_lilac", kWhiteTop)
	_gSteady("Rock Room/Panel_soar_rose", kBlackTop)
	_gSteady("Rock Room/Panel_incarnation_carnation", kRedMiddle)
	_gSteady("Rock Room/Panel_sun_sunflower", kBlueMiddle)

	_clearUniquePool()

	_gPaired("Shuffle Room/Panel_sword_words", "Shuffle Room/Panel_words_sword", kYellowMiddle)
	_gPaired("Shuffle Room/Panel_turn_runt", "Shuffle Room/Panel_turn_runt2", kYellowMiddle)
	_gPaired("Shuffle Room/Panel_lost_lots", "Shuffle Room/Panel_lost_slot", kYellowMiddle)
	_gDouble("Shuffle Room/Panel_amen_mean", "Shuffle Room/Panel_name_mean", kYellowMiddle)
	_gSingle("Shuffle Room/Panel_eyes_see_shuffle", kYellowMiddle)
	_gSingle("Shuffle Room/Panel_behind", kYellowMiddle)
	_gPaired("Shuffle Room/Panel_left_left", "Shuffle Room/Panel_right_right", kWhiteMiddle)
	_gSingle("Shuffle Room/Panel_middle_middle", kWhiteMiddle)
	_gCopy("Shuffle Room/Panel_left_left", "Shuffle Room/Panel_left_left_2")
	_gCopy("Shuffle Room/Panel_right_right", "Shuffle Room/Panel_right_right_2")
	_gCopy("Shuffle Room/Panel_middle_middle", "Shuffle Room/Panel_middle_middle_2")
	_gSingle("Shuffle Room/Panel_shortcuts", kYellowMiddle)
	_gSingle("Shuffle Room/Panel_tower", kYellowMiddle)
	_gPaired("Shuffle Room/Panel_far_far", "Shuffle Room/Panel_near_near", kWhiteMiddle)

	_gSingle("Maze Room/Panel_reflow_flower", kYellowMiddle)

	_gSingle("Appendix Room/Panel_rat_tar", kBlackMiddle)
	
	_gSingle("Painting Room/Panel_eon_one", kYellowMiddle)
	_gSingle("Painting Room/Panel_to_two", kRedMiddle)
	_setFlag(kShortAnswer)
	_short_answer_length = 4
	_gSingle("Painting Room/Panel_an_many", kBlueMiddle)
	_gSingle("Painting Room/Panel_may_many", kBlueMiddle)
	_gSingle("Painting Room/Panel_any_many", kBlueMiddle)
	_gSingle("Painting Room/Panel_man_many", kBlueMiddle)
	_clearFlag(kShortAnswer)
	_gSingle("Painting Room/Panel_runts_turns", kYellowMiddle)
	_gOrangeAddition("Painting Room/Panel_send_use_turns")
	_gOrangeWord("Painting Room/Panel_trust_06890")
	_gOrangeNumber("Painting Room/Panel_06890_trust")
	_writePanel("Painting Room/Panel_order_onepathmanyturns", "order", "%s road %s %s" % [_answer_by_path["Painting Room/Panel_eon_one"], _answer_by_path["Painting Room/Panel_any_many"], _answer_by_path["Painting Room/Panel_send_use_turns"]])

	_gStack2("Ceiling Room/Panel_blue_top_1", kBlueTop, "Ceiling Room/Panel_red_bot_1", kRedBottom)
	_gStack2(
		"Ceiling Room/Panel_red_mid_2", kRedMiddle, "Ceiling Room/Panel_blue_bot_2", kBlueBottom
	)
	_gStack2(
		"Ceiling Room/Panel_blue_mid_3", kBlueMiddle, "Ceiling Room/Panel_red_bot_3", kRedBottom
	)
	_gStack2("Ceiling Room/Panel_red_top_4", kRedTop, "Ceiling Room/Panel_blue_mid_4", kBlueMiddle)
	_gStack2(
		"Ceiling Room/Panel_yellow_top_5", kYellowTop, "Ceiling Room/Panel_blue_bot_5", kBlueBottom
	)
	_gStack2(
		"Ceiling Room/Panel_blue_top_6", kBlueTop, "Ceiling Room/Panel_yellow_mid_6", kYellowMiddle
	)
	# gen_.GeneratePanelStack("", {}, "Panel_blue_mid_7", kBlue, "Panel_yellow_bot_7", kYellow);
	_gStack2(
		"Ceiling Room/Panel_yellow_mid_8",
		kYellowMiddle,
		"Ceiling Room/Panel_black_bot_8",
		kBlackBottom
	)
	# gen_.GeneratePanelStack("Panel_black_top_9", kBlack, "", {}, "Panel_yellow_bot_9", kYellow);
	_gStack2(
		"Ceiling Room/Panel_yellow_top_10",
		kYellowTop,
		"Ceiling Room/Panel_black_bot_10",
		kBlackBottom
	)
	_gStack2(
		"Ceiling Room/Panel_black_top_11",
		kBlackTop,
		"Ceiling Room/Panel_yellow_mid_11",
		kYellowMiddle
	)
	_gStack2(
		"Ceiling Room/Panel_black_top_12", kBlackTop, "Ceiling Room/Panel_red_bot_12", kRedBottom
	)
	_gStack2(
		"Ceiling Room/Panel_red_top_13", kRedTop, "Ceiling Room/Panel_black_bot_13", kBlackBottom
	)
	_gStack2(
		"Ceiling Room/Panel_black_mid_14", kBlackMiddle, "Ceiling Room/Panel_red_bot_14", kRedBottom
	)
	_gStack2(
		"Ceiling Room/Panel_black_top_15", kBlackTop, "Ceiling Room/Panel_red_mid_15", kRedMiddle
	)
	#gen_.GenerateSinglePanel("Panel_answer_1", kBottom, kRed);
	#_gSingle("Ceiling Room/Panel_answer_2", kBlackTop)
	_writePanel("Ceiling Room/Panel_answer_1", "article", "the")
	_writePanel("Ceiling Room/Panel_answer_2", "paints", "art")
	_setFlag(kShortAnswer)
	_short_answer_length = 4
	_gSingle("Ceiling Room/Panel_answer_3", kBlueMiddle)
	_clearFlag(kShortAnswer)
	_gSingle("Ceiling Room/Panel_answer_4", kBlackTop)
	_writePanel("Countdown Panels/Panel_artistic_artistic", "", "the art %s %s" % [_answer_by_path["Ceiling Room/Panel_answer_3"], _answer_by_path["Ceiling Room/Panel_answer_4"]])

	_gExactLength("Backside Room/Panel_one_one", kWhiteMiddle, 1)
	_gExactLength("Backside Room/Panel_two_two", kWhiteMiddle, 2)
	_gExactLength("Backside Room/Panel_two_two_2", kWhiteMiddle, 2)
	_gExactLength("Backside Room/Panel_three_three", kWhiteMiddle, 3)
	_gExactLength("Backside Room/Panel_three_three_2", kWhiteMiddle, 3)
	_gExactLength("Backside Room/Panel_three_three_3", kWhiteMiddle, 3)

	_gSingle("Color Arrow Room/Panel_red_near", kWhiteMiddle)
	_gSingle("Color Arrow Room/Panel_blue_near", kWhiteMiddle)
	_gSingle("Color Arrow Room/Panel_yellow_near", kWhiteMiddle)
	_gSingle("Color Arrow Room/Panel_orange_near", kWhiteMiddle)
	_gSingle("Color Arrow Room/Panel_purple_near", kWhiteMiddle)
	_gSingle("Color Arrow Room/Panel_green_near", kWhiteMiddle)
	_gCopy("Color Arrow Room/Panel_red_near", "Color Arrow Room/Panel_red_afar")
	_gCopy("Color Arrow Room/Panel_blue_near", "Color Arrow Room/Panel_blue_afar")
	_gCopy("Color Arrow Room/Panel_yellow_near", "Color Arrow Room/Panel_yellow_afar")
	_gCopy("Color Arrow Room/Panel_orange_near", "Color Arrow Room/Panel_orange_afar")
	_gCopy("Color Arrow Room/Panel_purple_near", "Color Arrow Room/Panel_purple_afar")
	_gCopy("Color Arrow Room/Panel_green_near", "Color Arrow Room/Panel_green_afar")
	#painting_mapping.append(["colors_painting", "hatkirby"])

	_gOrangeNumber("Orange Room/Panel_lust")
	_gOrangeNumber("Orange Room/Panel_read")
	_gOrangeNumber("Orange Room/Panel_sew")
	_gOrangeNumber("Orange Room/Panel_dead")
	_gOrangeNumber("Tower Room/Panel_834283054_undaunted")
	_gOrangeWord("Orange Room/Panel_learn")
	_gOrangeWord("Orange Room/Panel_dust")
	_gOrangeWord("Orange Room/Panel_star")
	_gOrangeWord("Orange Room/Panel_wander")
	_gOrangeWord("Tower Room/Panel_wanderlust_1234567890")
	_gOrangeAddition("Tower Room/Panel_dads_ale_dead_1")
	_gOrangeAddition("Tower Room/Panel_art_art_eat_2")
	_gOrangeAddition("Tower Room/Panel_deer_wren_rats_3")
	_gOrangeAddition("Tower Room/Panel_learns_unsew_unrest_4")
	_gOrangeAddition("Tower Room/Panel_drawl_runs_enter_5")
	_gOrangeAddition("Tower Room/Panel_reads_rust_lawns_6")
	_gOrangeAddition("Tower Room/Panel_waded_wee_warts_7")

	_gPaintingPuzzle("Panel Room/Panel_painting_flower", "flower_painting_gray")
	_gPaintingPuzzle("Panel Room/Panel_painting_eye", "eye_painting_gray")
	_gPaintingPuzzle("Panel Room/Panel_painting_snowman", "snowman_painting_gray")
	_gPaintingPuzzle("Panel Room/Panel_painting_owl", "owl_painting_gray")
	_gPaintingPuzzle("Panel Room/Panel_painting_panda", "panda_painting_gray")

	_gStack2("Open Areas/Panel_angered_enraged", kYellowMiddle, "", kWhiteBottom)
	_gStack2("Open Areas/Panel_vote_veto", kYellowMiddle, "", kBlackBottom)

	#_setFlag(kObscureHint)
	_gSingle("Entry Room/Panel_forward_forward", kWhiteMiddle)
	_gSingle("Entry Room/Panel_between_between", kWhiteMiddle)
	_gSingle("Entry Room/Panel_backward_backward", kWhiteMiddle)

	_gPaired("Symmetry Room/Panel_leaf_feel", "Symmetry Room/Panel_feel_leaf", kBlackTop)
	_gPaired("Symmetry Room/Panel_warts_straw", "Symmetry Room/Panel_straw_warts", kBlackMiddle)
	_gPaired("Symmetry Room/Panel_near_far", "Symmetry Room/Panel_far_near", kBlackBottom)
	_gPaired("Symmetry Room/Panel_left_right", "Symmetry Room/Panel_right_left", kBlackBottom)

	_gSingle("Shuffle Room/Panel_theeyes_theeyes", kWhiteMiddle)
	_gSingle("Shuffle Room/Panel_crossroads_crossroads", kWhiteMiddle)
	_gSingle("Shuffle Room/Panel_corner_corner", kWhiteMiddle)
	_gSingle("Shuffle Room/Panel_hollow_hollow", kWhiteMiddle)

	_gSingle("Maze Room/Panel_trace_trace", kWhiteMiddle)

	_gSingle("Appendix Room/Panel_deadend_deadend", kWhiteMiddle)
	_gSingle("Appendix Room/Panel_lies_lies", kWhiteMiddle)

	_gSingle("Cross Room/Panel_shortcut_shortcut", kWhiteMiddle)

	_gSingle("Double Room/Panel_leap_leap", kWhiteMiddle)

	_gPaired("Appendix Room/Panel_left_left", "Appendix Room/Panel_right_right", kWhiteMiddle)

	_gSingle("Hallway Room/Panel_hollow_hollow", kWhiteMiddle)
	_gSingle("Hallway Room/Panel_out_out", kWhiteMiddle)

	_gSingle("Open Areas/Panel_staircase", kWhiteMiddle)
	_gSingle("Open Areas/Panel_smile_smile", kWhiteMiddle)
	_gSingle("Open Areas/Panel_undistracted", kWhiteMiddle)
	_gSingle("Open Areas/Panel_stargazer_stargazer", kWhiteMiddle)

	_gSingle("Backside Room/Panel_backside_1", kWhiteMiddle)
	_gSingle("Backside Room/Panel_backside_2", kWhiteMiddle)
	_gSingle("Backside Room/Panel_backside_3", kWhiteMiddle)
	_gSingle("Backside Room/Panel_backside_4", kWhiteMiddle)
	_gSingle("Backside Room/Panel_backside_5", kWhiteMiddle)
	_gSingle("Backside Room/Panel_farther_far", kRedMiddle)
	_gSingle("Backside Room/Panel_first_first", kWhiteMiddle)
	_gSingle("Backside Room/Panel_second_second", kWhiteMiddle)
	_gSingle("Backside Room/Panel_third_third", kWhiteMiddle)
	_gSingle("Backside Room/Panel_fourth_fourth", kWhiteMiddle)
	_gSingle("Backside Room/Panel_paranoid_paranoid", kWhiteMiddle)
	_gPaired("Backside Room/Panel_salt_pepper", "Backside Room/Panel_pepper_salt", kBlackBottom)

	_gExactLength("Backside Room/Panel_four_four", kWhiteMiddle, 4)
	_gExactLength("Backside Room/Panel_four_four_2", kWhiteMiddle, 4)
	_gExactLength("Backside Room/Panel_four_four_3", kWhiteMiddle, 4)
	_gExactLength("Backside Room/Panel_four_four_4", kWhiteMiddle, 4)
	_gExactLength("Backside Room/Panel_five_five", kWhiteMiddle, 5)
	_gExactLength("Backside Room/Panel_five_five_2", kWhiteMiddle, 5)
	_gExactLength("Backside Room/Panel_five_five_3", kWhiteMiddle, 5)
	_gExactLength("Backside Room/Panel_five_five_4", kWhiteMiddle, 5)
	_gExactLength("Backside Room/Panel_five_five_5", kWhiteMiddle, 5)
	_gExactLength("Backside Room/Panel_six_six", kWhiteMiddle, 6)
	_gExactLength("Backside Room/Panel_six_six_2", kWhiteMiddle, 6)
	_gExactLength("Backside Room/Panel_six_six_3", kWhiteMiddle, 6)
	_gExactLength("Backside Room/Panel_six_six_4", kWhiteMiddle, 6)
	_gExactLength("Backside Room/Panel_six_six_5", kWhiteMiddle, 6)
	_gExactLength("Backside Room/Panel_six_six_6", kWhiteMiddle, 6)
	_gExactLength("Backside Room/Panel_seven_seven", kWhiteMiddle, 7)
	_gExactLength("Backside Room/Panel_seven_seven_2", kWhiteMiddle, 7)
	_gExactLength("Backside Room/Panel_seven_seven_3", kWhiteMiddle, 7)
	_gExactLength("Backside Room/Panel_seven_seven_4", kWhiteMiddle, 7)
	_gExactLength("Backside Room/Panel_seven_seven_5", kWhiteMiddle, 7)
	_gExactLength("Backside Room/Panel_seven_seven_6", kWhiteMiddle, 7)
	_gExactLength("Backside Room/Panel_seven_seven_7", kWhiteMiddle, 7)
	_gExactLength("Backside Room/Panel_eight_eight", kWhiteMiddle, 8)
	_gExactLength("Backside Room/Panel_eight_eight_2", kWhiteMiddle, 8)
	_gExactLength("Backside Room/Panel_eight_eight_3", kWhiteMiddle, 8)
	_gExactLength("Backside Room/Panel_eight_eight_4", kWhiteMiddle, 8)
	_gExactLength("Backside Room/Panel_eight_eight_5", kWhiteMiddle, 8)
	_gExactLength("Backside Room/Panel_eight_eight_6", kWhiteMiddle, 8)
	_gExactLength("Backside Room/Panel_eight_eight_7", kWhiteMiddle, 8)
	_gExactLength("Backside Room/Panel_eight_eight_8", kWhiteMiddle, 8)
	_gExactLength("Backside Room/Panel_nine_nine", kWhiteMiddle, 9)
	_gExactLength("Backside Room/Panel_nine_nine_2", kWhiteMiddle, 9)
	_gExactLength("Backside Room/Panel_nine_nine_3", kWhiteMiddle, 9)
	_gExactLength("Backside Room/Panel_nine_nine_4", kWhiteMiddle, 9)
	_gExactLength("Backside Room/Panel_nine_nine_5", kWhiteMiddle, 9)
	_gExactLength("Backside Room/Panel_nine_nine_6", kWhiteMiddle, 9)
	_gExactLength("Backside Room/Panel_nine_nine_7", kWhiteMiddle, 9)
	_gExactLength("Backside Room/Panel_nine_nine_8", kWhiteMiddle, 9)
	_gExactLength("Backside Room/Panel_nine_nine_9", kWhiteMiddle, 9)

	#_clearFlag(kObscureHint)

	if _needs_reuse.size() > 0:
		print("Generation failed; the following words need to be reused:")
		for word_id in _needs_reuse:
			print(_answer_by_id[word_id])

	print("Generated!")
	for puzzle in panel_mapping:
		print("%s: %s? %s!" % puzzle)


func apply(scene):
	var panels_parent = scene.get_node("Panels")
	var found_panels = []
	for puzzle in panel_mapping:
		var panel = panels_parent.get_node(puzzle[0])
		panel.text = puzzle[1]
		panel.answer = puzzle[2]
		found_panels.append(puzzle[0])
	
	for panel_group in panels_parent.get_children():
		for panel_panel in panel_group.get_children():
			var relpath = String(panels_parent.get_path_to(panel_panel))
			if !found_panels.has(relpath):
				print("Unrandomized: %s" % relpath)
	
	for mapping in painting_mapping:
		var scene_path = "res://nodes/paintings/%s.tscn" % mapping[1]
		var painting_scene = load(scene_path)
		var new_painting = painting_scene.instance()
		new_painting.set_name("%s_new" % mapping[0])
		
		var old_painting = scene.get_node("Decorations/Paintings").get_node(mapping[0])
		new_painting.translation = old_painting.translation
		new_painting.rotation = old_painting.rotation

		scene.get_node("Decorations/Paintings").add_child(new_painting)
		old_painting.queue_free()
	
	# Randomize THE COLORFUL - WALLS
	var gridmap = scene.get_node("GridMap")
	var mesh_library = gridmap.mesh_library
	var mapvec1 = gridmap.world_to_map(gridmap.to_local(Vector3(-14.5, 0, -95.5)))
	var mapvec2 = gridmap.world_to_map(gridmap.to_local(Vector3(-9.5, 6.5, -84.5)))
	var mapvec_ex = gridmap.world_to_map(gridmap.to_local(Vector3(-10.5, 0.5, -86.5)))
	
	var walls_puzzle = _pickRandom(_gen_walls)
	var mesh_item = mesh_library.find_item_by_name(walls_puzzle[1])
	
	for x in range(mapvec1.x, mapvec2.x + 1):
		for y in range(mapvec1.y, mapvec2.y + 1):
			for z in range(mapvec1.z, mapvec2.z + 1):
				if gridmap.get_cell_item(x,y,z) != GridMap.INVALID_CELL_ITEM:
					gridmap.set_cell_item(x, y, z, mesh_item)
	
	gridmap.set_cell_item(mapvec_ex.x, mapvec_ex.y, mapvec_ex.z, mesh_library.find_item_by_name("MeshInstance2"))
	scene.get_node("Doors/Doorway Room Doors/Door_green/Hinge/MeshInstance").set_surface_material(0, mesh_library.get_item_mesh(mesh_item).surface_get_material(0))
	
	var walls_puzzle_node = panels_parent.get_node("Doorways Room/Panel_path_i")
	walls_puzzle_node.answer = walls_puzzle[0]

	# Randomize ceilings of The Steady
	var ceilings = [
		[71.5, 98.5], [64.5, 98.5], [57.5, 98.5], [50.5, 98.5], [43.5, 98.5], [36.5, 98.5],
		[71.5, 91.5], [64.5, 91.5], [57.5, 91.5], [50.5, 91.5], [43.5, 91.5], [36.5, 91.5],
		[57.5, 84.5], [50.5, 84.5], [43.5, 84.5], [36.5, 84.5]
	]
	for i in range(0, 16):
		var color_mesh = mesh_library.find_item_by_name(steady_mapping[i])
		var ceilingvec1 = gridmap.world_to_map(gridmap.to_local(Vector3(ceilings[i][0], 5.5, ceilings[i][1])))
		for x in range(ceilingvec1.x - 4, ceilingvec1.x + 1):
			for z in range(ceilingvec1.z - 4, ceilingvec1.z + 1):
				gridmap.set_cell_item(x, ceilingvec1.y, z, color_mesh)


func _pickRandom(set):
	if set.size() == 0:
		print("This is a problem: %s" % _cur_panel)
	var id = _rng.randi_range(0, set.size() - 1)
	return set[id]


func _setFlag(flag):
	_flags |= flag


func _clearFlag(flag):
	_flags &= ~flag


func _isFlag(flag):
	return (_flags & flag) > 0


func _setUniquePool(pool):
	_unique_pool = pool
	if not _answers_by_unique_pool.has(_unique_pool):
		_answers_by_unique_pool[_unique_pool] = []


func _clearUniquePool():
	_unique_pool = ""


func _gSingle(panel_path, puzzle_type):
	_cur_panel = panel_path
	if puzzle_type == kWhiteMiddle:
		var word_choices = _filterBestPuzzles(_answers_by_type[kWhiteMiddle])
		var answer_id = _pickRandom(word_choices)
		_setPanel(panel_path, answer_id, answer_id)
	else:
		var puzzle_choices = _filterBestPuzzles(_answers_by_type[puzzle_type])
		if puzzle_choices.empty():
			print("No possible puzzles for panel %s" % panel_path)
		else:
			var answer_id = _pickRandom(puzzle_choices)
			var hint_choices = _puzzle_by_id[answer_id][1][puzzle_type]
			var hint_id = _pickRandom(hint_choices)
			_setPanel(panel_path, hint_id, answer_id)


func _gDouble(panel1_path, panel2_path, puzzle_type):
	_cur_panel = panel1_path
	if puzzle_type == kWhiteMiddle:
		var word_choices = _filterBestPuzzles(_answers_by_type[kWhiteMiddle])
		var answer_id = _pickRandom(word_choices)
		_setPanel(panel1_path, answer_id, answer_id)
		_setPanel(panel2_path, answer_id, answer_id)
	else:
		var pre_choices = _filterBestPuzzles(_answers_by_type[puzzle_type])
		var puzzle_choices = []
		for word_id in pre_choices:
			if _puzzle_by_id[word_id][1][puzzle_type].size() > 1:
				puzzle_choices.append(word_id)
		if puzzle_choices.empty():
			print("No possible puzzles for panel %s" % panel1_path)
		else:
			var answer_id = _pickRandom(puzzle_choices)
			var hint_choices = _puzzle_by_id[answer_id][1][puzzle_type].duplicate()
			var hint1_id = _pickRandom(hint_choices)
			hint_choices.erase(hint1_id)
			var hint2_id = _pickRandom(hint_choices)
			_setPanel(panel1_path, hint1_id, answer_id)
			_setPanel(panel2_path, hint2_id, answer_id)


func _gQuadruple(panel1_path, panel2_path, panel3_path, panel4_path, puzzle_type):
	_cur_panel = panel1_path
	var pre_choices = _filterBestPuzzles(_answers_by_type[puzzle_type])
	var puzzle_choices = []
	for word_id in pre_choices:
		if _puzzle_by_id[word_id][1][puzzle_type].size() > 3:
			puzzle_choices.append(word_id)
	if puzzle_choices.empty():
		print("No possible puzzles for panel %s" % panel1_path)
	else:
		var answer_id = _pickRandom(puzzle_choices)
		var hint_choices = _puzzle_by_id[answer_id][1][puzzle_type].duplicate()
		var hint1_id = _pickRandom(hint_choices)
		hint_choices.erase(hint1_id)
		var hint2_id = _pickRandom(hint_choices)
		hint_choices.erase(hint2_id)
		var hint3_id = _pickRandom(hint_choices)
		hint_choices.erase(hint3_id)
		var hint4_id = _pickRandom(hint_choices)
		_setPanel(panel1_path, hint1_id, answer_id)
		_setPanel(panel2_path, hint2_id, answer_id)
		_setPanel(panel3_path, hint3_id, answer_id)
		_setPanel(panel4_path, hint4_id, answer_id)


func _gPaired(panel1_path, panel2_path, puzzle_type):
	_cur_panel = panel1_path
	var needed_type = puzzle_type
	if puzzle_type == kWhiteMiddle:
		needed_type = kBlackBottom
	var puzzle_choices = _filterBestPuzzles(_answers_by_type[needed_type])
	if puzzle_choices.empty():
		print("No possible puzzles for panel %s" % panel1_path)
	else:
		var answer_id = _pickRandom(puzzle_choices)
		var hint_choices = _puzzle_by_id[answer_id][1][needed_type]
		var hint_id = _pickRandom(hint_choices)
		if puzzle_type == kWhiteMiddle:
			_setPanel(panel1_path, hint_id, hint_id)
			_setPanel(panel2_path, answer_id, answer_id)
		else:
			_setPanel(panel1_path, hint_id, answer_id)
			_setPanel(panel2_path, answer_id, hint_id)


func _gStack2(panel1_path, puzzle_type1, panel2_path, puzzle_type2):
	_cur_panel = panel1_path
	if puzzle_type1 == kWhiteMiddle or puzzle_type2 == kWhiteMiddle:
		if puzzle_type1 == kWhiteMiddle:
			_gSingle(panel2_path, puzzle_type2)
			_gCopyAnswer(panel2_path, panel1_path)
		else:
			_gSingle(panel1_path, puzzle_type1)
			_gCopyAnswer(panel1_path, panel2_path)
	else:
		var pre_choices = _filterBestPuzzles(_answers_by_type[puzzle_type1])
		var puzzle_choices = []
		for word_id in pre_choices:
			if _puzzle_by_id[word_id][1].has(puzzle_type2):
				puzzle_choices.append(word_id)
		if puzzle_choices.empty():
			print("No possible puzzles for panel %s" % panel1_path)
		else:
			var answer_id = _pickRandom(puzzle_choices)
			var hint1_choices = _puzzle_by_id[answer_id][1][puzzle_type1]
			var hint1_id = _pickRandom(hint1_choices)
			var hint2_choices = _puzzle_by_id[answer_id][1][puzzle_type2]
			var hint2_id = _pickRandom(hint2_choices)
			_setPanel(panel1_path, hint1_id, answer_id)
			if panel2_path != "":
				_setPanel(panel2_path, hint2_id, answer_id)


func _gChain(panel_path, puzzle_types):
	_cur_panel = panel_path
	var just_answers = []
	var hints_by_answer = {}
	puzzle_types.invert()
	
	if _chain_cache.has(puzzle_types):
		just_answers = _chain_cache[puzzle_types][0]
		hints_by_answer = _chain_cache[puzzle_types][1]
	else:
		var remaining = []
		for ptype in puzzle_types:
			if remaining.empty():
				for answer in _answers_by_type[ptype]:
					for choice in _puzzle_by_id[answer][1][ptype]:
						remaining.append([choice, answer])
			elif ptype == kOrangeMiddle:
				var newrem = []
				for puzzle in remaining:
					if _gen_wanderlust.has(puzzle[0]):
						newrem.append(puzzle)
				if newrem.empty():
					print("No possible puzzles for panel %s" % panel_path)
					return
				remaining = newrem
			else:
				var newrem = []
				for puzzle in remaining:
					if _puzzle_by_id[puzzle[0]][1].has(ptype):
						for choice in _puzzle_by_id[puzzle[0]][1][ptype]:
							newrem.append([choice, puzzle[1]])
				if newrem.empty():
					print("No possible puzzles for panel %s" % panel_path)
					return
				remaining = newrem
		for puzzle in remaining:
			if !just_answers.has(puzzle[1]):
				just_answers.append(puzzle[1])
				hints_by_answer[puzzle[1]] = [puzzle[0]]
			else:
				hints_by_answer[puzzle[1]].append(puzzle[0])
		_chain_cache[puzzle_types] = [just_answers, hints_by_answer]
	var puzzle_choices = _filterBestPuzzles(just_answers)
	if puzzle_choices.empty():
		print("No possible puzzles for panel %s" % panel_path)
	else:
		var answer_id = _pickRandom(puzzle_choices)
		var hint_choices = hints_by_answer[answer_id]
		var hint_id = _pickRandom(hint_choices)
		if puzzle_types[puzzle_types.size()-1] == kOrangeMiddle:
			var hint = _applyCipher(_answer_by_id[hint_id])
			_writePanel(panel_path, hint, _answer_by_id[answer_id])
		else:
			_setPanel(panel_path, hint_id, answer_id)


func _gCopy(panel1_path, panel2_path):
	_cur_panel = panel2_path
	_writePanel(panel2_path, _hint_by_path[panel1_path], _answer_by_path[panel1_path])


func _gCopyAnswer(panel1_path, panel2_path):
	_cur_panel = panel2_path
	_writePanel(panel2_path, _answer_by_path[panel1_path], _answer_by_path[panel1_path])


func _gCopyHidden(panel1_path, panel2_path):
	_cur_panel = panel2_path
	_writePanel(panel2_path, "?".repeat(_answer_by_path[panel1_path].length()), _answer_by_path[panel1_path])


func _gExactLength(panel_path, puzzle_type, answer_length):
	_cur_panel = panel_path
	var puzzle_choices = _filterBestPuzzles(_answers_by_length[answer_length])
	if puzzle_type != kWhiteMiddle:
		# TODO: Do we even need this?
		pass
	if puzzle_choices.empty():
		print("No possible puzzles for panel %s" % panel_path)
	else:
		var answer_id = _pickRandom(puzzle_choices)
		if puzzle_type == kWhiteMiddle:
			_setPanel(panel_path, answer_id, answer_id)
		else:
			var hint_choices = _puzzle_by_id[answer_id][1][puzzle_type]
			var hint_id = _pickRandom(hint_choices)
			_setPanel(panel_path, hint_id, answer_id)


func _gOrangeNumber(panel_path):
	_cur_panel = panel_path
	var word_id = _pickRandom(_gen_wanderlust)
	var answer = _answer_by_id[word_id]
	var hint = _applyCipher(answer)
	_writePanel(panel_path, hint, answer)


func _gOrangeWord(panel_path):
	_cur_panel = panel_path
	var word_id = _pickRandom(_gen_wanderlust)
	var hint = _answer_by_id[word_id]
	var answer = _applyCipher(hint)
	_writePanel(panel_path, hint, answer)


func _gOrangeAddition(panel_path):
	_cur_panel = panel_path
	var puzzle = _pickRandom(_gen_addition)
	var word1 = _answer_by_id[puzzle[0]]
	var word2 = _answer_by_id[puzzle[1]]
	var solution = _answer_by_id[puzzle[2]]
	_writePanel(panel_path, "%s + %s" % [word1, word2], solution)


func _gPaintingPuzzle(panel_path, painting_name):
	_cur_panel = panel_path
	var chosen = _pickRandom(_painting_puzzles)
	_painting_puzzles.erase(chosen)
	_writePanel(panel_path, "painting", chosen[1])
	painting_mapping.append([painting_name, chosen[0]])


func _gCombo(panel_path, left_type, right_type):
	_cur_panel = panel_path
	var puzzles = _gen_combos[left_type][right_type]
	var chosen = _pickRandom(puzzles)
	var hint_text
	if chosen[1] == -1:
		hint_text = _answer_by_id[chosen[0]]
	else:
		hint_text = "%s %s" % [_answer_by_id[chosen[0]], _answer_by_id[chosen[1]]]
	_writePanel(panel_path, hint_text, _answer_by_id[chosen[2]])


func _gSteady(panel_path, puzzle_type):
	_cur_panel = panel_path
	var pre_choices = _filterBestPuzzles(_answers_by_type[kColorIs])
	var puzzle_choices = []
	for word_id in pre_choices:
		if _puzzle_by_id[word_id][1].has(puzzle_type):
			puzzle_choices.append(word_id)
	if puzzle_choices.empty():
		print("No possible puzzles for panel %s" % panel_path)
	else:
		var answer_id = _pickRandom(puzzle_choices)
		var hint_choices = _puzzle_by_id[answer_id][1][puzzle_type]
		var hint_id = _pickRandom(hint_choices)
		var color_choices = _puzzle_by_id[answer_id][1][kColorIs]
		var color_id = _pickRandom(color_choices)
		var color_word = _answer_by_id[color_id]
		_setPanel(panel_path, hint_id, answer_id)

		if color_word == "red":
			steady_mapping.append("MeshInstance7")
		elif color_word == "blue":
			steady_mapping.append("MeshInstance4")
		elif color_word == "green":
			steady_mapping.append("MeshInstance2")
		elif color_word == "black":
			steady_mapping.append("MeshInstance6")
		elif color_word == "white":
			steady_mapping.append("MeshInstance5")
		elif color_word == "brown":
			steady_mapping.append("MeshInstance3")
		elif color_word == "orange":
			steady_mapping.append("MeshInstance10")
		elif color_word == "yellow":
			steady_mapping.append("MeshInstance9")
		elif color_word == "purple":
			steady_mapping.append("MeshInstance8")


func _applyCipher(text):
	var result = text
	for i in range(result.length()):
		if result.substr(i,1) == "w":
			result[i] = "1"
		elif result.substr(i,1) == "a":
			result[i] = "2"
		elif result.substr(i,1) == "n":
			result[i] = "3"
		elif result.substr(i,1) == "d":
			result[i] = "4"
		elif result.substr(i,1) == "e":
			result[i] = "5"
		elif result.substr(i,1) == "r":
			result[i] = "6"
		elif result.substr(i,1) == "l":
			result[i] = "7"
		elif result.substr(i,1) == "u":
			result[i] = "8"
		elif result.substr(i,1) == "s":
			result[i] = "9"
		elif result.substr(i,1) == "t":
			result[i] = "0"
	return result


func _filterBestPuzzles(input):
	var acceptable = []
	for word_id in input:
		if (
			_unique_pool != ""
			and _answers_by_unique_pool.has(_unique_pool)
			and _answers_by_unique_pool[_unique_pool].has(word_id)
		):
			continue
		if _isFlag(kShortAnswer) and _answer_by_id[word_id].length() > _short_answer_length:
			continue
		if _isFlag(kForcePalindrome) and _answer_by_id[word_id] != _reverseString(_answer_by_id[word_id]):
			continue
		acceptable.append(word_id)
	#if _isFlag(kMustReuseAnswer):
	#	var possible = []
	#	for word_id in not_uniqued:
	#		if _puzzle_by_id[word_id][1].size() > 1:
	#			possible.append(word_id)
	#	not_uniqued = possible
	var result = []
	for word_id in acceptable:
		if _needs_reuse.has(word_id):
			result.append(word_id)
	if result.empty():
		for word_id in acceptable:
			result.append(word_id)
			if _possible_reuse.has(word_id):
				result.append(word_id)
	return result


func _setPanel(panel_path, hint_id, answer_id):
	if _unique_pool != "":
		_answers_by_unique_pool[_unique_pool].append(answer_id)

	var hint_text = _answer_by_id[hint_id]
	if _isFlag(kObscureHint):
		var numToObscure = 1
		if hint_text.length()/3 > 0:
			numToObscure = _rng.randi_range(1, hint_text.length()/3)
		var iota = range(0, hint_text.length())
		var order = []
		while not iota.empty():
			var i = _rng.randi_range(0, iota.size() - 1)
			order.append(iota[i])
			iota.remove(i)
		for i in range(0, numToObscure):
			if hint_text[order[i]] != " " and hint_text[order[i]] != "-":
				hint_text[order[i]] = "?"

	_writePanel(panel_path, hint_text, _answer_by_id[answer_id])
	if not _isFlag(kMustReuseAnswer):
		if _needs_reuse.has(answer_id):
			_needs_reuse.erase(answer_id)
			_possible_reuse.append(answer_id)


func _writePanel(panel_path, hint, answer):
	panel_mapping.append([panel_path, hint, answer])
	_hint_by_path[panel_path] = hint
	_answer_by_path[panel_path] = answer
	_total_generated += 1
	emit_signal("generator_status", "Generating puzzles... (%d/?)" % _total_generated)
	global._print("%s : %s -> %s" % [panel_path, hint, answer])


func _reverseString(s):
	var r = ""
	for i in range(s.length()-1, -1, -1):
		r += s[i]
	return r