/*	Author: Magdin Stoica
	Supervisor: Dr. Ryan Leduc
	
	Project created in conformity with the requirements for the Degree of Master of Engineering in Software Engineering, 
	Computing and Software Department, 
	McMaster University
	2003 - 2007
*/

#pragma once

#include <string>
#include <assert.h>
#include "NameValidator.h"
#include "DesTypes.h"

namespace DESpot
{

class DesEvent
{
	//Types_____________________________
	public:
		//#pragma region DesEvent Typedefs
		typedef unsigned char Props;
		typedef unsigned long long Index;
		typedef unsigned long long Count;
		//#pragma endregion

		//#pragma region DesEvent::ID Class Definition
		class ID
		{
			public:
				ID()
				{
					m_id = cInvalidIndex;
				}
				
				ID(const Index& id)
				{
					m_id = id;
				}
				
				ID(const ID& eventId)
				{
					m_id = eventId.m_id;
				}

				ID& operator = (const ID& eventId) 
				{
					if (this != &eventId)
					{
						m_id = eventId.m_id;
					}
					return *this; 
				}

				operator Index() const
				{
					return m_id;
				}

				Index id() const
				{
					return m_id;
				}

				
				bool operator == (const ID& other)
				{
					return m_id == other.m_id;
				}

				bool operator != (const ID& other)
				{
					return m_id != other.m_id;
				}

				bool isValid() const
				{
					return m_id != (Index)(-1);
				}

			private:
				Index m_id;
				static const Index cInvalidIndex;

			public:
				static const ID	cInvalidId;
		};
		//#pragma endregion

	public:
		DesEvent(void);
		DesEvent(DesEvent::ID eventId);
		DesEvent(DesEvent::ID eventId, const DesEvent& otherEvent);
		DesEvent(const DesEvent& otherEvent);

		~DesEvent(void);

		//copying
		DesEvent& operator= (const DesEvent& otherEvent);

		//comparing
		bool operator == (const DesEvent& otherEvent) const
		{
			return (m_id == otherEvent.m_id) && 
				   (m_name == otherEvent.m_name) &&
				   (m_alias == otherEvent.m_alias) &&
				   (m_props == otherEvent.m_props);
		}

		bool operator != (const DesEvent& otherEvent) const
		{
			return !(*this == otherEvent);
		}

	public:		
		std::wstring getName(void) const
		{
			return m_name;
		}
		
		void setName(const std::wstring& newName)
		{
			if (m_nameValidator.validate(newName))
			{
				m_name = newName;
			}
			else
			{
				std::wstring message = L"Invalid event name (";
				message += newName;
				message += L"). Use an alpha-numeric string (a-z;A-Z;0-9;.-_)";
				throw message;
			}
		}

		std::wstring getAlias(void) const
		{
			return (m_alias.empty() == false) ? m_alias : m_name;
		}
		
		void setAlias(const std::wstring& newAlias)
		{
			if (m_nameValidator.validate(newAlias))
			{
				m_alias = newAlias;
			}
			else
			{
				std::wstring message = L"Invalid event alias (";
				message += newAlias;
				message += L"). Use an alpha-numeric string (a-z;A-Z;0-9;.-_)";
				throw message;
			}
		}

		EventType getType(void) const
		{
			if (m_props & cTypeDefaultFlagMask)
				return eDefaultEvent;

			if (m_props & cTypeAnswerFlagMask)
				return eAnswerEvent;
			
			if (m_props & cTypeRequestFlagMask)
				return eRequestEvent;

			if (m_props & cTypeLDataFlagMask)
				return eLDataEvent;

			if (m_props & cTypeHighLevelFlagMask)
				return eHighLevelEvent;

			if (m_props & cTypeLowLevelFlagMask)
				return eLowLevelEvent;

			//if we got here the type is invalid
			assert(false);
			throw EX("Unknown event type")
		}

		EventType getSimpleType(void) const
		{
			if (m_props & cTypeDefaultFlagMask)
				return eDefaultEvent;

			if (m_props & cTypeAnswerFlagMask)
				return eAnswerEvent;
			
			if (m_props & cTypeRequestFlagMask)
				return eRequestEvent;

			if (m_props & cTypeLDataFlagMask)
				return eLDataEvent;

			if (m_props & cTypeHighLevelFlagMask)
				return eDefaultEvent;

			if (m_props & cTypeLowLevelFlagMask)
				return eDefaultEvent;

			//if we got here the type is invalid
			assert(false);
			throw EX("Unknown event type")
		}

		void setType(EventType type)
		{
			//reset the type bits
			setProps(m_props & ~(cTypeDefaultFlagMask | cTypeAnswerFlagMask | 
				                 cTypeRequestFlagMask | cTypeLDataFlagMask | 
								 cTypeHighLevelFlagMask | cTypeLowLevelFlagMask));

			switch(type)
			{
				case eDefaultEvent:
					setProps(m_props | cTypeDefaultFlagMask);
					break;

				case eAnswerEvent:
					setProps(m_props | cTypeAnswerFlagMask);
					break;

				case eRequestEvent:
					setProps(m_props | cTypeRequestFlagMask);
					break;
					
				case eLDataEvent:
					setProps(m_props | cTypeLDataFlagMask);
					break;

				case eHighLevelEvent:
					setProps(m_props | cTypeHighLevelFlagMask);
					break;

				case eLowLevelEvent:
					setProps(m_props | cTypeLowLevelFlagMask);
					break;

				default:
					assert(false);
					throw EX("Unknown event type. Cannot set type of event.")
			}
		}
		
		bool isUsed(void) const
		{
			return (m_props & cUsedFlagMask) != 0;
		}

		void setUsed(bool isUsed = true)
		{
			if (isUsed)
			{
				setProps(m_props | cUsedFlagMask);
			}
			else
			{
				setProps(m_props & ~cUsedFlagMask);
			}
		}
		
		bool isControllable(void) const
		{
			return (m_props & cControllableFlagMask) != 0;
		}

		void setControllable(bool isControllable = true)
		{
			if (isControllable)
			{
				setProps(m_props | cControllableFlagMask);
			}
			else
			{
				setProps(m_props & ~cControllableFlagMask);
			}
		}

		void toggleControllable()
		{
			setControllable(!isControllable());
		}

		Props  getProps(void) const
		{
			return m_props;
		}
		
		void setProps(const Props& newProps)
		{
			m_props = newProps;
		}

		const ID getId() const
		{
			return m_id;
		}

	//data
	private:
		std::wstring   m_name;
		std::wstring   m_alias;
		Props     m_props;
		const ID  m_id;

		static NameValidator m_nameValidator;

	//constants
	private:
		static const Props cUsedFlagMask;
		static const Props cTypeDefaultFlagMask;			
		static const Props cTypeAnswerFlagMask;			
		static const Props cTypeRequestFlagMask;			
		static const Props cTypeLDataFlagMask;			
		static const Props cTypeHighLevelFlagMask;			
		static const Props cTypeLowLevelFlagMask;			
		static const Props cControllableFlagMask;			
		static const Props cDefaultEventProps;			
};


} //end of namespace DESpot
