
extends Node

class ScriptNode:
	var scene
	
	
	func step_type():
		return ""	
	func get_scene():
		return scene
	func init(scene):
		print("starting step " + step_type())
		self.scene = scene 
		pass
	func do(delta):
		pass
	func is_done():
		return true
	
	func interrupt():
		return false
	
	func handle_input(event: InputEvent) -> bool:
		return false

class Say:
	extends ScriptNode
	var text
	var subject : Node2D = null
	var done = false
	var stop = true
	var started = false
	var t
	func step_type():
		return "Say " + text
	func init(scene):
		super(scene)
		
		#subject.play_animation("talk", 0)
	
	func do(delta):
		if !started:
			started = true
			t = Timer.new()
			t.autostart = true
			t.wait_time = 5.0
			subject.add_child(t)
			
			var label: Label = subject.find_child("talk-label")
			#label.set_text(text)
			#subject.find_child("label-root").show()
			await scene.find_child("dialogue").say(text)

			await t.timeout
			subject.remove_child(t)
			if stop:
				await scene.find_child("dialogue").done_say()
			#subject.find_child("label-root").hide()
			#subject.stop_animation()
			#subject.play_animation_raw("idle_" + subject.facing)
			done = true
		started = true
		
		
	func is_done():

		return done
	
	func interrupt():
		done = true
		if stop:
			await scene.find_child("dialogue").done_say()
		subject.stop_animation()
		subject.play_animation_raw("idle_" + subject.facing)
		subject.find_child("label-root").hide()


class Narrate:
	extends ScriptNode
	var text
	var done = false
	var started = false
	var dismissed_by_click = false
	var fade_in_duration = 0.3
	var fade_out_duration = 0.3
	var chars_per_second = 20.0  # Reading speed
	var min_duration = 2.0
	var max_duration = 10.0
	
	func step_type():
		return "Narrate " + text.substr(0, 20)
	
	func calculate_duration() -> float:
		var duration = text.length() / chars_per_second
		return clamp(duration, min_duration, max_duration)
	
	func do(delta):
		if !started:
			started = true
			dismissed_by_click = false
			
			# Show narrator overlay with fade in
			var narrator_overlay = scene.find_child("NarratorOverlay")
			if narrator_overlay:
				var panel = narrator_overlay.find_child("Panel")
				var label = panel.find_child("Label")
				if label:
					label.text = text
				if panel:
					panel.modulate = Color(1, 1, 1, 0)
					panel.show()
					
					# Fade in
					var tween = scene.create_tween()
					tween.tween_property(panel, "modulate", Color(1, 1, 1, 1), fade_in_duration)
					await tween.finished
			
			# Set global flag that narrator is active
			scene.set_meta("narrator_active", true)
			
			# Calculate display duration
			var display_duration = calculate_duration()
			
			# Wait for either timeout or click
			var timer = scene.get_tree().create_timer(display_duration)
			while timer.time_left > 0 and not dismissed_by_click:
				await scene.get_tree().process_frame
			
			# Fade out (keep narrator_active flag true during fade to prevent walk trigger)
			var overlay = scene.find_child("NarratorOverlay")
			if overlay:
				var p = overlay.find_child("Panel")
				if p and p.visible:
					var tween = scene.create_tween()
					tween.tween_property(p, "modulate", Color(1, 1, 1, 0), fade_out_duration)
					await tween.finished
					p.hide()
			
			# Clear global flag AFTER fade completes
			scene.set_meta("narrator_active", false)
			
			done = true
		started = true
	
	func is_done():
		return done
	
	func dismiss():
		dismissed_by_click = true
	
	func interrupt():
		done = true
		var overlay = scene.find_child("NarratorOverlay")
		if overlay:
			var p = overlay.find_child("Panel")
			if p:
				p.hide()
		scene.set_meta("narrator_active", false)
	
	func handle_input(event: InputEvent) -> bool:
		if event is InputEventMouseButton and Input.is_action_just_released("interact"):
			if not dismissed_by_click and not done:
				dismissed_by_click = true
				return true
		return false
		
		
