// PlayerCommunication.cpp: implementation of the PlayerCommunication class.
//
//////////////////////////////////////////////////////////////////////

#include "PlayerCommunication.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

PlayerCommunication::PlayerCommunication()
{
	int i;
	m_pszMessage = NULL;
	m_bHaveMsg = false;

	strcpy(m_szSayHash," ()*+-./0123456789<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz");
	memset(m_szHearHash, 0, 256);
	for(i=0;i<255;i++){
      m_szHearHash[i]=0;
	}
	for(i=0;i<sizeof(m_szSayHash)-1;i++){
      m_szHearHash[m_szSayHash[i]]=i;
	}
	log = NULL;
	log = new Log("aureal");
	m_nStepsOurHaveBall=6000;	/// pred 6000 krokmi mal nas loptu
}

PlayerCommunication::~PlayerCommunication()
{
	if (m_pszMessage!=NULL)
		delete[] m_pszMessage;
	if (log != NULL)
		delete log;
}

bool PlayerCommunication::EncodePosition(Point &pt, char *szMsg)
// zakoduje poziciu hraca do retazca
{
	char szPoz[4];
	memset(szPoz,0, 4);
	bool bX = EncodeFloat(pt.GetX(), szPoz);
	strcpy(szMsg, szPoz);
	bool bY = EncodeFloat(pt.GetY(), szPoz);
	szMsg = strcat(szMsg, szPoz);
	return bX&bY;
}

void PlayerCommunication::DecodePosition(const char *szMsg, Point &x)
// dekoduje poziciu hraca z retazca
{
	x.SetX(DecodeFloat(szMsg));
	x.SetY(DecodeFloat(szMsg+2));
}

bool PlayerCommunication::EncodeVector(Vector &vect, char *szMsg)
// zakoduje poziciu hraca do retazca
{
	char szPoz[4];
	memset(szPoz,0, 4);
	bool bX = EncodeFloat(vect.GetX(), szPoz);
	strcpy(szMsg, szPoz);
	bool bY = EncodeFloat(vect.GetY(), szPoz);
	szMsg = strcat(szMsg, szPoz);
	return bX&bY;
}

void PlayerCommunication::DecodeVector(const char *szMsg, Vector &x)
// dekoduje poziciu hraca z retazca
{
	x.SetX(DecodeFloat(szMsg));
	x.SetY(DecodeFloat(szMsg+2));
}

bool PlayerCommunication::EncodeFloat(float value, char *msg)
// zakoduje realne cislo do retacca s presnostou na jedno desatinne miesto 
// dovoleny rozsah cisla je <-53 ; 53 > (rozmery ihriska)
{
	if (value< -53.0 || value > 53.0)								// ak suradnica mimo ihriska
		return false;												// tak chyba
	float fAbsVal = (float) fabs(value);
	unsigned char celaCast = (unsigned char)fAbsVal;				// urci celu cast
	unsigned char desatinnaCast = (unsigned char)((fAbsVal - float(celaCast))*10);	//urci desatinnu cast
	if (value<0)													// urci znamienko
		desatinnaCast |= sign_bit ;									// pridaj ho do desatinnej casti
	msg[0] = EncodeByte(celaCast);									// zakoduj celu cast
	msg[1] = EncodeByte(desatinnaCast);								// zakoduj desatinnu cast
	msg[2] = 0;

	return true;
}

float PlayerCommunication::DecodeFloat(const char *msg)
// dekoduje realne cislo z retacca s presnostou na jedno desatinne miesto 
// dovoleny rozsah cisla je <-53 ; 53 > (rozmery ihriska)

{
	unsigned char celaCast = DecodeByte(msg[0]);					// dekoduj celu cast
	unsigned char desatinnaCast = DecodeByte(msg[1]);				// dekoduj desatinnu + znamienko
	float sign = (desatinnaCast/sign_bit) ? (float)-1.0 : (float)1.0;				// urci znamienko
	desatinnaCast &= ~sign_bit;										// odstran znamienko
	 
	return (float)(sign* (float)celaCast+ sign*((float)desatinnaCast/10.0));	// vypocitaj realnu hodnotu cisla
}

char PlayerCommunication::EncodeByte(unsigned char ch)
// zakoduje znak na povolene znaky
{
	return m_szSayHash[ch];		//zakoduj znak pomocou kodovacej tabulky
}

char PlayerCommunication::DecodeByte(unsigned char ch)
// dekoduje zakodovany znak
{
	return m_szHearHash[ch];	//dekoduj znak pomocou dekodovacej tabulky
}

