extends Node
## Manages the connection to the home automation system and provides a unified interface to the different home automation systems.

const Hass = preload ("res://lib/home_apis/hass/hass.gd")
const HassWebSocket = preload ("res://lib/home_apis/hass_ws/hass.gd")
const VoiceAssistant = preload ("res://lib/home_apis/voice_handler.gd")

const apis = {
	"hass": Hass,
	"hass_ws": HassWebSocket
}

const methods = [
	"get_devices",
	"get_device",
	"get_state",
	"set_state",
	"watch_state"
]

## Emitted when the connection to the home automation system is established
signal on_connect()

## Emitted when the connection to the home automation system is lost
signal on_disconnect()

## The current home automation system adapter
var api: Node

func _ready():
	print("HomeApi ready")

	var success = Store.settings.load_local()

	if success:
		start_adapter(Store.settings.type.to_lower(), Store.settings.url, Store.settings.token)

## Starts the adapter for the given type and url
func start_adapter(type: String, url: String, token: String):
	print("Starting adapter: %s" % type)
	if api != null:
		api.on_connect.disconnect(_on_connect)
		api.on_disconnect.disconnect(_on_disconnect)
		remove_child(api)
		api.queue_free()
		api = null

	api = apis[type].new(url, token)
	add_child(api)

	api.on_connect.connect(func():
		Store.house.load_local()
		on_connect.emit()
	)

	api.on_disconnect.connect(func():
		on_disconnect.emit()
	)

	for method in methods:
		assert(api.has_method(method), "%s Api does not implement method: %s" % [type, method])

func _on_connect():
	on_connect.emit()

func _on_disconnect():
	on_disconnect.emit()

## Returns true if the adapter is connected to the home automation system
func has_connected():
	if api == null:
		return false
	return api.has_connected()

## Get a list of all devices
func get_devices():
	assert(has_connected(), "Not connected")
	return await api.get_devices()

## Get a single device by id
func get_device(id: String):
	assert(has_connected(), "Not connected")
	return await api.get_device(id)

## Returns the current state of an entity
func get_state(entity: String):
	assert(has_connected(), "Not connected")
	return await api.get_state(entity)

## Updates the state of the entity and returns the resulting state
func set_state(entity: String, state: Variant, attributes: Dictionary={}):
	assert(has_connected(), "Not connected")
	return await api.set_state(entity, state, attributes)

## Watches the state and each time it changes, calls the callback with the changed state, returns a function to stop watching the state
func watch_state(entity: String, callback: Callable):
	assert(has_connected(), "Not connected")
	return api.watch_state(entity, callback)

## Returns true if the adapter has an integration in the home automation system
## allowing to send the room position of the headset.
func has_integration() -> bool:
	if has_connected() == false||api.has_method("has_integration") == false:
		return false

	return api.has_integration()

## Updates the room position of the headset in the home automation system
func update_room(room: String) -> void:
	if has_connected() == false||api.has_method("update_room") == false:
		return

	api.update_room(room)

## Returns the VoiceHandler if the adapter has a voice assistant
func get_voice_assistant() -> VoiceAssistant:
	assert(has_connected(), "Not connected")

	if api.has_method("get_voice_assistant") == false:
		return null

	return api.get_voice_assistant()