extends Node

signal character_ready

export var trailer_house: PackedScene
export var palace: PackedScene
export var car: PackedScene

onready var obj_names = {
	"palace": palace,
	"trailer_house": trailer_house,
}

var done = false

var side_wall: PackedScene = preload("res://objects/wall-side.scn")
var bottom_side: PackedScene = preload("res://objects/bottom-side.scn")
var bottom: PackedScene = preload("res://objects/bottom.scn")
var bottom_wheels: PackedScene = preload("res://objects/bottom-wheels.scn")
var entry: PackedScene = preload("res://objects/entry.scn")
var roof_floor: PackedScene = preload("res://objects/roof-floor.scn")
var roof_floor_range: PackedScene = preload("res://objects/roof-floor-range.scn")
var wall_internal:  PackedScene = preload("res://objects/wall-internal.scn")
var window_narrow: PackedScene = preload("res://objects/window-narrow.scn")
var window_wide: PackedScene = preload("res://objects/window-wide.scn")
var wall_solid: PackedScene = preload("res://objects/wall-solid.scn")

var courtyard_tile: PackedScene = preload("res://objects/courtyard-tile.scn")
var foundation_tile: PackedScene = preload("res://objects/foundation.scn")
var room_tile: PackedScene = preload("res://objects/block-room-corridoor.scn")
var tower_walls_tile: PackedScene = preload("res://objects/tower-walls.scn")
var tower_floor_tile: PackedScene = preload("res://objects/tower_floor.scn")
var stairs_tile: PackedScene = preload("res://objects/stairs.scn")
var gate_bottom_tile: PackedScene = preload("res://objects/gate_bottom.scn")
var gate_top_tile: PackedScene = preload("res://objects/gate-top.scn")
var entry_tile: PackedScene = preload("res://objects/block-room-entry.scn")
var roof_tile: PackedScene = preload("res://objects/roof.scn")
var tower_roof_tile: PackedScene = preload("res://objects/tower-roof.scn")
var courtroom: PackedScene = preload("res://objects/courtroom.tscn")
var bandit_camp: PackedScene = preload("res://bandit_camp.tscn")
var hostile_dwelling: PackedScene = preload("res://hostile_dwelling.tscn")

onready var palace_map_data = {
	"courtyard_tile": courtyard_tile,
	"foundation_tile": foundation_tile,
	"room_tile": room_tile,
	"tower_walls_tile": tower_walls_tile,
	"tower_floor_tile": tower_floor_tile,
	"stairs_tile": stairs_tile,
	"gate_bottom_tile": gate_bottom_tile,
	"gate_top_tile": gate_top_tile,
	"entry_tile": entry_tile,
	"roof_tile": roof_tile,
	"tower_roof_tile": tower_roof_tile,
	"courtroom": courtroom,
}

onready var buildings = {
	"trailer_house": preload("res://buildings/trailer-house.gd").new(),
}

func spawn_building(bname: String, xform: Transform) -> void:
	if !buildings.has(bname):
		return
	var objects = buildings[bname].build_house(xform)
	spawn_house_objects(xform, objects)