char PlayerCommunication::EncodePlayerNumber(char ch, bool bEnemy )
// zakoduje cislo hraca
{
	char encodedNumber = (ch | (bEnemy?enemy_bit:0));
	return EncodeByte(encodedNumber);
}


char PlayerCommunication::DecodePlayerNumber(char codedNumber, bool &bEnemy)
// dekoduje cislo hraca
{
	unsigned char decodeedNumber = DecodeByte(codedNumber);
	bEnemy = (decodeedNumber & (enemy_bit))==0?false:true;
	return decodeedNumber & (~enemy_bit);
}

void PlayerCommunication::SZaTebou(Point pos, Vector vel, unsigned int number)
//naformatuje a posle spravu za_tebov
{
	char msg[11];
	char szPosition[11];
	char szVelocity[11];
	char szNumber[2];


	EncodePosition(pos, szPosition);	/// zakoduje pozicu nepriatela
	EncodeVector(vel, szVelocity);		/// zakoduje rychlost nepriatela
	
	szNumber[1]=0;
	szNumber[0]= EncodePlayerNumber(number, true);		//zakoduje cilo hraca
	msg[0] = EncodeByte(msg_za_tebou);					// typ spravy za tebou 1b
	msg[1] = 0;							
	strcat(msg, szPosition);							// prida poziciu 4b
	strcat(msg, szVelocity);							// prida rychlost 4b
	strcat(msg, szNumber);								// prida cislo nepriatela  1b
	
	Send(msg);
	log->printf("\n>SZaTebou: %d, pos:(%.2f :%.2f)", number, pos.GetX(), pos.GetY());
	
	//FPRINTF(heardf,"\n>T:%04d, Za hracom s loptou je super cislo %d na pos: %.2f, %.2f,vel: %.2f, %.2f\n", number, m_pk->GetSimTime(), pos.m_x, pos.m_y, vel.m_x, vel.m_y);
}

void PlayerCommunication::SPusti(Point pos, unsigned nPlayer)
//naformatuje a posle spravu pusti
{
	char msg[11];
	char szPosition[11];
	char szNumber[2] ={0,0};
	EncodePosition(pos, szPosition);
	EncodePlayerNumber(nPlayer, false);
	msg[0] = EncodeByte(msg_pusti);
	msg[1] = 0;
	strcat(msg, szPosition);
	strcat(msg, szNumber);
	Send(msg);
	log->printf("\n>SPusti: %d, pos:(%.2f :%.2f)", nPlayer, pos.GetX(), pos.GetY());
	//	FPRINTF(heardf,"\n>T:%04d, Hrac %d pusti loptu, lopta je smeruje na poziciu: %.2f, %.2f\n",m_pk->GetSimTime(), nPlayer, pos.m_x, pos.m_y);
}


void PlayerCommunication::SPrihraj(Point pos, unsigned nPlayer)
//naformatuje a posle spravu prihraj
{
	char msg[11];
	char szPosition[11];
	char szNumber[2] ={0,0};
	EncodePosition(pos, szPosition);
	EncodePlayerNumber(nPlayer, false);
	msg[0] = EncodeByte(msg_prihraj);
	msg[1] = 0;
	strcat(msg, szPosition);
	strcat(msg, szNumber);
	Send(msg);
	log->printf("\n>SPrihraj: %d, pos:(%.2f :%.2f)", nPlayer, pos.GetX(), pos.GetY());
/*	if (m_pCommandGen != NULL)
		m_pCommandGen->Say(msg);
*/
	//FPRINTF(heardf,"\n>T:%04d, Prihraj hracovi %d, som na %.2f, %.2f\n", m_pk->GetSimTime(), nPlayer, pos.m_x, pos.m_y);
}


void PlayerCommunication::SBez(Vector vel, unsigned nPlayer)
//naformatuje a posle spravu bez
{
	char msg[11];
	char szPosition[11];
	char szNumber[2] ={0,0};
	EncodeVector(vel, szPosition);
	EncodePlayerNumber(nPlayer, false);
	msg[0] = EncodeByte(msg_bez);
	msg[1] = 0;
	strcat(msg, szPosition);
	strcat(msg, szNumber);

	Send(msg);
	log->printf("\n>SBez: %d, vect >vel:%.2fo dl:%.2f", nPlayer, vel.GetX(), vel.GetY());
/*	if (m_pCommandGen != NULL)
		m_pCommandGen->Say(msg);

	FPRINTF(heardf,"\n>T:%04d, Hrac %d, bez na %.2f %.2f\n", m_pk->GetSimTime(), nPlayer, vel.m_x, vel.m_y);
*/
}

