// DWBS ------------------------------------------------------------------ DWBS
//
// PLATFORM		: WIN32 PS2 PSP-INFRA XENON XBOX PS3 UNIX OSX
// PRODUCT		: BIT_DEMON
// VISIBILITY	: PUBLIC
//
// DWBS ------------------------------------------------------------------ DWBS

#include <bdNet/bdDiscovery/bdDiscoveryServer.h>
#include <bdConnection/bdMessage/bdMessageTypes.h>
#include <bdNet/bdDiscovery/bdGameInfo.h>
#include <bdNet/bdNet.h>

#define BD_LOG_LEVEL	"bdNet/discovery server"

bdDiscoveryServer::bdDiscoveryServer()
: m_status(BD_IDLE)
{
}

bdDiscoveryServer::~bdDiscoveryServer()
{
	stop();
}

bdBool bdDiscoveryServer::start(const bdGameInfoRef gameInfo, 
								const bdInetAddr& localAddr,
								const bdPort discoveryPort)
{
	bdBool ok = true;
	if(m_socket.create())
	{
		const bdAddr bindAddr(localAddr, discoveryPort);
		if(BD_NET_SUCCESS == m_socket.bind(bindAddr))
		{
			m_status = BD_PENDING;
			m_gameInfo = gameInfo;			
		}
		else
		{
			m_status = BD_ERROR;
			ok = false;
		}
	}
	else
	{
		m_status = BD_ERROR;
		ok = false;
	}
	return ok;
}

void bdDiscoveryServer::update()
{
	if(m_status == BD_PENDING)
	{
		bdAddr from;
		bdUByte8 data[BD_MAX_DATAGRAM_SIZE];
	
		const bdInt32 received = m_socket.receiveFrom(from, data, BD_MAX_DATAGRAM_SIZE);

		if(received > BD_MIN_DISCOVERY_SIZE)
		{
			bdBitBuffer payload(data, received << 3, true);			
			bdMessageType type;
	
			// read out the message's type
			bdBool ok;
			ok = payload.readUByte8(type);
			if (ok && (type == BD_DISCOVERY_BROADCAST))
			{
				// read out the nonce 
				bdUByte8 tempNonce[BD_NONCE_SIZE];
				ok = ok && payload.readFull(tempNonce);

				// read out the game's title id
				bdUInt32 titleID = 0;
				ok = ok && payload.readUInt32(titleID);

				// Does the title ID match our own?
				if (ok && (titleID == m_gameInfo->getTitleID()))
				{
					bdBitBuffer discoveryReply;
					discoveryReply.writeUByte8(BD_DISCOVERY_REPLY);
					discoveryReply.writeFull(tempNonce);
                    m_gameInfo->serialize(discoveryReply);

#if defined BD_PLATFORM_XBOX
					// xbox must reply to the broadcast address...
					bdAddr bcAddr(bdInetAddr::Broadcast(), from.getPort());
					from = bcAddr;
#endif
					const bdInt sendResult = m_socket.sendTo(from , discoveryReply.getData(), discoveryReply.getDataSize());
					if(sendResult > 0)
					{
						bdNChar8 buf[BD_ADDR_STRING_SIZE];
						from.toString(buf, sizeof(buf));
						BD_INFO(BD_LOG_LEVEL, "Sent discovery reply to: %s", buf);

						for (bdUInt i=0; i < m_listeners.getSize(); i++)
						{
							m_listeners[i]->onRequest();
						}
					}
				}
			}
		}
	}
	else
	{
		BD_WARN(BD_LOG_LEVEL,"Not initialized.");
	}
}

void  bdDiscoveryServer::stop()
{
	m_socket.close();
	m_status = BD_IDLE;
}

void bdDiscoveryServer::registerListener(bdDiscoveryListener *listener)
{
	m_listeners.pushBack(listener);
}

void bdDiscoveryServer::unregisterListener(bdDiscoveryListener *listener)
{
	m_listeners.removeAll(listener);
}

bdDiscoveryServer::bdStatus bdDiscoveryServer::getStatus() const
{
	return m_status;
}

bdGameInfoRef bdDiscoveryServer::getGameInfo() 
{ 
	return m_gameInfo; 
}