var traffic_rnd: RandomNumberGenerator
var traffic_astar: AStar
var towns = 0
func setup_town(site):
	if !RoadsData.site_is_town(site):
		print("site ", site, " type: ", RoadsData.get_site_type(site))
		return
	var poly = RoadsData.get_site_polygon_3d(site)
	var height = RoadsData.get_site_avg_height(site)
	var border2 = RoadsData.get_site_border(site, 60)
	var aabbs = []
	var poly2 = []
	poly2.resize(border2.size())
	for p in range(border2.size()):
		poly2[p] = Vector2(border2[p].x, border2[p].z)
	var center = Vector3()
	for p in poly:
		center += p
	center /= poly.size()
	center.y = height
	var infl = RoadsData.get_influence_cached(center.x, center.y, 256)
	center.y = infl.y
	var radial_points = RoadsData.get_site_radial_points(site, 32.0, 64.0)
	var max_r = 0.0
	for p in range(radial_points.size()):
		var ep = radial_points[p]
		var dst = ep.distance_to(center)
		if max_r < dst:
			max_r = dst
	for p in range(radial_points.size()):
		var ep = radial_points[p]
		var miff = RoadsData.get_influence_cached(ep.x, ep.z, 256.0)
		ep.y = center.y
		var d = (center - ep).normalized()
		assert(d.length_squared() > 0)
		var dst = ep.distance_to(center)
		print(dst)
		if dst < 64.0 + 12 + 8 + 4:
			continue
		var step = 32.0
		var pstart = ep + d * 32.0
		dst -= 32.0
		while dst > 0.0:
			var ok = true
			miff = RoadsData.get_influence_cached(pstart.x, pstart.z, 256.0)
			if miff.x > 0.0:
				ok = false
			if ok:
				pstart.y = miff.y
				if !Geometry.is_point_in_polygon(Vector2(pstart.x, pstart.z), poly2):
					ok = false
			if ok:
				for b in aabbs:
					if b.has_point(pstart):
						ok = false
			if ok:
				var xform = Transform(Basis(), pstart).looking_at(pstart + d, Vector3.UP)
				spawn_building("trailer_house", xform)
				var aabb = AABB(pstart, Vector3())
				aabb = aabb.grow(32)
				aabbs.push_back(aabb)
				print("placed to: ", pstart)
			pstart = pstart + d * step
			dst -= step
	towns += 1

func setup_first_town(site):
	assert(!done)
	var poly = RoadsData.get_site_polygon_3d(site)
	var height = RoadsData.get_site_avg_height(site)
	var border = RoadsData.get_site_border(site, 32)
	var border1a = RoadsData.get_site_border(site, 42)
	var border2 = RoadsData.get_site_border(site, 60)

	var poly2 = []
	poly2.resize(border2.size())
	for p in range(border2.size()):
		poly2[p] = Vector2(border2[p].x, border2[p].z)
	var center = Vector3()
	for p in poly:
		center += p
	center /= poly.size()
	center.y = height
	var infl = RoadsData.get_influence_cached(center.x, center.z, 256)
	center.y = infl.y
	print("first town center: ", center)
	var radial_points = RoadsData.get_site_radial_points(site, 32.0, 64.0)
	var max_r = 0.0
	for p in range(radial_points.size()):
		var ep = radial_points[p]
		var dst = ep.distance_to(center)
		if max_r < dst:
			max_r = dst
	var u = radial_points.size()
	var v = int(max_r / 32)
	var grid = []
	grid.resize(u * v)
	for p in range(u):
		var start = radial_points[p]
		var end = center
		var step = (end - start).normalized() * (end - start).length() / float(v + 1)
		var val = start
		for q in range(v):
			grid[p * v + q] = {"position": val}
	var aabbs = []
	var palace_aabb = AABB(center, Vector3())
	palace_aabb = palace_aabb.grow(48)
	print(palace_aabb)
	aabbs.push_back(palace_aabb)
#	stream_obj("palace", Transform(Basis(), center))
	
	for p in range(radial_points.size()):
		var ep = radial_points[p]
		var miff = RoadsData.get_influence_cached(ep.x, ep.z, 256.0)
		ep.y = center.y
		var d = (center - ep).normalized()
		assert(d.length_squared() > 0)
		var dst = ep.distance_to(center)
		print(dst)
		if dst < 64.0 + 12 + 8 + 4:
			continue
		var step = 32.0
		var pstart = ep + d * 32.0
		dst -= 32.0
		while dst > 0.0:
			var ok = true
			miff = RoadsData.get_influence_cached(pstart.x, pstart.z, 256.0)
			if miff.x > 0.0:
				ok = false
			if ok:
				pstart.y = miff.y
				if !Geometry.is_point_in_polygon(Vector2(pstart.x, pstart.z), poly2):
					ok = false
			if ok:
				for b in aabbs:
					if b.has_point(pstart):
						ok = false
			if ok:
				var xform = Transform(Basis(), pstart).looking_at(pstart + d, Vector3.UP)