void PlayerCommunication::SChytaj(Point pos, unsigned nPlayer)
//naformatuje a posle spravu chytaj
{
	char msg[11];
	char szPosition[11];
	char szNumber[2] ={0,0};
	EncodePosition(pos, szPosition);		// zakoduj poziciu
	szNumber[0] = EncodePlayerNumber(nPlayer, false);		// zakoduj cilo hraca
	msg[0] = EncodeByte(msg_chytaj);
	msg[1] = 0;
	strcat(msg, szPosition);
	strcat(msg, szNumber);
	Send(msg);
	log->printf(">SChytaj: %d, x:%.2f y:%.2f", nPlayer, pos.GetX(), pos.GetY());
	
/*	
	if (m_pCommandGen != NULL)
		m_pCommandGen->Say(msg);

	FPRINTF(heardf,"\n>T:%04d, Prihravam hracovi:%d na pos.:%.2f, %.2f,\n", m_pk->GetSimTime(), nPlayer, pos.m_x, pos.m_y );
*/
}

void PlayerCommunication::SMam(Point pos, Vector vel)
//naformatuje a posle spravu mam
{
	char msg[11];
	char szPosition[11];
	char szNumber[2] ={0,0};
	char szVelocity[11];
	m_nStepsOurHaveBall = 0;
	EncodePosition(pos, szPosition);
	EncodeVector(vel, szVelocity);
	msg[0] = EncodeByte(msg_mam);
	msg[1] = 0;
	strcat(msg, szPosition);
	strcat(msg, szVelocity);
	
	Send(msg);
	log->printf("\n>SMam: x:%.2f y:%.2f vel x:%.2f y:%.2f", pos.GetX(), pos.GetY(), vel.GetX(), vel.GetY());
/*	
	if (m_pCommandGen != NULL)
		m_pCommandGen->Say(msg);

	FPRINTF(heardf,"\n>T:%04d, Mam loptu je na pos.:%.2f,%.2f, a ma rychlost %.2f,%.2f\n", m_pk->GetSimTime(), pos.m_x, pos.m_y, vel.m_x, vel.m_y);
	*/
}


void PlayerCommunication::DecodeMsgPV(const char *msg, Point &pos, Vector &vel)
{
	DecodePosition(msg, pos);
	DecodeVector(msg+4, vel);
}

void PlayerCommunication::DecodeMsgPP(const char *msg, Point &pos, unsigned int &num, bool &bEnemy)
{
	DecodePosition(msg, pos);
	num = DecodePlayerNumber(msg[4], bEnemy);
}

void PlayerCommunication::DecodeMsgPVP(const char *msg, Point &pos, Vector &vel, unsigned int &num, bool &bEnemy)
{
	DecodePosition(msg, pos);
	DecodeVector(msg+4, vel);
	num = DecodePlayerNumber(msg[8], bEnemy);
}

