#ifndef COMPONENTTYPE_H_
#define COMPONENTTYPE_H_

#include <set>
#include <map>
#include <string>

namespace objectives
{

class ComponentType;

/**
 * Set of ComponentTypes.
 */
typedef std::set<ComponentType> ComponentTypeSet;

/**
 * Enumeration of Component types.
 *
 * This is a typesafe enum containing all of the available Component types,
 * such as COMP_KILL and COMP_KO. Named static instance functions are provided
 * for all of the types, and a std::map is also available to look up a
 * ComponentType instance by name (as provided by a spawnarg).
 */
class ComponentType
{
private:
	// Static enum count
	static int enumCount;

	// Static map of named ComponentType instances
	typedef std::map<std::string, ComponentType> ComponentTypeMap;
	static ComponentTypeMap& getMap();

	// Integer ID of this ComponentType
	int _id;

	// Raw name of this type
	std::string _name;

	// User-friendly display name
	std::string _displayName;

private:

	// Construct a named ComponentType
	ComponentType(const std::string& name, const std::string& displayName);

public:

	/**
	 * Get the ComponentType identified by the given name.
	 *
	 * @param name
	 * The text name of the ComponentType to retrieve, such as "kill" or "ko".
	 *
	 * @return
	 * The identified ComponentType if it exists.
	 *
	 * @exception ObjectivesException
	 * Thrown if the named ComponentType does not exist.
	 */
	static ComponentType getComponentType(const std::string& name);

	/**
	 * Get the ComponentType identified by the given ID.
	 *
	 * @param id
	 * The id of the ComponentType.
	 *
	 * @return
	 * The identified ComponentType if it exists.
	 *
	 * @exception ObjectivesException
	 * Thrown if the named ComponentType does not exist.
	 */
	static ComponentType getComponentType(int id);

	/**
	 * Get the name of this ComponentType.
	 *
	 * This returns the "raw" name of the ComponentType as used in the entity
	 * spawnargs, such as "ai_alert" or "ko".
	 */
	const std::string& getName() const {
		return _name;
	}

	/**
	 * Get the display name of this ComponentType.
	 *
	 * This returns a user-friendly display name which is suitable for
	 * identifying the ComponentType in a dialog, such as "AI is killed".
	 */
	const std::string& getDisplayName() const {
		return _displayName;
	}

	/**
	 * Get the numeric ID of this ComponentType.
	 */
	int getId() const {
		return _id;
	}

	/**
	 * @name ComponentType instances.
	 */

	//@{

	static const ComponentType& COMP_KILL();			// AI is killed
	static const ComponentType& COMP_KO();				// AI is knocked out.
	static const ComponentType& COMP_AI_FIND_ITEM();	// AI finds an item.
	static const ComponentType& COMP_AI_FIND_BODY();	// AI finds a body.
	static const ComponentType& COMP_ALERT();			// AI is alerted.
	static const ComponentType& COMP_DESTROY();		// Object is destroyed.
	static const ComponentType& COMP_ITEM();			// Player possesses a number of items or loot.
	static const ComponentType& COMP_PICKPOCKET();		// Item is pickpocketed from conscious AI.
	static const ComponentType& COMP_LOCATION();		// Item is in a particular objective location (defined by a brush).
	static const ComponentType& COMP_CUSTOM_ASYNC();	// Custom component updated by user script.
	static const ComponentType& COMP_CUSTOM_CLOCKED();	// Custom component which periodically checks a user script.
	static const ComponentType& COMP_INFO_LOCATION();	// Item is in a particular <b>info_location</b> area.
	static const ComponentType& COMP_DISTANCE();		// Two entities are within a radius of each other.
	static const ComponentType& COMP_READABLE_OPENED();		// Readable is opened
	static const ComponentType& COMP_READABLE_CLOSED();		// Readable is closed
	static const ComponentType& COMP_READABLE_PAGE_REACHED();	// A page of a readable is viewed

	//@}

	/**
	 * @name ComponentType convenience sets.
	 */

	//@{

	/** All ComponentTypes. */
	static const ComponentTypeSet& SET_ALL();

	//@}
};

/**
 * Operator less for ComponentType objects.
 *
 * This is required to allow ComponentTypes to be placed in a map or set.
 */
inline bool operator< (const ComponentType& first, const ComponentType& second)
{
	return first.getId() < second.getId();
}

}

#endif /*COMPONENTTYPE_H_*/
