#include "socket.h"

#include "framedata.h"
#include "mamescene.h"

#include "aicpu.h"
#include "job.h"

Socket::Socket(const QString &address, QObject *parent):
	QUdpSocket(parent),nShots(0),nExplosions(0)
{
	cpu = new AICpu(this);	
	connect(this, SIGNAL(readyRead()), this, SLOT(receiveFrame()));
	connect(this, SIGNAL(connected()), this, SLOT(initialSend()));
	connectToHost(address,1979);

}


Socket::~Socket()
{
}

void Socket::initialSend()
{
	static bool done = false;
	QByteArray bArray = "ctmame@0";
	int bytes = write(bArray);
	if(bytes == 8 && !done)
	{
		qDebug() << "started";
		done = true;
	}
}

bool Socket::parseFrame(const QByteArray &frame)
{
	unsigned char check = (unsigned char)frame[1];
	if(check != 0xe0 && check != 0xe2)
		{
			qDebug() << "frame error";
			
		}
		else
		{
			frameData = ai->getCurrent();
			frameData->clear();
			unsigned short *ram = (unsigned short*)frame.data();
			int i=1;
			
			int pX, pY, dX, dY, scale, brightness;
			int tempDX, tempDY;
			enum ShipSide{Left, Right} shipSide = Right;
			while(true)
			{
				int op=ram[i] >> 12;
				switch(op)
				{
					case 0xa: // LABS: Legt Position und Skalierungsfaktor fest
						// Y-Koordinate
						pY = ram[i] & 0x3ff;
						// X-Koordinate
						pX = ram[i+1] & 0x3ff;
						// Skalierungsfaktor
						scale = ram[i+1] >> 12;
						break;
					case 0xb: //HALT: Ende erreicht
						return true;
					case 0xc: //JSRL: Subroutine
						switch(ram[i] & 0xfff)
						{
							case 0x8d0:
								if(scale == 12)
								{
									nExplosions++;
								}
								break;
							
							
							case 0x8f3: // Asteroid Typ 1
								frameData->asteroidFound(pX, pY, 1, scale);
								break;
							case 0x8ff: // Asteroid Typ 2
								frameData->asteroidFound(pX, pY, 2, scale);
								break;
							case 0x90d: // Asteroid Typ 3
								frameData->asteroidFound(pX, pY, 3, scale);
								break;
							case 0x91a: // Asteroid Typ 4
								frameData->asteroidFound(pX, pY, 4, scale);
								break;
							case 0x929: // UFO
								frameData->saucerFound(pX, pY, scale);
								break;
							default:
								break;
						}
						break;
					case 0xd: // RTSL: Rücksprung sollte nicht vorkommen
					case 0xe: // JMPL: sollte nicht vorkommen		
						return false;
					case 0xf: // SVEC: Zeichnet einen kurzen Vektor ist aber unwichtig	
						break;
					default: // VCRT: Zeichnet Vektor d.h. Schuss oder Schiff
						// Vektor Y-Komponente
						dY=ram[i] & 0x3ff;
						// Vector X-Komponente
						dX=ram[i+1] & 0x3ff;

						// Vorzeichen
						if(ram[i] & 0x400)
						{
							dY = -dY;
						}
						if(ram[i+1] & 0x400)
						{
							dX = -dX;
						}

						// Heligkeit
						brightness = ram[i+1] >> 12;

						// Schuss?
						if(!dX && !dY && brightness == 15)
						{
							// Schuss!
							frameData->shotFound(pX, pY);
						}

						// Schiff?
						if(dX && dY && brightness == 12 && op == 6)
						{
							switch(shipSide)
							{
								case Right:
									tempDX = dX;
									tempDY = dY;
									shipSide = Left;
									break;
								case Left:
									shipSide = Right;
									// Schiff!
									frameData->shipFound(pX, pY, tempDX - dX, tempDY - dY);
									break;
								default:
									break;
							}

						}
				}
				if (op <= 0xa)
				{
					i++;
				}
				if (op != 0xe)
				{
					i++;
				}
			}

		}
	return true;

}

