
#include "Player.h"
#include "GameWorld.h"
#include "GameWorldClient.h"
#include "GameNetInterface.h"

namespace DinastyOnline {
	namespace Network {
		TNL_IMPLEMENT_NETOBJECT(Player);
		
		// Player constructor
		Player::Player() : GameObject() {
			// assign a random starting position for the player.
			//startPos.x = TNL::Random::readI();
			//startPos.y = TNL::Random::readI();
			startPos.x = 7300;
			startPos.y = 7000;
			endPos.x = startPos.x;
			endPos.y = startPos.y;
			position = startPos;
			TNL::logprintf("***Player created (%i, %i)",position.x, position.y);
			this->mObjectTypeMask = PlayerType;
			
			// preset the movement parameter for the player to the end of
			// the path
			t = 1.0;
	
			tDelta = 0;
	
			mNetFlags.set(Ghostable);
			world = NULL;
			this->playerName = "Unnamed";
		}
		
		void Player::addToWorld(GameWorld *theWorld) {
			Parent::addToWorld(theWorld);
			
			// add the player to the list of players in the game.
			//theWorld->addToGameObjectList(this);
			
			if (!theWorld->isServer()) {
				GameWorldClient* theClientWorld = (GameWorldClient *) theWorld;
				//if (playerName == theClientWorld->getClientName()) {
				if (this->myPlayer) {
					theClientWorld->setClientPlayer(this);
					TNL::logprintf("********* MY PLAYER");
				} else {
					TNL::logprintf("********* NOT MY PLAYER");
				}
			}
	
		}
		
		bool Player::onGhostAdd(TNL::GhostConnection *theConnection) {
			Parent::onGhostAdd(theConnection);
			TNL::logprintf("Player added!");
	
			return true;
		}
	
		void Player::performScopeQuery(TNL::GhostConnection *connection) 	{
			Vector<GameObject *> players = world->getGameObjects();
			// Everything is in scope
			for(TNL::S32 i = 0; i < players.size(); i++) {
				connection->objectInScope(players[i]);
			}
		}
	
		TNL::U32 Player::packUpdate(TNL::GhostConnection *connection, TNL::U32 updateMask, TNL::BitStream *stream) {
			// if this is the initial update, write out some static
			// information about this player.  Since we never call
			// setMaskBits(InitialMask), we're guaranteed that this state
			// will only be updated on the first update for this object.
			if(stream->writeFlag(updateMask & InitialMask))
			{
				// write a flag to the client if this is the player that
				// that client controls.
				stream->writeFlag(connection->getScopeObject() == this);
				stream->writeString(playerName.getString());
			}
	
			// see if we need to write out the position data
			// we must write a bit to let the unpackUpdate know whether to
			// read the position data out of the stream.
	
			if(stream->writeFlag(updateMask & PositionMask))
			{
				// since the position data is all 0 to 1, we can use
				// the bit stream's writeFloat method to write it out.
				// 12 bits should be sufficient precision
				stream->writeSignedInt(startPos.x, 32);
				stream->writeSignedInt(startPos.y, 32);
				stream->writeSignedInt(endPos.x, 32);
				stream->writeSignedInt(endPos.y, 32);
				stream->writeSignedInt(position.x, 32);
				stream->writeSignedInt(position.y, 32);
				stream->write(t); // fully precise t and tDelta
				stream->write(tDelta);
			}
	
			//TNL::logprintf("Player object sent...");
			//TNL::logprintf(playerName.getString());
			
	
			// if I had other states on the player that changed independently
			// of position, I could test for and update them here.
			// ...
	
			// the return value from packUpdate is the update mask for this
			// object on this client _AFTER_ this update has been sent.
			// Normally this will be zero, but it could be nonzero if this
			// object has a state that depends on some other object that
			// hasn't yet been ghosted.
			return 0;
		}
	