void PlayerCommunication::OnHear(const char *pOdkaz, unsigned int Sender, World* world)
{
	Point pos;
	Point pos2;
	Vector vel;
	SimPlayer p(world);

	unsigned num;
	bool bEnemy =false;
	unsigned uniform;
//	char szPos[20];
//	char szVel[20];
	int pIndex;
	log->printf("\n<<<<<<<<<Message form: %d, ", Sender);

	switch (GetMessageId(pOdkaz)){
	case msg_mam:
			m_nStepsOurHaveBall = 0;
			log->printf(">msg_mam");
			DecodeMsgPV(pOdkaz+1, pos, vel);
			world->ball->pos = pos;	// uprav poziciu a rychlost lopty
			world->ball->vel = vel; 
			
			/*	pos.GetAsString(szPos);
				vel.GetAsString(szVel);
				FPRINTF(heardf,"Loptu ma %d, je na %s, s ma rychlost %s\n",Sender, szPos, szVel);
			*/
		break;
	case msg_chytaj:
		
		DecodeMsgPP(pOdkaz+1, pos, num, bEnemy);
//		pos.GetAsString(szPos);
		m_nStepsOurHaveBall = 6000;
		if (num==world->me->GetUniform()  || 
			(num==0 && (world->me->pos - pos).GetLength()<5.0))  
		{
			//FPRINTF(heardf,"Prihravka pre mna na poziciu: %s\n", szPos);
			vel = pos - world->me->pos;
			vel.SetLength(vel.GetLength()*0.8f);
			pos2 = pos - vel;
			world->me->preparedCommands.SetTarget(pos2); 
			log->printf("\nI set the target");
		}
		
		else{
			//FPRINTF(heardf,"Prihravka hracovi %d na poziciu :%s.\n", num, szPos);
		}
		log->printf(">msg_chytaj (%3.2f %3.2f)", pos.GetX(), pos.GetY());
		break;
	case msg_za_tebou:
		log->printf(">msg_za_tebou");
		DecodeMsgPVP(pOdkaz+1, pos, vel, num, bEnemy);		// dekoduj spravu
		pIndex = world->enemies.FindByUniformIndex(num);
			
		if (pIndex!=-1){
			if (world->enemies[pIndex].GetAge()>3){
				world->enemies[pIndex].SetAge(1);
				world->enemies[pIndex].vel = vel;
				world->enemies[pIndex].pos = pos;
			}
		}
		else{
			SimPlayer p(world);
			p.SetAge(1);
			p.SetSide(THEIR);
			p.pos = pos;
			p.vel = vel;
			world->enemies.Add(p);	// pridaj nepriatela do svojho sveta
		}
/*				
		pos.GetAsString(szPos);
		vel.GetAsString(szVel);
		FPRINTF(heardf,"Hrac:%d je za tebou. Pos:%s, Vel:%s\n", num, szPos, szVel);
*/
		break;
	case msg_prihraj:
		{
		
		DecodeMsgPP(pOdkaz+1, pos, num, bEnemy);
		log->printf(">msg_prihraj hracovi %d: je na (%3.2) (%3.2f)", num, pos.GetX(), pos.GetY());

		PASSABLE_FRIEND pass;
		pass.age = 1;
		pass.Uniform = num;
		//pass.Uniform = num;
				
		pIndex = world->friends.FindByUniformIndex(num);
		if (pIndex!=-1){
			world->friends[pIndex].SetAge(1);
			world->friends[pIndex].pos = pos;
			world->friends[pIndex].vel = vel;			
			pass.index = pIndex;
		}
		else{
			p.SetAge(1);
			p.SetSide(OUR);
			p.pos = pos;
			p.vel = vel;
			pass.index = world->friends.Add(p);
		}
		world->passableFriends.Add(pass);
		}
		break;
	case msg_default:
			log->printf(">msg_default");
			DecodeMsgPV(pOdkaz+1, pos, vel);
			
			unsigned uniform =DecodePlayerNumber(pOdkaz[9], bEnemy);

			pIndex = world->friends.FindByUniformIndex(uniform);
			if (pIndex!=-1){
				world->friends[pIndex].SetAge(1);
				world->friends[pIndex].pos = pos;
				world->friends[pIndex].vel = vel;			
			}
			else{
				SimPlayer p(world);
				p.SetAge(1);
				p.SetSide(OUR);
				p.pos = pos;
				p.vel = vel;
				p.SetUniform(uniform);
				world->friends.Add(p);
			}	
		break;
	}
	m_nStepsOurHaveBall++;
}

char PlayerCommunication::GetMessageId(const char *pszMsg)
{
	return DecodeByte(pszMsg[0]);
}

void PlayerCommunication::Init(int maximumMessageSize)
{
	m_pszMessage = new char[maximumMessageSize+1];
	if (m_pszMessage==NULL)
	{
		DBGN("Memory allocation error at %d", __LINE__);
		exit(1);
	}
	m_bHaveMsg = false;
}

void PlayerCommunication::Send(char *msg)
{
	if (m_pszMessage!=NULL)
	{
		strcpy(m_pszMessage, msg);
		m_bHaveMsg = true;
	}
	else
	{
		DBGN("Error: Can't send message");
	}
}

bool PlayerCommunication::GetMessage(char *&pszMsg, World *world)
{
	if (m_bHaveMsg)
	{	// ak mam co poslat
		pszMsg = m_pszMessage;
		m_bHaveMsg = false;
		return true;
	}
	DefaultMessage(world);
	
	if (m_bHaveMsg){	// ak mam co poslat
		pszMsg = m_pszMessage;
		m_bHaveMsg = false;
		return true;
	}
	
	pszMsg = NULL;
	return false;
}

void PlayerCommunication::DefaultMessage(World *world)
{
	int time = world->state->GetSimTime() % 14;
	if(time != (world->me->GetUniform() - 2) * 2) return;

	char msg[11];
	char szPosition[11];
	char szVelocity[11];
	log->printf("\n>msg_default");
	EncodePosition(world->me->pos, szPosition);
	EncodeVector(world->me->vel, szVelocity);
	msg[0] = EncodeByte(msg_default);
	msg[1] = 0;
	strcat(msg, szPosition);
	strcat(msg, szVelocity);
	char szUnif[2];
	szUnif[0] = EncodePlayerNumber(world->me->GetUniform(), false);
	szUnif[1] = 0;
	strcat(msg, szUnif);
	Send(msg);
}
