extends Node
class_name DialogicParser


# adds name coloring to the dialog texts
static func parse_characters(dialog_script):
	var characters = DialogicUtil.get_character_list()
	var event_index := 0
	for event in dialog_script['events']:
		# if this is a text or question event
		if event.get('event_id') in ['dialogic_001', 'dialogic_010']:
			var text :String = event.get({'dialogic_001':'text', 'dialogic_010':'question'}[event.get('event_id')], '')
			for character in characters:
				# check whether to use the name or the display name
				var char_names = [character.get('name')]
				if character.get('data', {}).get('display_name_bool', false):
					if character.get('display_name'): char_names.append(character.get('display_name'))
				if character.get('data', {}).get('nickname_bool', false):
					for nickname in character.get('data').get('nickname', '').split(',', true, 0):
						if nickname.strip_edges():
							char_names.append(nickname.strip_edges())
				
				#Regex purposefully excludes [] as replacing those interferes with the second regex
				var escapeRegExp = "(?=[+&|!(){}^\"~*.?:\\\\-])" 
				
				var regex = RegEx.new()
				regex.compile(escapeRegExp)
				char_names = regex.sub(str(char_names), "\\", true)
				
				var regex_thing = "((\\]|^)[^\\[]*)(?<name>"+str(char_names).replace("[", "(").replace("]", ")").replace(", ", "|")+")"
				regex.compile(regex_thing)
				
				var counter = 0
				for result in regex.search_all(text):
					text = text.insert(result.get_start("name")+((9+8+8)*counter), '[color=#' + character['color'].to_html() + ']')
					text = text.insert(result.get_end("name")+9+8+((9+8+8)*counter), '[/color]')
					result = regex.search(text)
					counter += 1
				
				dialog_script['events'][event_index][{'dialogic_001':'text', 'dialogic_010':'question'}[event.get('event_id')]] = text
		
		event_index += 1

	return dialog_script


# removes empty lines, splits message at new lines
static func parse_text_lines(unparsed_dialog_script: Dictionary, preview:bool = false) -> Dictionary:
	var parsed_dialog: Dictionary = unparsed_dialog_script
	var new_events: Array = []
	var settings = DialogicResources.get_settings_config()
	var split_new_lines = settings.get_value('dialog', 'new_lines', true)
	var remove_empty_messages = settings.get_value('dialog', 'remove_empty_messages', true)

	# Return the same thing if it doesn't have events
	if unparsed_dialog_script.has('events') == false:
		return unparsed_dialog_script

	# Parsing
	for event in unparsed_dialog_script['events']:
		if event.has('text') and event.has('character') and event.has('portrait'):
			if event['text'].empty() and remove_empty_messages:
				pass
			elif '\n' in event['text'] and preview == false and split_new_lines:
				var lines = event['text'].split('\n')
				var counter = 0 
				for line in lines:
					if not line.empty():
						var n_event = {
							'event_id':'dialogic_001',
							'text': line,
							'character': event['character'],
							'portrait': event['portrait'],
						}
						#assigning voices to the new events 
						if event.has('voice_data'):
							if event['voice_data'].has(str(counter)):
								n_event['voice_data'] = {'0':event['voice_data'][str(counter)]}
						new_events.append(n_event)
					counter += 1 
			else:
				new_events.append(event)
		else:
			new_events.append(event)

	parsed_dialog['events'] = new_events

	return parsed_dialog


# returns the text but with BBcode for glossary and the values of the variables
static func parse_definitions(current_dialog, text: String, variables: bool = true, glossary: bool = true):
	var final_text: String = text
	if not current_dialog.preview:
		current_dialog.definitions = Dialogic._get_definitions()
	if variables:
		final_text = _insert_variable_definitions(current_dialog, text)
	if glossary and current_dialog._should_show_glossary():
		final_text = _insert_glossary_definitions(current_dialog, final_text)
	return final_text