		void Player::unpackUpdate(TNL::GhostConnection *connection, TNL::BitStream *stream)	{
			//TNL::logprintf("Starting to receive object player...");
			// see if the initial packet data was written:
			if(stream->readFlag())
			{
				// check to see if it's not an AI player.
				if(stream->readFlag()) {
					this->myPlayer = true;
				} else {
					this->myPlayer = false;
				}
				
				char stringBuf[256];
				stream->readString(stringBuf);
				this->playerName = stringBuf;
			}
			// see if the player's position has been updated:
			if(stream->readFlag()) {
				startPos.x = stream->readSignedInt(32);
				startPos.y = stream->readSignedInt(32);
				endPos.x = stream->readSignedInt(32);
				endPos.y = stream->readSignedInt(32);
				position.x = stream->readSignedInt(32);
				position.y = stream->readSignedInt(32);
				stream->read(&t);
				stream->read(&tDelta);
				// update the render position
				//idle(0);
			}
			//TNL::logprintf("Player object received...");
		}
	
		bool Player::isMoving() {
			if ((position.x == endPos.x) && (position.y == endPos.y)) {
				return false;
			} else {
				return true;
			}
		}
		
		void Player::serverSetPosition(Position inStartPos, Position inEndPos, TNL::F32 inT, TNL::F32 inTDelta)	{
			// update the instance variables of the object
			startPos = inStartPos;
			endPos = inEndPos;
			t = inT;
			tDelta = inTDelta;
	
			// notify the network system that the position state of this object has changed:
			setMaskBits(PositionMask);
	
			// call a quick RPC to all the connections that have this object in scope
			//rpcPlayerDidMove(inEndPos.x, inEndPos.y);
		}
	
		void Player::idle(TNL::F32 timeDelta)	{
			if (myPlayer) {
				return;
			}
			
			t += tDelta * timeDelta;
			if(t >= 1.0) {
				t = 1.0;
				tDelta = 0;
				position = endPos;
			}

			position.x = startPos.x + (int)((endPos.x - startPos.x) * t);
			position.y = startPos.y + (int)((endPos.y - startPos.y) * t);
			//TNL::logprintf("Moved player (%i, %i)",position.x, position.y);

			// notify the network system that the position state of this object has changed:
			setMaskBits(PositionMask);
		}
	
		void Player::onGhostAvailable(TNL::GhostConnection *theConnection) {
			// this function is called every time a ghost of this object is known
			// to be available on a given client.
			// we'll use this to demonstrate targeting a NetObject RPC
			// to a specific connection.  Normally a RPC method marked as
			// RPCToGhost will be broadcast to ALL ghosts of the object currently in
			// scope.
	
			// first we construct an event that will represent the RPC call...
			// the RPC macros create a RPCNAME_construct function that returns
			// a NetEvent that encapsulates the RPC.
			TNL::NetEvent *theRPCEvent = TNL_RPC_CONSTRUCT_NETEVENT(this, rpcPlayerIsInScope, (position.x, position.y));
	
			// then we can just post the event to whatever connections we want to have
			// receive the message.
			// In the case of NetObject RPCs, if the source object is not ghosted,
			// the RPC will just be silently dropped.
			theConnection->postNetEvent(theRPCEvent);
		}
	
		TNL_IMPLEMENT_NETOBJECT_RPC(Player, rpcPlayerIsInScope,
		(TNL::F32 x, TNL::F32 y), (x, y),
		TNL::NetClassGroupGameMask, TNL::RPCGuaranteedOrdered, TNL::RPCToGhost, 0) {
			TNL::F32 fx = x, fy = y;
			TNL::logprintf("A player is now in scope at %g, %g", fx, fy);
		}
	
		TNL_IMPLEMENT_NETOBJECT_RPC(Player, rpcPlayerWillMove,
									(TNL::F32 testString), (testString),
		TNL::NetClassGroupGameMask, TNL::RPCGuaranteedOrdered, TNL::RPCToGhostParent, 0) {
			TNL::logprintf("Expecting a player move from the connection: %g", testString);
		}
	
		/*TNL_IMPLEMENT_NETOBJECT_RPC(Player, rpcPlayerDidMove,
		(TNL::Float<6> x, TNL::Float<6> y), (x, y),
		TNL::NetClassGroupGameMask, TNL::RPCGuaranteedOrdered, TNL::RPCToGhost, 0)
		{
			TNL::F32 fx = x, fy = y;
			TNL::logprintf("A player moved to %g, %g", fx, fy);
		}*/
	}
}