class WalkTo:
	extends ScriptNode
	var path = []
	var subject : Node2D = null
	var script_fn = null
	func step_type():
		return "WalkTo " + subject.get_name()
	
	func do(delta):
				# Calculate the movement distance for this frame
		var distance_to_walk = 1500 * delta  * (subject.get_transform().get_scale().y)
		var p = subject.position
		
		if path.size()> 0:
			# Move the player along the path until he has run out of movement or the path ends.
			while distance_to_walk > 0 and path.size() > 0:
				var dir = snapped(subject.position.angle_to_point(path[0]), PI/4) / (PI/4)
				dir = wrapi(dir, 0, 8)
				var anim
				
						
					#	seek = animation_player.current_animation_position
					#animation_player.play(anim)
					#animation_player.seek(seek, true)
					#subject.find_node("AnimationPlayer").playback_speed=1.8
				var distance_to_next_point = p.distance_to(path[0])
				if distance_to_walk <= distance_to_next_point: 
					var facing = ""
					if dir == 4:
						facing="W"
						
					if dir == 5:
						facing ="NW"
					elif dir == 6:
						facing="N"
						
					elif dir ==7:
						facing="NE"
						
					elif dir == 0:
						facing="E"
					elif dir == 1:
						facing="SE"
					elif dir == 2:
						facing = "S"
					elif dir == 3:
						facing = "SW"
							
					if facing:
						var seek = 0
						subject.facing = facing
						var sprite = subject.play_animation_maintain("walk")
					# The player does not have enough movement left to get to the next point.
					p += p.direction_to(path[0]) * distance_to_walk
				else:
					# The player get to the next point
					p = path[0]
					path = path.slice(1)
				# Update the distance to walk
				distance_to_walk -= distance_to_next_point
			
			subject.position =  p;
	
	func is_done():
		if path.size() == 0:
			subject.play_animation("idle", 0)
			#subject.find_child("AnimatedSprite2D").pause()
		return path.size() == 0
class WalkToDeferred:
	extends WalkTo
	var named_to 
	var named_from
	var subject_name
	func step_type():
		return "Walk to " + named_to
	func init(scene):
		super(scene)
		print("SCENE IS", scene)
		subject = scene.get_node(subject_name)
		var s = scene.get_node("SceneViewport/background")
		var g = s.get_children()
		
		path = NavigationServer2D.map_get_path(s.map, s.pathfind.to_local(s.get_node(named_from).global_position), s.pathfind.to_local(s.get_node(named_to).global_position), true)
		print(" PATH IS", path)  
		#[Vector2(0,0), Vector2(300,300)]
		
class Transition:
	extends ScriptNode
	var path = []
	var subject : Node2D = null
	var next : Resource = null
	var script_fn = null
	var position = null
	var transitioned : String
	var running = false
	var done = false
	
	func step_type():
		return "transition"
	func do(delta):
		if not running:
			running = true
			var parent = subject.get_parent()
			
			var fade_player = subject.get_parent().get_parent().find_child("FadePlayer", true)
			fade_player.play("fade-out",0.0, 1.5)
			await fade_player.animation_finished
			var new_scene = next.instantiate()
			new_scene.name="background"		
			parent.remove_child(subject)
			var pos : Node2D = new_scene.get_node(position)
			var c = new_scene.get_children()
			if new_scene is Scene:
				new_scene.starting_point = pos.global_position
			parent.add_child(new_scene)
			
			
			
			subject.emit_signal("transitioned", new_scene)
			subject.queue_free()		
			fade_player.play("fade-in",0.0, 1.5)
			await fade_player.animation_finished
			done = true
			parent
			
	func is_done():
		return done
		
class TriggerSignal:
	extends ScriptNode
	var path = []
	var subject : Node2D = null
	var next : Resource = null
	var script_fn = null
	var running = false
	var s :String = ""
	
	func do(delta):
		pass
	#	get_tree().find_child("Node2D/background").emit_signal(s)
			
	func is_done():
		true

class SwitchCamera:
	extends ScriptNode
	var path = ""
	var d = false
	var t: SceneTreeTimer 

	func init(scene: Node2D):
		var s = scene.get_node("SceneViewport/background")
		var g = s.get_children()
		CameraTransition.transition_camera2D(scene.get_tree().root.get_node("Node2D/SceneViewport/PlayerTracker/Camera2D"), s.find_child(path))
		t = scene.get_tree().create_timer(2.0)
		
	func do(delta):
		await t.timeout
		d = true
	#	get_tree().find_child("Node2D/background").emit_signal(s)
			
	func is_done():
		return d
