#include "Spieler.h"
#include <math.h>
#ifdef WIN32
#include <random>
#else
#include <stdlib.h>
#endif

// Inhalt der Spieler Klasse aus Spieler.h
// Konstruktor
Spieler::Spieler()
{
	accountId = 0;
	sNum = 0;
	team = 0;
	spielerFarbe = 0;
	teamFarbe = 0;
	xPos = 0;
	yPos = 0;
	xSpeed = 0;
	ySpeed = 0;
	kurve = 0;
	radius = 1;
	amLeben = 1;
	lKurve = 0;
	rKurve = 0;
	klient = 0;
	spielZeit = 0;
	todesZeit = -1;
	linieUnterbrochen = 1;
	nLUChange = 3;
	lUStrength = 0.3;
	punkte = 0;
	rundenGewonnen = 0;
	linienLength = 0;
	zeitAmLeben = 0;
	zeitTod = 0;
	kills = 0;
	tode = 0;
	killFarbe = 0;
	rundenLinienLength = new Array< int >();
	rundenLinienLength->set( 0, 0 );
	rundenKills = new Array< int >();
	rundenKills->set( 0, 0 );
	rundenTodesZeit = new Array< int >();
	rundenTodesZeit->set( 0, 0 );
	rundenNummer = 0;
	ref = 1;
}

// Destruktor
Spieler::~Spieler()
{
	if( klient )
		klient->release();
	rundenLinienLength->release();
	rundenKills->release();
	rundenTodesZeit->release();
}

// nicht constant
void Spieler::offline()
{
	klient->offline();
}

void Spieler::online( SSKlientV *zKlient )
{
	klient->online( zKlient );
}

void Spieler::neuRunde( bool amLeben )
{
	if( this->amLeben )
		rundenGewonnen++;
	killFarbe = 0;
	xPos = 0;
	yPos = 0;
	xSpeed = 0;
	ySpeed = 0;
	kurve = 0;
	radius = 1;
	this->amLeben = amLeben;
	lKurve = 0;
	rKurve = 0;
	spielZeit = 0;
	todesZeit = -1;
	linieUnterbrochen = 1;
	nLUChange = 3;
	lUStrength = 0.3;
	rundenNummer++;
	rundenLinienLength->set( 0, rundenNummer );
	rundenKills->set( 0, rundenNummer );
	rundenTodesZeit->set( 0, rundenNummer );
}

void Spieler::setAccountId( int id )
{
	accountId = id;
}

void Spieler::setSpielerNummer( int sNum )
{
	this->sNum = sNum;
}

void Spieler::setTeam( int team )
{
	this->team = team;
}

void Spieler::setSpielerFarbe( int farbe )
{
	spielerFarbe = farbe;
}

void Spieler::setTeamFarbe( int farbe )
{
	teamFarbe = farbe;
}

void Spieler::setGeschwindigkeit( double xSpeed, double ySpeed )
{
	this->xSpeed = xSpeed;
	this->ySpeed = ySpeed;
}

void Spieler::setPosition( double x, double y )
{
	xPos = x;
	yPos = y;
}

void Spieler::setWendigkeit( double kurve )
{
	this->kurve = kurve;
}

void Spieler::setSize( int radius )
{
	this->radius = radius;
}

void Spieler::setKlient( Klient *klient )
{
	if( this->klient )
		this->klient->release();
	this->klient = klient;
}

void Spieler::setLKurve( bool lK )
{
	lKurve = lK;
}

void Spieler::setRKurve( bool rK )
{
	rKurve = rK;
}

void Spieler::setLinienUnterbrechungsStrength( double uS )
{
	lUStrength = uS;
}

void Spieler::addPunkte( int p )
{
	punkte += p;
}

void Spieler::addKill()
{
	kills++;
	if( rundenKills->hat( rundenNummer ) )
		rundenKills->set( rundenKills->get( rundenNummer ) + 1, rundenNummer );
}

