extends Spatial

enum {OPEN_UP, OPEN_DOWN, OPEN_LEFT, OPEN_RIGHT}

var bitflags = 0
const BITUP = (1 << 0)
const BITDOWN = (1 << 1)
const BITLEFT = (1 << 2)
const BITRIGHT = (1 << 3)
var exits = []
var windows = []
var outside_doors = []

var door = preload("res://scenes/maps/door.tscn")
var jail_door = preload("res://scenes/maps/jail-door.tscn")
var window = preload("res://scenes/maps/window.tscn")
var outside_door = preload("res://scenes/maps/door-outside.tscn")
var stairs = preload("res://scenes/maps/stairs.tscn")
var outside_door_placed = false
var tw2d
var te2d
var tn2d
var ts2d

var furniture_data = {}
var save_data = {
	"personal_room": false,
	"kitchen_room": false,
	"master_room": false,
	"edit_room": false,
	"jail_room": false,
	"remove_iws": false,
	'stairs': -1
}

func v2s(v: Vector3):
	return str(v.x) + " " + str(v.y) + " " + str(v.z)
func xform2s(xform: Transform):
	var v1 = xform.origin
	var v2 = xform.basis[0]
	var v3 = xform.basis[1]
	var v4 = xform.basis[2]
	var ret = ""
	for v in [v1, v2, v3, v4]:
		ret += v2s(v)
		if v != v4:
			ret += " "
	return ret
func s2xform(s: String):
	var vs = s.split(" ")
	var fvs = []
	for v in vs:
		fvs.push_back(float(v))
	var ret: = Transform()
	ret.origin = Vector3(fvs[0], fvs[1], fvs[2])
	ret.basis[0] = Vector3(fvs[3], fvs[4], fvs[5])
	ret.basis[1] = Vector3(fvs[6], fvs[7], fvs[8])
	ret.basis[2] = Vector3(fvs[9], fvs[10], fvs[11])
	return ret

func save_furniture():
	for k in get_children():
		var save_data = []
		for e in ["fc", "fwb"]:
			if k.name.begins_with(e):
				for l in k.get_children():
					if l.has_meta("save_path"):
						save_data.push_back({"path": l.get_meta("save_path"), "xform": xform2s(l.transform)})
				furniture_data[k.name] = save_data
func save():
	save_data.exits = exits
	save_data.windows = windows
	if has_meta("master_room"):
		save_data.master_room = true
	if has_meta("kitchen_room"):
		save_data.kitchen_room = true
	if has_meta("jail_room"):
		save_data.jail_room = true
	if has_meta("exit_room"):
		save_data.exit_room = true
	if has_meta("stairs"):
		save_data.stairs = get_meta("stairs")
	save_furniture()
	save_data.furniture = furniture_data
	var spawns = {}
	for k in get_children():
		var e: Spatial = k
		if e.is_in_group("spawn"):
			var stats = []
			if e.has_meta("stats"):
				stats = e.get_meta("stats")
			spawns[e.name] = {"groups": e.get_groups(), "xform": xform2s(e.transform), "stats": stats}
	save_data.spawns = spawns
			
func restore_furniture():
	for k in furniture_data.keys():
		var ch = get_node(k)
		for d in furniture_data[k]:
			var f = load(d.path).instance()
			ch.add_child(f)
			f.transform = s2xform(d.xform)
func restore():
	for k in save_data.exits:
		open_path(k)
	for k in save_data.windows:
		open_window(k)
	if save_data.master_room:
		set_meta("master_room", true)
	if save_data.kitchen_room:
		set_meta("kitchen_room", true)
	if save_data.jail_room:
		set_meta("jail_room", true)
	if save_data.stairs >= 0:
		set_meta("stairs", save_data.stairs)
	create_rooms()
	furniture_data = save_data.furniture
	restore_furniture()
	var spawns = save_data.spawns
	for k in spawns.keys():
		var e = Spatial.new()
		for g in spawns[k].groups:
			e.add_to_group(g)
		e.transform = s2xform(spawns[k].xform)
		add_child(e)
		e.name = k
func _ready():
	tw2d = $c_modular/w2d.transform
	te2d = $c_modular/e2d.transform
	tn2d = $c_modular/n2d.transform
	ts2d = $c_modular/s2d.transform
var open_paths = {
	OPEN_UP: {
		"rm_walls": ["n2d", "iw4d"],
		"rm": ["fc4"],
		"bit": BITUP
	},
	OPEN_DOWN: {
		"rm_walls": ["s2d", "iw11d"],
		"rm": ["fc5"],
		"bit": BITDOWN
	},
	OPEN_LEFT: {
		"rm_walls": ["w2d", "iw17d"],
		"rm": ["fc2"],
		"bit": BITLEFT
	},
	OPEN_RIGHT: {
		"rm_walls": ["e2d", "iw22d"],
		"rm": ["fc3"],
		"bit": BITRIGHT
	},
}
func open_path(flag):
	if !flag in exits:
		var kf = open_paths[flag]
		bitflags |= kf.bit
		exits.push_back(flag)
		for e in kf.rm:
			if get_node(e):
				get_node(e).queue_free()