class ResetCamera:
	extends ScriptNode
	var path = ""
	var d = false
	var t: SceneTreeTimer 

	func init(scene: Node2D):
		var s = scene.get_node("SceneViewport/background")
		CameraTransition.transition_camera2D(scene.get_viewport().get_camera_2d(), scene.get_node("SceneViewport/PlayerTracker/Camera2D"))
		t = scene.get_tree().create_timer(2.0)
		
	func do(delta):
		await t.timeout
		d = true
	#	get_tree().find_child("Node2D/background").emit_signal(s)
			
	func is_done():
		return d
		
class ScriptEdge:
	var from : ScriptNode
	var to : ScriptNode
	
class ScriptGraph:
	var initial: ScriptNode 
	var current = []
	var nodes = []
	var edges = []
	var can_interrupt = false
	var current_scene 
	var last_node
	
	signal script_complete 
	func step(delta, scene):
		for c in range(current.size()):
			current[c].do(delta)
		var to_add = []
		for current_index in range(current.size()):
			var current_step = current[-current_index]
			if current_step.is_done():
				print("Stopping step", current_step.step_type())
				current.remove_at(-current_index)
				for edge in edges:
					if edge.from == current_step:
						to_add.push_back(edge.to)
						edge.to.init(scene)
						
		current.append_array(to_add)
		if current.size() == 0:
			emit_signal("script_complete")
			
			
	func interrupt():
		for c in current:
			c.interrupt()
		return can_interrupt
	
	func handle_input(event: InputEvent) -> bool:
		for c in current:
			if c.handle_input(event):
				return true
		return false
			

var script_graph: ScriptGraph
#var last_node: ScriptNode

func init(step: ScriptNode):
	script_graph = ScriptGraph.new()
	script_graph.initial = step
	step.init(get_node("/root/Node2D"))
	script_graph.current = [step]
	script_graph.last_node = step
	#last_node = step
	return self

func and_then(step: ScriptNode):
	script_graph.nodes.push_back(step)
	var edge = ScriptEdge.new()
	edge.from = script_graph.last_node
	edge.to = step
	script_graph.edges.push_back(edge)
	script_graph.last_node=step
	return self

func can_interrupt():
	script_graph.can_interrupt = true
	return self
	
func build(subject, on_complete):
	script_graph.connect("script_complete", Callable(subject, on_complete))
	script_graph.connect("script_complete", self.on_complete)
	return script_graph
	
func on_complete():
	current_script=null

func walk_path(subject, path):
	var walk_to = WalkTo.new()
	walk_to.subject = subject
	walk_to.path = path
	return walk_to
	
func say(subject, text, stop=true):
	var say = Say.new()
	say.subject = subject
	say.text = text
	say.stop = stop
	return say

func narrate(text):
	var narrate = Narrate.new()
	narrate.text = text
	return narrate

func dismiss_current_narrator():
	if current_script:
		for node in current_script.current:
			if node is Narrate:
				node.dismiss()
				return true
	return false

func walk_to_deferred(named_from, named_to):
	var say = WalkToDeferred.new()
	say.subject_name = "SceneViewport/background/Graham"
	say.named_from = named_from
	say.named_to = named_to
	return say
	
func trigger_signal(s):
	var thing = TriggerSignal.new()
	thing.s = s
	return thing
func transition(subject, scene, position_identifier):
	var t = Transition.new()
	t.subject = subject
	t.next = scene
	t.position = position_identifier
	return t
func switch_camera(path):
	var thing = SwitchCamera.new()
	thing.path = path 
	return thing
func reset_camera():
	var thing = ResetCamera.new()
	return thing

class GiveItem:
	extends ScriptNode
	var item_id: String = ""
	var text: String = ""
	var subject: Node2D = null
	var done = false
	var started = false

	func step_type():
		return "GiveItem " + item_id

	func init(scene):
		super(scene)

	func do(delta):
		if not started:
			started = true
			if text:
				await scene.find_child("dialogue").say(text)
			InventoryManager.acquire_item(item_id)
			done = true

	func is_done():
		return done

	func interrupt():
		done = true

class GiveItemDeferred:
	extends GiveItem
	var subject_name: String = ""

	func init(scene):
		super(scene)
		subject = scene.get_node(subject_name)

func give_item(item_id: String, text: String = ""):
	var step = GiveItem.new()
	step.item_id = item_id
	step.text = text
	return step

func give_item_deferred(item_id: String, subject_name: String, text: String = ""):
	var step = GiveItemDeferred.new()
	step.item_id = item_id
	step.subject_name = subject_name
	step.text = text
	return step
	
var current_script : ScriptGraph = null
