// Copyright 2017-2023 the openage authors. See copying.md for legal info.

#include "evententity.h"

#include <compare>

#include "log/log.h"
#include "log/message.h"

#include "event/event.h"
#include "event/event_loop.h"
#include "event/eventhandler.h"
#include "util/fixed_point.h"


namespace openage::event {


void EventEntity::changes(const time::time_t &time) {
	// This target has some change, so we have to notify all dependents
	// that subscribed on this entity.

	if (this->parent_notifier or this->dependents.size()) {
		log::log(DBG << "Target: processing change request at t=" << time
		             << " for EventEntity " << this->idstr() << "...");
	}

	if (this->parent_notifier != nullptr) {
		this->parent_notifier(time);
	}

	// This is a maybe-erase construct so obsolete dependents are cleaned up.
	for (auto it = this->dependents.begin(); it != this->dependents.end();) {
		auto dependent = it->lock();
		if (dependent and not dependent->get_entity().expired()) {
			switch (dependent->get_eventhandler()->type) {
			case EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY:
			case EventHandler::trigger_type::DEPENDENCY:
				// Enqueue a change so that change events,
				// which depend on this target, will be retriggered

				log::log(DBG << "Target: change at t=" << time
				             << " for EventEntity " << this->idstr() << " registered");
				this->loop->create_change(dependent, time);
				++it;
				break;

			case EventHandler::trigger_type::ONCE:
				// If the dependent is a ONCE-event
				// forget the change if the once event has been notified already.
				if (dependent->get_last_changed() > time::time_t::min_value()) {
					it = this->dependents.erase(it);
				}
				else {
					this->loop->create_change(dependent, time);
					++it;
				}
				break;

			case EventHandler::trigger_type::TRIGGER:
			case EventHandler::trigger_type::REPEAT:
				// Ignore announced changes for triggered or repeated events
				// for that there is the 'DEPENDENCY' events.

				// TRIGGER events are only triggered when this entity's
				// trigger() function is called
				++it;
				break;
			}
		}
		else {
			// The dependent is no more, so we can safely forget him
			it = this->dependents.erase(it);
		}
	}
}


void EventEntity::trigger(const time::time_t &last_valid_time) {
	// notify all dependent events that are triggered `on_keyframe`
	// that the this target changed.
	// the only events that is "notified" by are TRIGGER.

	for (auto it = this->dependents.begin(); it != this->dependents.end();) {
		auto dependent = it->lock();
		if (dependent) {
			if (dependent->get_eventhandler()->type == EventHandler::trigger_type::TRIGGER) {
				log::log(DBG << "Target: trigger creates a change for "
				             << dependent->get_eventhandler()->id()
				             << " at t=" << last_valid_time);

				loop->create_change(dependent, last_valid_time);
			}

			++it;
		}
		else {
			it = this->dependents.erase(it);
		}
	}
}


void EventEntity::add_dependent(const std::shared_ptr<Event> &event) {
	this->dependents.emplace_back(event);
}

void EventEntity::show_dependents() const {
	log::log(DBG << "Dependent list:");
	for (auto &dep : this->dependents) {
		auto dependent = dep.lock();
		if (dependent) {
			log::log(DBG << " - " << dependent->get_eventhandler()->id());
		}
		else {
			log::log(DBG << " - ** outdated old reference **");
		}
	}
}

} // namespace openage::event