#				stream_obj("trailer_house", xform)
				spawn_building("trailer_house", xform)
				var aabb = AABB(pstart, Vector3())
				aabb = aabb.grow(32)
				aabbs.push_back(aabb)
				print("placed to: ", pstart)
			pstart = pstart + d * step
			dst -= step
	towns += 1
	done = true

func setup_traffic(site):
	var poly = RoadsData.get_site_polygon_3d(site)
	var lp = get_tree().root
	for p in range(poly.size()):
		var p1 = poly[p]
		var p2 = poly[(p + 1) % poly.size()]
		var n = (p2 - p1).cross(Vector3.UP).normalized()
		var t = (p2 - p1).normalized()
		var l = p1.distance_to(p2)
		assert(l > 0)
		assert(t.length_squared() > 0)
		var xpos = p1 + t * 8.0
		var xe = 128.0
		if l < xe + 16.0:
			xpos = p1.linear_interpolate(p2, 0.5)
			var x = xpos 
			var xform = Transform(Basis(), x).looking_at(x - t * 3.0, Vector3.UP)
			var c = Spatial.new()
			lp.add_child(c)
			c.transform = xform
			c.add_to_group("spawn")
			c.add_to_group("keep")
			c.add_to_group("traffic_spawn")
		else:
			while l > xe + 16.0:
				var x = xpos
				var xform = Transform(Basis(), x).looking_at(x - t * 3.0, Vector3.UP)
				var c = Spatial.new()
				lp.add_child(c)
				c.transform = xform
				c.add_to_group("spawn")
				c.add_to_group("keep")
				c.add_to_group("traffic_spawn")
				xpos += t * xe
				l -= xe
func _ready():
	traffic_rnd = RandomNumberGenerator.new()
	traffic_rnd.randomize()
	traffic_astar = AStar.new()
	for k in obj_names.keys():
		Spawner.add_scene(k, obj_names[k])
	for k in palace_map_data.keys():
		Spawner.add_scene(k, palace_map_data[k])
	var parts = {
		"side_wall": side_wall,
		"bottom_side": bottom_side,
		"bottom": bottom,
		"bottom_wheels": bottom_wheels,
		"entry": entry,
		"roof_floor": roof_floor,
		"roof_floor_range": roof_floor_range,
		"wall_internal": wall_internal,
		"window_narrow": window_narrow,
		"window_wide": window_wide,
		"wall_solid": wall_solid
	}
	for k in parts.keys():
		Spawner.add_scene(k, parts[k])
#	Traffic.set_min_spawn_distance(50.0)
#	Traffic.set_deny_physics()
#	Traffic.set_physics_distance(Vector3(30, -10, 40))
#	Traffic.set_debug(true)
	Traffic.set_spawn_cooldown(10, 15)
	Traffic.set_default_speed(8.5)
	Traffic.add_traffic_vehicle(car)
	var water_mat = load("res://water/Water.material")
	Water.set_material(water_mat)
	connect("character_ready", self, "char_ready")

var delay = 3.0
var state = 0
func _process(delta):
	match state:
		0:
			delay -= delta
			if delay < 0:
				state = 1
		1:
#			Spawner.update_view(self, 200)
			var sc = get_tree().root
			var viewport: = get_viewport()
			if !viewport:
				return
			var cam: = viewport.get_camera()
			if !cam:
				return
			var cam_xform: = cam.global_transform
			# building parts
#			call_deferred("real_spawn_child")

func stream_obj(obj: String, xform: Transform):
	Spawner.place_scene(obj, xform)
var can_spawn = false
func char_ready():
	can_spawn = true