void Socket::normalizePositions()
{
	QPointF shipPos = frameData->shipData.position;

	QList<MovingObject*> movingObjectList = frameData->movingObjectList;
	for(int i = 0; i < movingObjectList.size(); i++)
	{
		// Alle Koordinaten in abhängigkeit vom Schiff
		QPointF nPos = movingObjectList[i]->position - shipPos;
		
		// X-Anpassung
		if(nPos.x() > 499.0)
		{
			nPos.setX(nPos.x() - 1024.0);
		}
		else if(nPos.x() < -524.0)
		{
			nPos.setX(nPos.x() + 1024.0);
		}
		// Y-Anpassung
		if(nPos.y() > 371.0)
		{
			nPos.setY(nPos.y() - 768.0);
		}
		else if(nPos.y() < -396.0)
		{
			nPos.setY(nPos.y() + 768.0);
		}
		movingObjectList[i]->position = nPos;
	}
}

void Socket::mergeFrames()
{
	FrameData *current = ai->getCurrent();
	FrameData *last = ai->getLast();

	// Schiff Daten
	current->shipData.match(&(last->shipData));

	// Ufo Daten
	current->saucerData.match(&(last->saucerData));

	// Asteroiden Daten
	QList<AsteroidData*> currentAList = current->asteroidDataList;
	QList<AsteroidData*> lastAList = last->asteroidDataList;
	for(int i = 0; i < lastAList.size(); i++)
	{
		for(int j = 0; j < currentAList.size(); j++)
		{
			if(currentAList[j]->match(lastAList[i]))
			{
				currentAList.removeAt(j);
				break;
			}
		}
	}

	// Schuss Daten
	QList<ShotData*> currentSList = current->shotDataList;
	QList<ShotData*> lastSList = last->shotDataList;
	for(int i = 0; i < currentSList.size(); i++)
	{
		int index = -1;
		double minDiff = 1.0;
		for(int j = 0; j < lastSList.size(); j++)
		{
			double diff = currentSList[i]->tryMatch(lastSList[j]);
			if(minDiff > diff)
			{
				minDiff = diff;
				index = j;
			}
		}
		if(index != -1)
		{
			currentSList[i]->match(lastSList[index]);
			lastSList.removeAt(index);
		}
	}
	/*for(int i = 0; i < lastSList.size(); i++)
	{
		int index = -1;
		for(int j = currentSList.size()-1; j > -1; j--)
		{
			
			double minDiff = 1.0;
			double diff = currentSList[j]->tryMatch(lastSList[i]);
			if(minDiff > diff)
			{
				minDiff = diff;
				index = j;
			}
			
		}
		if(index != -1)
		{
			currentSList[index]->match(lastSList[i]);
			currentSList.removeAt(index);
		}
	}*/
}

