// exampleplayer.cpp: Beispielspieler für Asteroids
// Original: Harald Bögeholz / c't

#include "exampleplayer.h"
#include "commonplayer.h"
#include "game.h"

#ifdef _DEBUG
#include "output.h"
#endif

extern Stats stats;

using namespace CommonPlayer;


int gameMode, muzzleHot = 0;
int calibration_counter = 0;
int memorize_rotations = 0;
int movement_sequence = -1; // Flugsteuerung zurücksetzen
//int sr_frames = 0, sr_sum = 0, sr_count = 0;
bool saucer_alarm = false;
	
//time_t timer;
TaskList Schedule;



const char* ExamplePlayer::GetName(void) const
{
	return "ExamplePlayer";
}

const char* ExamplePlayer::GetToken(void) const
{
	// Nur 3 Buchstaben erlaubt
	return "fya";
}

void ExamplePlayer::Prepare (const GameStatus& gamestate)
{

}

void ExamplePlayer::MakeTurn (GameStatus& gamestate, KeysPacket &turn)
{
	unsigned __int32 startTimer = stats.StatsGetTimer();
			
	int calib, foresee = 0;
	float gamma;
	GameObject *currShot, *currObj, *attackObj, *closestObj;
	Ship *ship;
	//Vector2 direc;
	bool found;
	bool display_events = false;
	
	if (!gamestate.ship.IsPresent())
		return;
	
	
	// Blick in die Zukunft (funktioniert nicht)============	
	ship = &gamestate.ship;
	//ship->toFuture(foresee);
	//gamestate.saucer.toFuture(foresee);
	ObjectManager objm1, objm2;
	
	ObjectManager *ast_future = &gamestate.m_objects; // &objm1;
	ObjectManager *shot_future = &gamestate.m_shots; // &objm2;
	/*
	ast_future->copyFrom(&gamestate.m_objects);
	shot_future->copyFrom(&gamestate.m_shots);
	for (int i = 0; i < ast_future.count(); i++)
		ast_future[i]->toFuture(foresee);
	for (int i = 0; i < shot_future.count(); i++)
		shot_future[i]->toFuture(foresee);
	*/

	Schedule.connect(&gamestate, ast_future, shot_future, &turn);

	// Auswahl des GameMode=================================
	if (gamestate.nasteroids > 0 || gamestate.saucer.IsPresent())
		gameMode = GAMEMODE_ATTACK;
	else
		gameMode = GAMEMODE_MOVETOCENTER;


	// Los gehts!!!	========================================
	if (gameMode == GAMEMODE_ATTACK)
	{
		// Berechnung der Distanz, und =====================
		// - gefährliches Objekt mit minimalen Abstand suchen
		// - gefährliches Objekt mit maximaler Priorität suchen
		int currPrio = -1000000, currDist = 1000000;
		for (int i = 0; i < ast_future->count(); i++)	
		{
			currObj = (*ast_future)[i];
			if (currObj->objecttype == TYPE_ASTEROID)
				InitializeDistanceVectors(&closestObj, &attackObj, currObj, ship, shot_future, &currPrio, &currDist);
		}
		if (gamestate.saucer.IsPresent())
		{
			currObj = &gamestate.saucer;
			InitializeDistanceVectors(&closestObj, &attackObj, currObj, ship, shot_future, &currPrio, &currDist);
			saucer_alarm = true;			
		}
		
		for (int i = 0; i < shot_future->count(); i++)	
		{
			currShot = (*shot_future)[i];
			if (currShot->objecttype == TYPE_SHOT)
				currShot->dist = currShot->pos - ship->pos;
		}
		
		// Kalibrierung des Winkelbyte======================
		// muss *vor* der Drehung ausgeführt werden
		if (calibration_counter <= 0)
		{
			calib = ship->calibrateWinkelbyteByView(memorize_rotations);
			if (abs(calib - ship->Winkelbyte) > 0)
			{
				printf("%d: Calibration: %d --> %d     rot: %d \n", gamestate.frameno, ship->Winkelbyte, calib, memorize_rotations);
				ship->Winkelbyte = calib;
			}
			
			calibration_counter = (int) FRAMERATE / 4;
				
		}
		
		if (calibration_counter == foresee + 1)
			memorize_rotations = 0;		

		calibration_counter--;

		// Objekte anzeigen=================================
		if (gamestate.frameno % 4 == 0 && false) // <-- Schalter für Objektanzeige
		{	
			clrscr();
			gamestate.Kometenanzeige(ast_future, shot_future, &gamestate.saucer, ship);
		}
		if (display_events) printf("%d: shots: %d ", gamestate.frameno, gamestate.nshots);
		
		// Vorhalten und Steuerung==========================
		gamma = normalizeAngle(attackObj->dist.phase() - attackObj->att_alpha - ship->omega());	
		if (gamma >  ROTATION_ANGLE / (float) 2)
		{
			Schedule.plan(TASK_TURNLEFT);
			if (display_events) printf("L ");
			ship->ChangeWinkelbyte(1);
			memorize_rotations += 1;
		}
		if (gamma < -ROTATION_ANGLE / (float) 2)
		{
			Schedule.plan(TASK_TURNRIGHT);
			if (display_events) printf("R ");
			ship->ChangeWinkelbyte(-1);
			memorize_rotations += -1;
		}
	
		// Hyperspace=======================================
		// Wenn Skalarprodukt (obj.vel * direc) < 0: Objekt kommt auf Schiff *zu*!
		float vel_rel = (closestObj->vel - ship->vel).length();
		if (vel_rel > 16) vel_rel = 0; // Ist das hier nötig? 
		if (closestObj->dist.length() < closestObj->radius + ship->radius + vel_rel &&
			closestObj->dist * closestObj->vel < 0)	// Skalarprodukt < 0
		{
			//turn.hyperspace(true);
			Schedule.plan(TASK_HYPERSPACE);
			printf("%d: Hyperspace due to tag %d!!! rad: %d, dist: %.0f, speed: %.2f\n", gamestate.frameno, closestObj->tag, closestObj->radius, closestObj->dist.length(), vel_rel);
		}
		
		// Hyperspace für Ufoschüsse
		if (!gamestate.saucer.IsPresent() && shot_future->count() == 0)
			saucer_alarm = false;			
		for (int i = 0; i < shot_future->count(); i++)
		{
			currShot = (*shot_future)[i];
			if (currShot->dist.length() < ship->radius + 16 &&
				flybyDist(currShot, ship) < ship->radius &&
				currShot->dist * currShot->vel < 0 && // Skalarprodukt < 0
				currShot->tag == 0 && // fremder Schuss
				saucer_alarm) // Ohne Ufo-Alarm kein Hyperspace
			{
				Schedule.plan(TASK_HYPERSPACE);
				printf("%d: SHOT Hyperspace!!! dist: %.0f \n", gamestate.frameno, currShot->dist.length());
				break;
			}
		}
		
		// Gasgeben=========================================
		if (closestObj->dist.length() > 250 && gamestate.ship.vel.length() < 3)	 // beschleunigen, wenn nichts in der Nähe
		{
			Schedule.plan(TASK_THRUST);
			if (display_events) printf("T ");
		}
		
		if (gamestate.ship.vel.length() < 1 && gamestate.saucer.IsPresent())  
		{
			Schedule.plan(TASK_THRUST);
			if (display_events) printf("T ");
		}

		if (display_events && muzzleHot) printf("muzzleHot! ");
		
		// Boardschütze ====================================
		if (fabs(gamma) < FiringAccuracy(attackObj) && !muzzleHot && gamestate.nshots < 4)  // Feuerknopf drücken, falls letztesmal nicht gefeuert wurde
		{
			if (display_events) printf("FIRE! \n");
			
			Vector2 vel_rel = attackObj->vel - ship->vel;
			if (display_events) 
				//printf("pos(%.0f, %.0f), att(%.0f, %.0f), vel(%.3f, %.3f), view: %.3f, gamma: %.3f, alpha: %.3f, omega: %.3f ", ship->pos.x, ship->pos.y, attackObj->pos.x, attackObj->pos.y, vel_rel.x, vel_rel.y, ship->view.phase(), gamma, attackObj->att_alpha, ship->omega());
				printf("tag: %d, dist: %.0f, prio: %d, acc: %.3f", attackObj->tag, attackObj->att_dist.length(), attackObj->prio, gamma);
			Schedule.plan(TASK_FIRE);
							
			// Künstlicher Shot wegen Shot-Tracking
			GameObject *fakeshot = new Shot((int) ship->pos.x, (int) ship->pos.y);
			Vector2 shot_offs(SHOT_OFFSET * cos(ship->omega()), SHOT_OFFSET * sin(ship->omega()));
			Vector2 shot_vel(SHOT_VELOCITY * cos(ship->omega()), SHOT_VELOCITY * sin(ship->omega()));
			fakeshot->vel = ship->vel + shot_vel;
			fakeshot->pos = ship->pos + shot_offs - fakeshot->vel;
			fakeshot->tag = attackObj->tag;
			Schedule.plan(gamestate.frameno + 1, TASK_NEWOBJECT, fakeshot);

			stats.IncShots(); // sollte immer da stehen, wo geschossen wird		
			muzzleHot += 2; // Jedes 2. Mal feuern
			if (attackObj->objecttype == TYPE_SAUCER) muzzleHot += 20;
			
		}
						
		if (muzzleHot) muzzleHot--;
		if (display_events) printf("\n");
		
	} // attack	
		
	if (gameMode == GAMEMODE_MOVETOCENTER)
	{	
		switch (movement_sequence)
		{
			case -1: // Sequenz inaktiv
				if (!ObjectCentered(ship->pos, 0.2))
					movement_sequence = 0; // Starte Sequenz
				else
					movement_sequence = 2; // Bremse trotzdem
			break;
			case 0: // Abbremsen
				if ((ship->vel).length() > 2)
					SteerShipTo(ship->pos - ship->vel, ship, &Schedule);
				else
					movement_sequence++; // Nächster Schritt der Sequenz
			break;
			case 1: // Fliege zu Bildschirmmitte
				SteerShipTo(ScreenMidpoint, ship, &Schedule);
				if (ObjectCentered(ship->pos, 0.2))
					movement_sequence++; // Nächster Schritt der Sequenz
			break;
			case 2: // Nochmal Abbremsen
				if ((ship->vel).length() > 1)
					SteerShipTo(ship->pos - ship->vel, ship, &Schedule);
				else
					movement_sequence = -1; // Sequenz zurücksetzen
			break;
			
		} // end switch
		
	}
	
	// Execute Schedule=================================
	Schedule.execute();
			
	// Stats-Zeug
	unsigned __int32 endTimer = stats.StatsGetTimer();
	stats.UpdateMakeTurnPerformance(endTimer - startTimer);
	
}


void ExamplePlayer::GameOver (const GameStatus& gamestate)
{

}


void ExamplePlayer::Abort (const GameStatus& gamestate)
{

}


const ExtGameStatus* ExamplePlayer::GetExtGameStatus(void) const
{
	return NULL;
}


