#pragma once
#include <string>
#include <memory>
#include <vector>
#include "Event.hpp"

class Component;
class Transform;

// Game Entity. Container for Components.
class GameObject final {
public:
	GameObject();
	~GameObject();

	bool IsActive();
	void ToggleActive();
	// Add new Component of type T to this GameObject. Returns the Component as T
	template<typename T>
	std::shared_ptr<T> AddComponent();
	// Get a std::vector of all Components of type T attached to this GameObject.
	template<typename T>
	std::vector<std::shared_ptr<T>> GetComponents();

	std::shared_ptr<Transform> GetTransform();

	const std::vector<std::shared_ptr<GameObject>>& GetChildren();
	void InitialiseSelf(); // Run when the GameObject is loaded.
	void Initialise();     // Run once all GameObjects are loaded.

	// Events for Components.
	Event<void()> OnInitialiseSelf; // When this GameObject is loaded.
	Event<void()> OnInitialise;     // Once all GameObjects are loaded.
	Event<void()> OnEnable;         // When this GameObject becomes active.
	Event<void()> OnDisable;        // When this GameObject becomes inactive.

	void setId(int id); // Should this be public?
	int getId();
	void setActive(bool active);
	void setName(std::string name);

	std::string GetName();
private:
	bool active = false;
	int id;
	std::string name;
	std::weak_ptr<GameObject> parent;
	std::vector<std::shared_ptr<GameObject>> children;
	std::shared_ptr<Transform> transform;
	std::vector<std::shared_ptr<Component>> components;
};

template<typename T>
std::shared_ptr<T> GameObject::AddComponent() {
	static_assert(std::is_convertible<T*, Component*>::value,"Can only add Components!");
	static_assert(!std::is_convertible<T*, Transform*>::value, "Cannot add Transform manually. All GameObjects have a Transform automatically");
	auto newComponent = std::shared_ptr<T>(new T(this));
	components.push_back(newComponent);
	return newComponent;
}

template<typename T>
std::vector<std::shared_ptr<T>> GameObject::GetComponents() {
	static_assert(std::is_convertible<T*, Component*>::value, "Can only get Components!");
	static_assert(!std::is_convertible<T*, Transform*>::value, "Don't use GameObject::GetComponents<Transform>(). Use GameObject::GetTransform() instead.");
	std::vector<std::shared_ptr<T>> results;
	for (auto component : components) {
		auto typed = std::dynamic_pointer_cast<T>(component);
		if (typed != nullptr) results.push_back(typed);
	}
	return results;
}
