tool
extends EditorImportPlugin

var YarnScript = preload("yarn_script.gd")

enum Presets { DEFAULT }

# types
const EMPTY = "empty"
const NODE_BODY_END = "node_body_end"
const NODE_BODY_START = "node_body_start"
const NODE_TAG = "node_tag"
const JUMP = "jump"
const TEXT = "text"
const CHOICE = "choice"
const CHOICE_BLOCKS = "choice_blocks"
const CONDITION_IF = "if"
const CONDITION_ELSE = "else"
const CONDITION_ELSEIF = "elseif"
const CONDITION_ENDIF = "endif"
const CONDITION_BLOCKS = "condition_blocks"
const COMMAND = "command"

# Entry point

func import(source_file, save_path, options, r_platform_variants, r_gen_files):
	var file : File = File.new()
	var err := file.open(source_file, File.READ)
	if err != OK:
		return err
	
	var yarn = (YarnScript).new()
	yarn.nodes = {}
	
	while not file.eof_reached():
		var node = parse_header(file)
		node["body"] = parse_body(file)
		if node["title"] != "":
			yarn.nodes[node["title"]] = node
		
	file.close()
	
	return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], yarn)

# Nodes parsing

func parse_header(file : File):
	var new_node = {"title": "", "tags": "", "body":[]}
	
	while not file.eof_reached():
		var parsed_line = parse_line_header(file.get_line())
		var type = parsed_line["type"]
		
#		print("[" + type + "]\n\t" + str(parsed_line))
		
		if type == EMPTY:
			pass
		elif type == NODE_BODY_START:
			break
		elif type == NODE_TAG:
			new_node[parsed_line["key"]] = parsed_line["value"]
	
	return new_node
	
func parse_body(file):
	var body = []
	
	while not file.eof_reached():
		var parsed_line = parse_line_body(file.get_line())
		var type = parsed_line["type"]
		
#		print("[" + type + "]\n\t" + str(parsed_line))
		
		if type == EMPTY:
			pass
		elif type == NODE_BODY_END:
			break
		elif type == CONDITION_IF:
			body.push_back(parse_condition_blocks(file, parsed_line))
		elif type == CHOICE:
			if body.size() == 0 or body[body.size() - 1]["type"] != CHOICE_BLOCKS:
				body.push_back({"type":CHOICE_BLOCKS, "blocks":[]})
			body[body.size() -1]["blocks"].push_back(parsed_line)
		else:
			body.push_back(parsed_line)
		
	return body
	
func parse_condition_blocks(file, parsed_line):
	var condition_blocks = {"type":CONDITION_BLOCKS, "blocks":[]}
		
	var current_block = {"condition":parsed_line["expression"], "body":[]}
	
	while not file.eof_reached():
		parsed_line = parse_line_body(file.get_line())
		var type = parsed_line["type"]
		
#		print("[" + type + "]\n\t" + str(parsed_line))
		
		if type == EMPTY:
			pass
		elif type == CONDITION_IF:
			current_block["body"].push_back(parse_condition_blocks(file, parsed_line))
		elif type == CONDITION_ELSEIF:
			condition_blocks["blocks"].push_back(current_block)
			current_block = {"condition":parsed_line["expression"], "body":[]}
		elif type == CONDITION_ELSE:
			condition_blocks["blocks"].push_back(current_block)
			current_block = {"condition":parsed_line["expression"], "body":[]}
		elif type == CONDITION_ENDIF:
			condition_blocks["blocks"].push_back(current_block)
			break
		else:
			current_block["body"].push_back(parsed_line)
	
	return condition_blocks

# Lines Parsing

func parse_line_header(raw_line : String):
	var parsed_line = {"type":"????", "raw":raw_line}
	
	if raw_line.strip_edges() == "":
		parsed_line["type"] = EMPTY
	elif raw_line.begins_with("---"):
		parsed_line["type"] = NODE_BODY_START
	else:
		var split = raw_line.split(':')
		if split.size() >= 2:
			parsed_line["type"] = NODE_TAG
			parsed_line["key"] = split[0]
			parsed_line["value"] = split[1].strip_edges()
	
	return parsed_line

func parse_line_body(raw_line : String):
	#todo later, use indentation
	raw_line = raw_line.strip_edges()
	
	var parsed_line = {"type":"????", "raw":raw_line}
	
	if raw_line.strip_edges() == "":
		parsed_line["type"] = EMPTY
	elif raw_line.begins_with("==="):
		parsed_line["type"] = NODE_BODY_END
	elif raw_line.begins_with("[["):
		var split := raw_line.replace("[[", "").replace("]]", "").split("|")
		if split.size() > 1:
			parsed_line["type"] = CHOICE
			parsed_line["text"] = split[0]
			parsed_line["node"] = split[1]
		else:
			parsed_line["type"] = JUMP
			parsed_line["node"] = split[0]
	elif raw_line.begins_with("<<"):
		var split := raw_line.replace("<<", "").replace(">>", "").strip_edges().split(' ')
		if split.size() > 0:
			if split[0] == "if":
				parsed_line["type"] = CONDITION_IF
				var expr := ""
				for token in Array(split).slice(1, split.size() -1):
					expr += token + " "
				parsed_line["expression"] = expr
			elif split[0] == "elseif":
				parsed_line["type"] = CONDITION_ELSEIF
				var expr := ""
				for token in Array(split).slice(1, split.size() -1):
					expr += token + " "
				parsed_line["expression"] = expr
			elif split[0] == "else":
				parsed_line["type"] = CONDITION_ELSE
				parsed_line["expression"] = "true" # add default expression always true
			elif split[0] == "endif":
				parsed_line["type"] = CONDITION_ENDIF
			else: 
				parsed_line["type"] = COMMAND
				parsed_line["command"] = split[0]
				parsed_line["args"] = Array(split).slice(1, split.size() - 1)
	else:
		parsed_line["type"] = TEXT
		# use regex ?
		var separator_idx = raw_line.find(':')
		if separator_idx != -1:
			var chara = raw_line.substr(0, separator_idx).strip_edges()
			var text = raw_line.substr(separator_idx + 1 , -1).strip_edges()
			parsed_line["chara"] = chara
			parsed_line["text"] = text
		else:
			parsed_line["chara"] = ""
			parsed_line["text"] = raw_line.strip_edges()
			
	return parsed_line

# Required editor functions

func get_importer_name():
	return "yarn.script"
	
func get_visible_name():
	return "Yarn Script"

func get_recognized_extensions():
	return ["yarn", "txt"]

func get_save_extension():
	return "res"
	
func get_resource_type():
	return "Resource"

func get_preset_count():
	return Presets.size()
	
func get_preset_name(preset):
	match preset:
		Presets.DEFAULT:
			return "Default"
		_:
			return "Unknown"

func get_import_options(preset):
	match preset:
		_:
			return []

func get_option_visibility(option, options):
	return true
