class_name PropertySaveRestoreStack extends Node
## Allows saving and restoring sets of properties on a target Node.

## Stores a batch of saved properties to be restored all at once on pop.
class Frame:
	## The saved properties to be restored when this frame is popped.
	var saved_properties: Dictionary[NodePath, Variant]
	## Saves properties to this frame and then overwrites them on the target.
	func save_and_modify(target: Node, properties: Dictionary) -> void:
		for key in properties:
			var property_path := key as NodePath
			saved_properties[property_path] = target.get_indexed(property_path)
			target.set_indexed(property_path, properties[property_path])
	## Restores saved properties. Called when the frame is popped.
	func restore(target: Node) -> void:
		for property_path in saved_properties:
			target.set_indexed(property_path, saved_properties[property_path])

## All property paths are understood as relative to this Node.
## [br][br]
## Altering this property at runtime will immediately empty the stack,
## restoring all saved properties to the previous target in the process.
@export var target: Node:
	set(value):
		if value != target:
			while !is_empty():
				pop()
			target = value

var _stack: Array[Frame]

## Saves properties to a new stack frame and then overwrites them on the target.
func push(properties: Dictionary) -> void:
	var frame := Frame.new()
	_stack.push_back(frame)
	frame.save_and_modify(target, properties)

## Pops the top stack frame and restores saved properties from it.
func pop() -> void:
	var frame: Frame = _stack.pop_back()
	if frame:
		frame.restore(target)

## Whether the stack is empty.
func is_empty() -> bool:
	return _stack.is_empty()