void Spieler::tick( Bild *zMap, double zeit )
{
	spielZeit += zeit;
	if( amLeben )
	{
		zeitAmLeben += zeit;
		if( lKurve || rKurve )
		{
			double grad = kurve * zeit;
			if( lKurve )
				grad = -grad;
			double cs = cos( grad );
			double sn = sin( grad );
			double tmpX = ( xSpeed * cs - ySpeed * sn );
			double tmpY = ( xSpeed * sn + ySpeed * cs );
			xSpeed = tmpX;
			ySpeed = tmpY;
		}
		if( zMap && !linieUnterbrochen )
		{
			int x1 = (int)( xPos + 0.5 );
			int y1 = (int)( yPos + 0.5 );
			int x2 = (int)( xPos + xSpeed * zeit + 0.5 );
			int y2 = (int)( yPos + ySpeed * zeit + 0.5 );
			int *buff = zMap->getBuffer();
			int dpx = 0;
			int dpy = 0;
			int dgx = zMap->getBreite();
			int dgy = zMap->getHeight();
			int xlen = x2 - x1, axlen = abs( xlen );
			int ylen = y2 - y1, aylen = abs( ylen );
			double xf = (double)xlen / ( aylen ? aylen : 1 );
			double yf = (double)ylen / ( axlen ? axlen : 1 );
			if( axlen > aylen )
				xf = xf < 0 ? -1 : 1;
			else
				yf = yf < 0 ? -1 : 1;
			double x = (double)x1, y = (double)y1;
			int maxP = (int)( sqrt( (double)( xlen * xlen + ylen * ylen ) ) + 0.5 );
			int count = 0;
			int lastX = (int)( x + 0.5 ), lastY = (int)( y + 0.5 );
			while( !( (int)( x + 0.5 ) == x2 && (int)( y + 0.5 ) == y2 ) && count < maxP )
			{
				if( (int)( x + 0.5 ) < dpx || (int)( x + 0.5 ) >= dgx || (int)( y + 0.5 ) < dpy || (int)( y + 0.5 ) >= dgy )
				{
					if( !rundenTodesZeit->hat( rundenNummer ) || !rundenTodesZeit->get( rundenNummer ) )
						tode++;
					xPos = x;
					yPos = y;
					todesZeit = spielZeit;
					rundenTodesZeit->set( (int)todesZeit, rundenNummer );
					amLeben = 0;
					killFarbe = 0;
					break;
				}
				if( buff[ (int)( x + 0.5 ) + (int)( y + 0.5 ) * zMap->getBreite() ] )
				{
					killFarbe = buff[ (int)( x + 0.5 ) + (int)( y + 0.5 ) * zMap->getBreite() ];
					if( killFarbe == spielerFarbe )
						killFarbe = 0;
					if( !rundenTodesZeit->hat( rundenNummer ) || !rundenTodesZeit->get( rundenNummer ) )
						tode++;
					xPos = x;
					yPos = y;
					todesZeit = spielZeit;
					rundenTodesZeit->set( (int)todesZeit, rundenNummer );
					amLeben = 0;
					break;
				}
				buff[ (int)( x + 0.5 ) + (int)( y + 0.5 ) * zMap->getBreite() ] = spielerFarbe;
				x += xf, y += yf;
                if( (int)( x + 0.5 ) < dpx || (int)( x + 0.5 ) >= dgx || (int)( y + 0.5 ) < dpy || (int)( y + 0.5 ) >= dgy )
                {
                    if( !rundenTodesZeit->hat( rundenNummer ) || !rundenTodesZeit->get( rundenNummer ) )
                        tode++;
                    xPos = x;
                    yPos = y;
                    todesZeit = spielZeit;
                    rundenTodesZeit->set( (int)todesZeit, rundenNummer );
                    amLeben = 0;
                    killFarbe = 0;
                    break;
                }
				if( ( lastX != (int)( x + 0.5 ) || lastY != (int)( y + 0.5 ) ) && !buff[ (int)( x + 0.5 ) + (int)( y + 0.5 ) * zMap->getBreite() ] )
				{
					linienLength++;
					if( rundenLinienLength->hat( rundenNummer ) )
						rundenLinienLength->set( rundenLinienLength->get( rundenNummer ) + 1, rundenNummer );
				}
				if( lastX != (int)( x + 0.5 ) && lastY != (int)( yf + 0.5 ) )
				{
					if( (int)xf )
					{
						if( !( lastX < dpx || lastX >= dgx || (int)( y + 0.5 ) < dpy || (int)( y + 0.5 ) >= dgy ) )
							buff[ lastX + (int)( y + 0.5 ) * zMap->getBreite() ] = spielerFarbe;
					}
					else
					{
						if( !( (int)( x + 0.5 ) < dpx || (int)( x + 0.5 ) >= dgx || lastY < dpy || lastY >= dgy ) )
							buff[ (int)( x + 0.5 ) + lastY * zMap->getBreite() ] = spielerFarbe;
					}
				}
				lastX = (int)( x + 0.5 );
				lastY = (int)( y + 0.5 );
				count++;
			}
		}
		if( amLeben )
		{
			xPos += xSpeed * zeit;
			yPos += ySpeed * zeit;
			nLUChange -= zeit;
			if( nLUChange <= 0 )
			{
				linieUnterbrochen = !linieUnterbrochen;
				if( linieUnterbrochen )
					nLUChange = lUStrength;
				else
					nLUChange = 1 / lUStrength + ( ( rand() % (int)( 1 / lUStrength ) ) - ( ( 1 / lUStrength ) / 2 ) );
			}
		}
	}
	else
		zeitTod += zeit;
}