func _physics_process(delta):
	var cam = get_viewport().get_camera()
	if !cam:
		return
	match state:
		1:
			var space_state = get_viewport().get_world().direct_space_state
			# probaly should not be here
			if can_spawn:
				for n in get_tree().get_nodes_in_group("spawn"):
					var ok = false
#					if !n.is_in_group("keep"):
#						var where = n.get_global_transform().origin
#						var from = where
#						var to = where
#						from.y -= 8.0
#						to.y += 8.0
#						var result = space_state.intersect_ray(from, to)
#						if result.empty() || !result.has("collider"):
#							continue
#						if result.collider:
#							n.global_transform.origin = result.position
#							ok = true
					ok = true
					if ok || n.is_in_group("keep"):
						var base = ["cmdq", "marker", "hurtboxes"]
						var g = ""
						if n.is_in_group("male"):
							g = "male"
						elif n.is_in_group("female"):
							g = "female"
							if n.is_in_group("mystress"):
								base.push_back("mystress")
						if n.is_in_group("student"):
							base.push_back("student")
						elif n.is_in_group("bandit"):
							base.push_back("bandit")
						if g.length() > 0:
							print(g, " ", base)
							CharacterSystemWorld.create_character_in_pool(g, base, n.global_transform)
							n.queue_free()
						elif n.is_in_group("car"):
							var p1 = cam.global_transform.origin
							var p2 = n.global_transform.origin
							if p1.distance_squared_to(p2) < 5000.0:
								var c = car.instance()
								c.add_to_group("saved_vehicle")
								var p = get_tree().root
								p.add_child(c)
								c.global_transform = n.global_transform
								n.queue_free()

# buildings

#var spawn = []
onready var spawn_lock: Mutex = Mutex.new()

func spawn_child(n: String, xform: Transform) -> void:
	spawn_lock.lock()
	Spawner.queue_place_scene(n, xform)
	spawn_lock.unlock()

func update_node_position(n):
	var space_state = get_viewport().get_world().direct_space_state
	var where = n.get_global_transform().origin
	var from = where
	var to = where
	from.y -= 8.0
	to.y += 8.0
	var result = space_state.intersect_ray(from, to)
	if result.empty() || !result.has("collider"):
		return
	else:
		n.global_transform.origin = result.position

func spawn_house_objects(place: Transform, objects) -> void:
	for obj in objects:
		var x = obj.xform
		for w in obj.data:
			if w.ends_with("_rotated"):
				x.basis = x.basis.rotated(Vector3(0, 1, 0), PI)
				var n = w.replace("_rotated", "")
				spawn_child(n, place * x)
			else:
				spawn_child(w, place * x)

static func get_place_rnd(xform: Transform):
	var rnd = RandomNumberGenerator.new()
	var s = int(xform.origin.x + 100 * xform.origin.z * 2) % 0x1ffffff
	rnd.seed = s
	return rnd

func setup_bandit_camp(site):
	var poly = RoadsData.get_site_polygon_3d(site)
	var height = RoadsData.get_site_avg_height(site)
	var center = Vector3()
	for p in poly:
		center += p
	center /= poly.size()
	center.y = height
	var xform = Transform(Basis(), center)
	var camp = bandit_camp.instance()
	get_tree().root.add_child(camp)
	camp.global_transform = xform
	return camp
func setup_bandit_camp_outskirts(site):
	var b = RoadsData.get_site_border(site, 96.0)
	for e in range(min(4, b.size())):
		if scenario.camp_dwellings.has(e):
			if scenario.camp_dwellings[e].health <= 0:
				continue
		var h_xform = Transform(Basis(), b[e])
		var dwg = hostile_dwelling.instance()
		get_tree().root.add_child(dwg)
		dwg.global_transform = h_xform
		if !scenario.camp_dwellings.has(e):
			scenario.camp_dwellings[e] = {}
			scenario.camp_dwellings[e].health = 100.0
