#ifndef ROGUE_CHARACTER_H
#define ROGUE_CHARACTER_H

#include "core/roguedefs.h"
#include <functional>
#include <memory>

namespace rogue {
class World;
class Character;

// Character Logic, abstract Subclass Sandbox Pattern implementation that declares the necessary bits to communicate with the character
struct CharacterLogic {
	virtual ~CharacterLogic();

	virtual Tile move() = 0;
protected:
	// CharacterLogic doesn't need to do anything else,
	//so we limit their interaction with the world to just the character (which exposes the parent World instance)
	Character const *character{nullptr};
private:
	friend class Character; // hide the character
	void set_character(Character *character);
};

// Character's stats as a struct that's easilly made into a flyweight to share around
struct CharacterData {
	int health{1};
	int damage{1};
	Sprite sprite{0};
};

// Character class, serves as the primary entity type in the game
struct Character {
	Character() = default;
	Character(Tile location, CharacterLogic *logic, CharacterData const *stats);
	typedef std::function<Character(Tile)> Spawner; // <functional> style Factory Pattern
	void act();
	void draw();
	bool deal_damage(int damage);

	CharacterData const *data{}; // Type Object pattern implemented as a Flyweight
								 // marked constant as it is shared between instances of the same type.
	int health{1};
	Tile location{0, 0};
	World *world{nullptr};
private:
	// The Subclass Sandbox Pattern instance that implements the behavioural logic of this character.
	std::shared_ptr<CharacterLogic> logic{nullptr};
public:
	// Simplifies creating a new character logic instance for construction of a Character
	// bind into a Factory object using make_factory to pass to world generators
	template <typename TLogicType>
	static Character create(CharacterData const &stats, Tile tile);
	// binds Character::create into a std::function,
	// making a Factory Pattern object to pass to WorldGenerator as a spawner
	template <typename TLogicType>
	static Spawner make_spawner(CharacterData const &stats);
};

template <typename TLogicType>
Character Character::create(CharacterData const &stats, Tile tile) {
		return Character(tile, new TLogicType, &stats);
}

template <typename TLogicType>
Character::Spawner Character::make_spawner(CharacterData const &stats) {
	return std::bind(&Character::create<TLogicType>, stats, std::placeholders::_1);
}

struct NullCharacterLogic : public CharacterLogic {
 	virtual Tile move() override;
};
}

#endif // !ROGUE_CHARACTER_H