// constant
Klient *Spieler::zKlient() const
{
	return klient;
}

int Spieler::getSpielerNummer() const
{
	return sNum;
}

int Spieler::getSpielerFarbe() const
{
	return spielerFarbe;
}

int Spieler::getSpielerTeam() const
{
	return team;
}

int Spieler::getTeamFarbe() const
{
	return teamFarbe;
}

double Spieler::getX() const
{
	return xPos;
}

double Spieler::getY() const
{
	return yPos;
}

double Spieler::getSpeedX() const
{
	return xSpeed;
}

double Spieler::getSpeedY() const
{
	return ySpeed;
}

double Spieler::getKurve() const
{
	return kurve;
}

bool Spieler::istAmLeben() const
{
	return amLeben;
}

int Spieler::getAccountId() const
{
	return accountId;
}

double Spieler::getTodesZeit() const
{
	return todesZeit;
}

bool Spieler::linieIstUnterbrochen() const
{
	return linieUnterbrochen;
}

int Spieler::getPunkte() const
{
	return punkte;
}

bool Spieler::istOnline() const
{
	return klient ? klient->istOnline() : 0;
}

int Spieler::getKillFarbe() const
{
	return killFarbe;
}

int Spieler::getRundenGewonnen() const
{
	return rundenGewonnen + amLeben;
}

int Spieler::getLinienLength() const
{
	return linienLength;
}

int Spieler::getZeitAmLeben() const
{
	return (int)zeitAmLeben;
}

int Spieler::getZeitTod() const
{
	return (int)zeitTod;
}

int Spieler::getKills() const
{
	return kills;
}

int Spieler::getTode() const
{
	return tode;
}

int Spieler::getLinienLength( int runde ) const
{
	return rundenLinienLength->hat( runde ) ? rundenLinienLength->get( runde ) : 0;
}

int Spieler::getKills( int runde ) const
{
	return rundenKills->hat( runde ) ? rundenKills->get( runde ) : 0;
}

int Spieler::getTodesZeit( int runde ) const
{
	return rundenTodesZeit->hat( runde ) ? rundenTodesZeit->get( runde ) : 0;
}

bool Spieler::machtKurve( bool rechts ) const
{
	return ( rechts && rKurve ) || ( !rechts && lKurve );
}

// Reference Counting
Spieler *Spieler::getThis()
{
	ref++;
	return this;
}

Spieler *Spieler::release()
{
	ref--;
	if( !ref )
		delete this;
	return 0;
}