# creates a list of questions to be used at the end of choices
static func parse_branches(current_dialog, dialog_script: Dictionary) -> Dictionary:
	current_dialog.questions = [] # Resetting the questions

	# Return the same thing if it doesn't have events
	if dialog_script.has('events') == false:
		return dialog_script

	var parser_queue = [] # This saves the last question opened, and it gets removed once it was consumed by a endbranch event
	var event_idx: int = 0 # The current id for jumping later on
	var question_idx: int = 0 # identifying the questions to assign options to it
	for event in dialog_script['events']:
		if event['event_id'] == 'dialogic_011':
			var opened_branch = parser_queue.back()
			var option = {
				'question_idx': opened_branch['question_idx'],
				'label': parse_definitions(current_dialog, event['choice'], true, false),
				'event_idx': event_idx,
				}
			if event.has('condition') and event.has('definition') and event.has('value'):
				option = {
					'question_idx': opened_branch['question_idx'],
					'label': parse_definitions(current_dialog, event['choice'], true, false),
					'event_idx': event_idx,
					'condition': event['condition'],
					'definition': event['definition'],
					'value': event['value'],
					}
			else:
				option = {
					'question_idx': opened_branch['question_idx'],
					'label': parse_definitions(current_dialog, event['choice'], true, false),
					'event_idx': event_idx,
					'condition': '',
					'definition': '',
					'value': '',
					}
			dialog_script['events'][opened_branch['event_idx']]['options'].append(option)
			event['question_idx'] = opened_branch['question_idx']
		elif event['event_id'] == 'dialogic_010':
			event['event_idx'] = event_idx
			event['question_idx'] = question_idx
			event['answered'] = false
			question_idx += 1
			current_dialog.questions.append(event)
			parser_queue.append(event)
		elif event['event_id'] == 'dialogic_012':
			event['event_idx'] = event_idx
			event['question_idx'] = question_idx
			event['answered'] = false
			question_idx += 1
			current_dialog.questions.append(event)
			parser_queue.append(event)
		elif event['event_id'] == 'dialogic_013' and parser_queue:
			event['event_idx'] = event_idx
			var opened_branch = parser_queue.pop_back()
			event['end_branch_of'] = opened_branch['question_idx']
			dialog_script['events'][opened_branch['event_idx']]['end_idx'] = event_idx
		event_idx += 1

	return dialog_script


static func parse_anchors(current_dialog):
	current_dialog.anchors = {}
	var idx = 0
	for event in current_dialog.dialog_script['events']:
		if event['event_id'] == 'dialogic_015':
			current_dialog.anchors[event['id']] = idx
		idx += 1


# adds the alignment BBCode to text events
static func parse_alignment(current_dialog, text):
	var alignment = current_dialog.current_theme.get_value('text', 'alignment', 0)
	var fname = current_dialog.current_theme.get_value('settings', 'name', 'none')
	if alignment in [1,4,7]:
		text = '[center]' + text + '[/center]'
	elif alignment in [2,5,8]:
		text = '[right]' + text + '[/right]'
	return text


# adds the values of the variables
static func _insert_variable_definitions(current_dialog, text: String):
	var final_text := text;
	
	# Regex for searching text inside brackets []
	var regex = RegEx.new()
	regex.compile('\\[(.*?)\\]')
	var result = regex.search_all(final_text)
	if result:
		for res in result:
			var r_string = res.get_string()
			# Choosing a random word if there is a list like [word1,word2,word3,word4]
			if ',' in r_string:
				var r_string_array = r_string.replace('[', '').replace(']', '').split(',')
				var new_word = r_string_array[randi() % r_string_array.size()]
				# Check if the random selected word is a variable that exists and get the value
				for d in current_dialog.definitions['variables']:
					var name : String = d['name']
					if new_word == d['name']:
						new_word = str(d['value'])
				# Replace the old string with the new word
				final_text = final_text.replace(r_string, new_word)
			else:
				# Replace the name of a value [whatever] with the result
				var r_string_array = r_string.replace('[', '').replace(']', '')
				
				# Find the ID if it's got an absolute path
				if '/' in r_string_array:
					var variable_id=Dialogic._get_variable_from_file_name(r_string_array)
					for d in current_dialog.definitions['variables']:
						if d['id'] == variable_id:
							final_text = final_text.replace(r_string, d['value'])
				else:					
					for d in current_dialog.definitions['variables']:
						if d['name'] == r_string_array:
							final_text = final_text.replace(r_string, d['value'])
	
	return final_text

# adds the BBCode for the glossary words
static func _insert_glossary_definitions(current_dialog, text: String):
	var color = current_dialog.current_theme.get_value('definitions', 'color', '#ffbebebe')
	var final_text := text
	# I should use regex here, but this is way easier :)
	for d in current_dialog.definitions['glossary']:
		final_text = final_text.replace(d['name'],
			'[url=' + d['id'] + ']' +
			'[color=' + color + ']' + d['name'] + '[/color]' +
			'[/url]'
		)
	return final_text