#include "precompiled.h"

#include "lib/posix.h"
#include "lib/lib.h"
#include <stdio.h>
#include <map>

#include "simulation/Entity.h"
#include "ps/Vector2D.h"
#define ALLNETMSGS_IMPLEMENT

#include "NetMessage.h"

#include "ps/CLogger.h"

#define LOG_CAT_NET "net"

// NEVER modify the deserializer map outside the ONCE-block in DeserializeMessage
typedef std::map <ENetMessageType, NetMessageDeserializer> MessageDeserializerMap;
MessageDeserializerMap g_DeserializerMap;

CNetMessage::~CNetMessage()
{
	m_Type=NMT_NONE;
}

u8 *CNetMessage::Serialize(u8 *pos) const
{ return pos; }

uint CNetMessage::GetSerializedLength() const
{
	return 0;
}

CStr CNetMessage::GetString() const
{
	if (m_Type==NMT_NONE)
		return "NMT_NONE { Invalid Message }";
	else
		return CStr("Unknown Message ")+CStr(m_Type);
}

const u8 *CNetMessage::Deserialize(const u8* pos, const u8* UNUSED(end))
{
	return pos;
}

CNetMessage *CNetMessage::Copy() const
{
	LOG(ERROR, LOG_CAT_NET, "CNetMessage::Copy(): Attempting to copy non-copyable message!");
	return new CNetMessage(NMT_NONE);
}

CNetMessage *CNetMessage::DeserializeMessage(ENetMessageType type, u8 *buffer, uint length)
{
	{
	ONCE(
		SNetMessageDeserializerRegistration *pReg=&g_DeserializerRegistrations[0];
		for (;pReg->m_pDeserializer;pReg++)
		{
			g_DeserializerMap.insert(std::make_pair(pReg->m_Type, pReg->m_pDeserializer));
		}
	);
	}
	
	MessageDeserializerMap::const_iterator dEntry=g_DeserializerMap.find(type);
	if (dEntry == g_DeserializerMap.end())
	{
		LOG(WARNING, LOG_CAT_NET, "Unknown message received on socket: type 0x%04x, length %u", type, length);
		return NULL;
	}
	NetMessageDeserializer pDes=dEntry->second;
	return (pDes)(buffer, length);
}

void CNetMessage::ScriptingInit()
{
#define def(_msg) g_ScriptingHost.DefineConstant(#_msg, _msg)
	
	def(NMT_Goto);
	def(NMT_Run);
	def(NMT_Patrol);
	def(NMT_AddWaypoint);
	def(NMT_Generic);
	def(NMT_Produce);
	def(NMT_PlaceObject);
	def(NMT_NotifyRequest);
	def(NMT_FormationGoto);
	def(NMT_FormationGeneric);
}

CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSContext *cx, uintN argc, jsval *argv)
{
	debug_assert(argc >= 1);

	int msgType;

	try
	{
		msgType = ToPrimitive<int>( argv[0] );
	}
	catch(PSERROR_Scripting_ConversionFailed)
	{
		JS_ReportError(cx, "Invalid order type");
		return NULL;
	}
	
	#define ArgumentCountError() STMT(\
			JS_ReportError(cx, "Too few parameters!"); \
			return NULL; )
	#define ArgumentTypeError() STMT(\
			JS_ReportError(cx, "Parameter type error!"); \
			return NULL; )
	#define ReadPosition(_msg, _field) \
		try { \
			if (argIndex+2 > argc) \
				ArgumentCountError();\
			if (!JSVAL_IS_INT(argv[argIndex]) || !JSVAL_IS_INT(argv[argIndex+1])) \
				ArgumentTypeError(); \
			_msg->_field ## X = ToPrimitive<int>(argv[argIndex++]); \
			_msg->_field ## Y = ToPrimitive<int>(argv[argIndex++]); \
		} catch (PSERROR_Scripting_ConversionFailed) { \
			JS_ReportError(cx, "Invalid location"); \
			return NULL; \
		}
	#define ReadEntity(_msg, _field) \
		STMT(\
			if (argIndex+1 > argc) \
				ArgumentCountError(); \
			if (!JSVAL_IS_OBJECT(argv[argIndex])) \
				ArgumentTypeError(); \
			CEntity *ent=ToNative<CEntity>(argv[argIndex++]); \
			if (!ent) \
			{ \
				JS_ReportError(cx, "Invalid entity parameter"); \
				return NULL; \
			} \
			_msg->_field=ent->me; \
		)
	#define ReadInt(_msg, _field) \
		STMT(\
			if (argIndex+1 > argc) \
				ArgumentCountError(); \
			if (!JSVAL_IS_INT(argv[argIndex])) \
				ArgumentTypeError(); \
			int val=ToPrimitive<int>(argv[argIndex++]); \
			_msg->_field=val; \
		)
	#define ReadString(_msg, _field) \
		STMT(\
			if (argIndex+1 > argc) \
				ArgumentCountError(); \
			if (!JSVAL_IS_STRING(argv[argIndex])) \
				ArgumentTypeError(); \
			CStrW val=ToPrimitive<CStrW>(argv[argIndex++]); \
			_msg->_field=val; \
		)
	
	#define PositionMessage(_msg) \
		case NMT_ ## _msg: \
		{ \
			C##_msg *msg = new C##_msg(); \
			msg->m_Entities = entities; \
			ReadPosition(msg, m_Target); \
			return msg; \
		}
	
	#define EntityMessage(_msg) \
		case NMT_ ## _msg: \
		{ \
			C##_msg *msg = new C##_msg(); \
			msg->m_Entities = entities; \
			ReadEntity(msg, m_Target); \
			return msg; \
		}

	#define EntityIntMessage(_msg) \
		case NMT_ ## _msg: \
		{ \
			C##_msg *msg = new C##_msg(); \
			msg->m_Entities = entities; \
			ReadEntity(msg, m_Target); \
			ReadInt(msg, m_Action); \
			return msg; \
		}
	
	#define ProduceMessage(_msg) \
		case NMT_ ## _msg: \
		{ \
			C##_msg *msg = new C##_msg(); \
			msg->m_Entities = entities; \
			ReadInt(msg, m_Type); \
			ReadString(msg, m_Name); \
			return msg; \
		}

	#define PlaceObjectMessage(_msg) \
		case NMT_ ## _msg: \
		{ \
			C##_msg *msg = new C##_msg(); \
			msg->m_Entities = entities; \
			ReadString(msg, m_Template); \
			ReadInt(msg, m_X); \
			ReadInt(msg, m_Y); \
			ReadInt(msg, m_Z); \
			ReadInt(msg, m_Angle); \
			return msg; \
		}

	// argIndex, incremented by reading macros. We have already "eaten" the
	// first argument (message type)
	uint argIndex = 1;
	switch (msgType)
	{
		// NMT_Goto, targetX, targetY
		PositionMessage(Goto)
		PositionMessage(Run)
		PositionMessage(Patrol)
		PositionMessage(AddWaypoint)
		PositionMessage(FormationGoto)

		// NMT_Generic, target, action
		EntityIntMessage(Generic)
		EntityIntMessage(NotifyRequest)
		EntityIntMessage(FormationGeneric)

		// NMT_Produce, type, name
		ProduceMessage(Produce)

		// NMT_PlaceObject, template, x, y, z, angle, action
		PlaceObjectMessage(PlaceObject)

		default:
			JS_ReportError(cx, "Invalid order type");
			return NULL;
	}
}

CNetMessage *CNetMessage::CreatePositionMessage( const CEntityList& entities, const int type, CVector2D pos )
{
	#define PosMessage(_msg) \
		case NMT_ ## _msg: \
		{ \
			C##_msg* msg = new C##_msg(); \
			msg->m_Entities = entities; \
			msg->m_TargetX = pos.x; \
			msg->m_TargetY = pos.y; \
			return msg; \
		}
	
	switch (type)
	{
		PosMessage(Goto)
		PosMessage(Run)
		PosMessage(Patrol)
		PosMessage(AddWaypoint)
		PosMessage(FormationGoto)
	
	default:
			return NULL;
	}
}
CNetMessage *CNetMessage::CreateEntityIntMessage( const CEntityList& entities, const int type, HEntity& target, int action )
{
	#define EntMessage(_msg) \
		case NMT_ ## _msg: \
		{ \
			C##_msg* msg = new C##_msg(); \
			msg->m_Entities = entities; \
			msg->m_Target = target; \
			msg->m_Action = action; \
			return msg; \
		}

	switch (type)
	{
		EntMessage(Generic)
		EntMessage(NotifyRequest)
		EntMessage(FormationGeneric)

	default:
		return NULL;
	}
}
CNetMessage *CNetMessage::CreateProduceMessage( const CEntityList& entities, const int type, int proType, CStrW name )
{
	#define ProMessage(_msg)\
		case NMT_ ## _msg: \
		{ \
			C##_msg* msg = new C##_msg(); \
			msg->m_Entities = entities; \
			msg->m_Type = proType; \
			msg->m_Name = name; \
			return msg; \
		}

	switch (type)
	{
		ProMessage(Produce)

	default:
		return NULL;
	}
}

		

	