void Socket::receiveFrame()
{
	QByteArray packet;
	packet.resize(1026);
	if(readDatagram(packet.data(),1026)==1026)
	{
		// Netzwerk Checks
		static char prevFrame = 0;
		if(packet.data()[1024] != ++prevFrame)
		{
			qDebug()<<"Frames Lost: "<< packet.data()[1024]-prevFrame;
			prevFrame = packet.data()[1024];
		}
	
		parseFrame(packet);
		normalizePositions();
		mergeFrames();
		
		// Setup
		ai->swapFrames();
		ai->clearActions();

		//**********
		// KI
		// *********
		
		// Schüsse zuordnen
		cpu->whosShot();
		// Geschwindigkeiten berechenen
		cpu->calculateVelocities();
		// Enfernungsdaten berechenen
		cpu->generateDistanceData2();
		//cpu->generateDistanceData3();
		// Bedrohungen finden
		cpu->checkThread2();
		// Ziel auswählen
		cpu->findJob3();
		// Ziehlen
		cpu->aimBot2();
		// Letzte Notfall aktionen
		cpu->escape();		
		


		// Zeichen
		//ai->getMameScene()->progress();
		
		static long fN = 0;
		fN++;
		if(Options::PrintInfo)
		{
			qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXX";
			qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXX";
			qDebug() << "Frame No: " << fN;
			QList<ShotData*> sList = ai->getLast()->shotDataList;
			for(int i = 0; i < sList.size(); i++)
			{
				sList[i]->print();
			}
			qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXX";
			qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXX";
			qDebug() << " ";
			qDebug() << " ";
			qDebug() << " ";
		}

		/*QList<ShotData *> sL = ai->getLast()->shotDataList;
		for(int i = 0; i < sL.size(); i++)
		{
			if(sL[i]->hasSpeed)
				qDebug()<<sL[i]->velocity;
		}*/

		/*static QPointF point;
		static int fn = 0;
		double dist = 0.0;
		FrameData *data = ai->getLast();
		if(data->shipData.visible && !data->shotDataList.isEmpty())
		{
			QPointF shotPos = data->shotDataList[0]->position;
			if(!point.isNull())
			{
				QPointF vDist = shotPos - point;
				dist = sqrt(vDist.x() * vDist.x() + vDist.y() *vDist.y());
				
			}
			qDebug()<<"Frame: "<<fn<<"||Schuss: "<<shotPos<<"||V: "<<dist;
			fn++;
			point = shotPos;
		}*/

		
		/*static int n = 0;

		if(ai->getLast()->shipData.visible && n/6 < 800 )
		{
			if(n % 10 == 0)
			{
				ai->actionLeft(true);
				
			}
			else if(n % 10 == 8)
			{
				ai->actionFire(true);
				qDebug() << "N: " << (n -8) / 10<< "   A: " <<floor(ai->getLast()->shipData.angle);
			}
			else if(n % 80 == 60 && ai->getLast()->shotDataList.size() > 0 && 1==0)
			{
				QPointF shotPos = ai->getLast()->shotDataList[0]->position;
				QPointF shipPos = ai->getLast()->shipData.position;
				QPointF shotVec = shotPos - shipPos;
				double length = sqrt(shotVec.x() * shotVec.x() + shotVec.y() * shotVec.y());
				shotVec = shotVec / length;
				double sA = (180 / PI) * acos(shotVec.x());
				qDebug() << "N: " << (n -60) / 80<< "   A: " <<floor(ai->getLast()->shipData.angle) << "   S: " << sA;
			}
			else
			{
				ai->actionFire(false);
				ai->actionLeft(false);
			}
			n++;
		}*/


		// Senden
		QByteArray bArray = ai->getActionPacket();
	
		
		if(bArray.data()[6] & AICore::ACTION_HYPERSPACE)
		{
			AICpu::Hyper = true;
		}
		else
		{
			AICpu::Hyper = false;
		}
		

		if(bArray.data()[6] & AICore::ACTION_FIRE)
		{
			AICpu::Shot = true;
			nShots++;
		}
		else
		{
			AICpu::Shot = false;
		}


		static int nRots = 0;
		if(bArray.data()[6] & AICore::ACTION_LEFT || bArray.data()[6] & AICore::ACTION_RIGHT)
		{
			nRots++;
		}

		if(fN % 3600 == 0)
		{
			Messanger::Post() << (double)nRots / (double)nShots;
		}
		//qDebug() << (double)nExplosions / (double)nShots;


		/*if(bArray.data()[6] & AICore::ACTION_FIRE)
		{
			cpu->findJob2();
			Job *job =ai->getJob();
			if(job)
			{
				fire = cpu->applyAngle(job->target->angAstF, false);
			}
		}*/

		bArray.data()[7]++;
		int bytes = write(bArray);
		if(bytes != 8)
		{	
			qDebug() << "Not Send";
		}
		


		// Zeitmessung
		static QTime time;
		static QList<int> timeSamples;
		timeSamples << time.msecsTo(QTime::currentTime());
		if(timeSamples.size() == 100)
		{
			int sum = 0;
			for(int i = 0; i < timeSamples.size(); i++)
			{
				sum += timeSamples[i];
			}
			//qDebug() << qRound((double)sum / 100.0);
			timeSamples.clear();
		}
		time = QTime::currentTime();

	}

}