func create_paths():
	for flag in exits:
		var kf = open_paths[flag]
		for e in kf.rm_walls:
			$c_modular.get_node(e).queue_free()
		for e in kf.rm:
			if get_node(e):
				get_node(e).queue_free()
	if exits.size() == 1 && !has_meta("master_room"):
		$fc1.queue_free()
func open_window(flag):
	if !flag in windows:
		windows.push_back(flag)
func remove_iws():
	for k in $c_modular.get_children():
		if k.name.begins_with("iw"):
			k.queue_free()
	save_data.remove_iws = true
func personal_room():
	var door_xform
	remove_iws()
	assert(exits.size() == 1)
	match(int(exits[0])):
		OPEN_UP:
			door_xform = tn2d
		OPEN_DOWN:
			door_xform = ts2d
		OPEN_LEFT:
			door_xform = tw2d
		OPEN_RIGHT:
			door_xform = te2d
		_:
			print(exits)
			assert(false)
	var door_i = door.instance()
	add_child(door_i)
	door_i.transform = door_xform
	save_data.personal_room = true
func master_room():
	remove_iws()
	for k in exits:
		var door_xform: Transform
		match(int(k)):
			OPEN_UP:
				door_xform = tn2d
			OPEN_DOWN:
				door_xform = ts2d
			OPEN_LEFT:
				door_xform = tw2d
			OPEN_RIGHT:
				door_xform = te2d
			_:
				print(exits)
				assert(false)
		var door_i = door.instance()
		add_child(door_i)
		door_i.transform = door_xform
func wall2door(n):
	var door_xform = n.transform
	var door_i
	for e in get_tree().get_nodes_in_group("door"):
		var xpos = e.global_transform.origin
		var ypos = n.global_transform.origin
		if xpos.distance_to(ypos) < 1.3:
			n.queue_free()
			return
	if has_meta("jail_room"):
		door_i = jail_door.instance()
	else:
		door_i = door.instance()
	add_child(door_i)
	door_i.transform = door_xform
	door_i.add_to_group("door")
	n.queue_free()
func exit_door(n):
	var xn = n.name
	var exit_door_xform = n.transform
	var xdoor_i = outside_door.instance()
	add_child(xdoor_i)
	xdoor_i.transform = exit_door_xform
	xdoor_i.name = xn
	n.queue_free()
	xdoor_i.add_to_group("exit_door")
func wall2window(n):
	var window_xform = n.transform
	var xn = n.name
	var window_i
	window_i = window.instance()
	add_child(window_i)
	window_i.name = xn
	window_i.transform = window_xform
	n.queue_free()

var bitdata = {
	BITLEFT: {
		"doors": ["iw17d"],
		"path_remove": ["path_west"],
		"nodes_remove": ["iw1", "iw2d", "iw3", "iw8", "iw9d", "iw10"]
	},
	BITRIGHT: {
		"doors": ["iw22d"],
		"path_remove":  ["path_east"],
		"nodes_remove": ["iw5", "iw6d", "iw7", "iw12", "iw13d", "iw14"]
	},
	BITUP: {
		"doors": [],
		"path_remove": ["path_north"],
		"nodes_remove": ["iw15", "iw16d", "iw23d", "iw24"]
	},
	BITDOWN: {
		"doors": [],
		"path_remove": ["path_south"],
		"nodes_remove": ["iw18d", "iw19", "iw20", "iw21d"]
	}
}

var master_metas = ["master_room", "kitchen_room", "exit_room", "stairs"]
func create_rooms():
	assert(exits.size() > 0)
	create_paths()
	var is_master_room = false
	for m in master_metas:
		if has_meta(m):
			is_master_room = true
	if is_master_room:
		master_room()
		if has_meta("exit_room"):
			assert(windows.size() > 0)
			for e in get_children():
				if e.is_in_group("furniture"):
					e.queue_free()
		if has_meta("stairs"):
			var stairs_i = stairs.instance()
			add_child(stairs_i)
			for e in get_children():
				if e.is_in_group("furniture"):
					e.queue_free()
			if get_meta("stairs") > 0:
				$c_modular/floor.queue_free()
	elif exits.size() == 1:
		personal_room()
	elif exits.size() == 4:
		wall2door($c_modular/iw2d)
		wall2door($c_modular/iw6d)
		wall2door($c_modular/iw9d)
		wall2door($c_modular/iw13d)
	else:
		for e in [BITLEFT, BITRIGHT, BITUP, BITDOWN]:
			if bitflags & e == 0:
				for g in bitdata[e].doors:
					wall2door($c_modular.get_node(g))
				for g in bitdata[e].path_remove:
					get_node(g).queue_free()
				for g in bitdata[e].nodes_remove:
					$c_modular.get_node(g).queue_free()
	var external = {
		OPEN_LEFT: ["w1", "w2d", "w3"],
		OPEN_DOWN: ["s1", "s2d", "s3"],
		OPEN_RIGHT: ["e1", "e2d", "e3"],
		OPEN_UP: ["n1", "n2d", "n3"]
	}
	for p in windows:
		for q in external[p]:
			var n = $c_modular.get_node(q)
			if !p in outside_doors:
				wall2window(n)
	for p in outside_doors:
		var q = external[p][1]
		var n = $c_modular.get_node(q)
		exit_door(n)
