#include <GameOS\GameOS.hpp>
#include <GOSScript\GOSScriptHeaders.hpp>
#include <memory.h>
#include <string.h>
#include <GameOS\ToolOS.hpp>
//
//
//



void __stdcall ExitTestNetworkShell();
void __stdcall DoMainMenuLogic();
//
// Message 'types' used by application (224 maximum!)
//
typedef enum
{
	NM_PADDLE_LOCATION = 0,
	NM_BALL_LOCATION,
	NM_EXIT_GAME
};


typedef struct
{
	WORD x,y;

} PaddleLocation;


typedef struct
{
	WORD x,y;
	short int vel_x,vel_y;

} BallLocation;


bool Running=false;


int Player1Score = 0,
	Player2Score = 0;
int homeIndex = 0;
int awayIndex = 0;
int Counter = 500;


int ballx = 320;
int bally = 240;
int ballx_vel = 200;
int bally_vel = 0;
int ball_w;
int ball_h;

int pad_x[]= {32,640-64};
int pad_y[]= {240,240};

int pad_h;
int pad_w;

double LastTime;

extern HGOSFONT3D font3d;

HGOSSCRIPT PongScript;


//
// Come here to clean up game
//
void CleanUpPong()
{
//
// Stop game
//
	gosScript_EndScript( PongScript );
//
// Send 'game finished' packet to everyone
//
	if( gos_NetInformation(gos_AmITheServer) )
	{
		NetPacket Packet;
		memset( &Packet, 0, sizeof(Packet) );
		Packet.ToID = (DWORD)gos_NetInformation(gos_AllID);
		Packet.Type = NM_EXIT_GAME;
		Packet.pData = 0;
		Packet.Flags = 0;
		Packet.Length = 0;
		gos_NetSendMessage( &Packet );
	}
//
// stop the network shell layer
//
	gos_NetEndGame();
//
// unregister game-related script variables
//
	if( Running )
	{
		gosScript_UnregisterVariable("pad_x");
		gosScript_UnregisterVariable("pad_y");
		gosScript_UnregisterVariable("ballx");
		gosScript_UnregisterVariable("bally");
	}
}













void BounceOffPaddle(int y, int paddle_index)
{
	int localy = y - pad_y[paddle_index];
	if (localy == 0)
		localy = 1;

	float bounce = Counter * 1.5f * ((float)localy/(float)pad_h - 0.50f); 
	bally_vel = (int)bounce;
}







void __stdcall DoPongGameLogic()
{
//
// Check for ESC key (back to main menu)
//
	DWORD KeyPress=gos_GetKey()&255;

	if( KeyPress==27 )
	{
		CleanUpPong();
		Environment.TerminateGameEngine=ExitTestNetworkShell;
		Environment.DoGameLogic=DoMainMenuLogic;
		Environment.UpdateRenderers=0;
		Environment.Key_Exit = KEY_ESCAPE;
		return;
	}
//
// Check for any network packets
//
	NetPacket* Message;

	Message=gos_NetGetMessage();
	while( Message )
	{
		switch( Message->Type)
		{
			case NM_PADDLE_LOCATION:
			{
				PaddleLocation* Paddle = (PaddleLocation*)Message->pData;
				pad_x[awayIndex] = Paddle ->x;
				pad_y[awayIndex] = Paddle ->y;
				break;
			}
			case NM_BALL_LOCATION:
			{
				BallLocation* Ball = (BallLocation*)Message->pData;
				ballx = Ball->x;
				bally = Ball->y;
				ballx_vel = Ball->vel_x;
				bally_vel = Ball->vel_y;
				break;
			}
			case gosNet_GameEnded:
			case NM_EXIT_GAME:
			{
				CleanUpPong();
				Environment.TerminateGameEngine=ExitTestNetworkShell;
				Environment.DoGameLogic=DoMainMenuLogic;
				Environment.UpdateRenderers=0;
				Environment.Key_Exit = KEY_ESCAPE;
				return;
			}
			default:
				break;
		}
		Message=gos_NetGetMessage();
	}
//
// If the server, lock the game when 2 players are in
//
	if( gos_NetInformation(gos_AmITheServer) )
	{
		if( (DWORD)gos_NetInformation(gos_NumberOfPlayers)>1 )
		{
			if( 0==(DWORD)gos_NetInformation(gos_LockedStatus) )
			{
				gos_NetServerCommands(gos_Command_LockGame, 1);
			}
		}
		else
		{
			if( 0!=(DWORD)gos_NetInformation(gos_LockedStatus) )
			{
				gos_NetServerCommands(gos_Command_LockGame, 0);
			}
		}
	}
//
// Do game 'physics'
//
	if( (DWORD)gos_NetInformation(gos_NumberOfPlayers)>1 )
	{
		float x,y;
		int hit_something = 0;
		bool button_down = false;
		DWORD buttons;
		static int nPressed = 0;

		gos_GetMouseInfo( &x,&y,0,0,0,&buttons );

		pad_y[homeIndex] = (int)(y*480);
		if (pad_y[homeIndex] > 480) 
		{
			pad_y[homeIndex] = 480;
		}

		if (buttons & 1 && nPressed < 5)
		{
			button_down = TRUE;
			nPressed++;
		}

		int ball_right_edge = ballx + ball_w;
		int ball_left_edge = ballx;
		int ball_center_y = bally + ball_h/2;

		
		if (ballx_vel > 0 &&
			ball_right_edge > pad_x[1] &&
			ball_center_y > pad_y[1] &&
			ball_center_y < pad_y[1] + pad_h)
		{
			// we struck the right paddle
			if (homeIndex == 1 && (buttons & 1))
			{
				Counter = 900;
			}
			else
			{
				Counter = 500;
			}

			BounceOffPaddle(ball_center_y, 1);
			ballx_vel = -Counter;
			hit_something = 2;
		}
		else if (ballx_vel < 0 &&
				ball_left_edge  < pad_x[0] + pad_w &&
				ball_center_y > pad_y[0] &&
				ball_center_y < pad_y[0] + pad_h)
		{
			// we struck the left paddle
			if (homeIndex == 0 && (buttons & 1))
			{
				Counter = 900;
			}
			else
			{
				Counter = 500;
			}
			
			BounceOffPaddle(ball_center_y, 0);
			ballx_vel = Counter;
			hit_something = 1;
		}
			
		if (!hit_something)
		{
			if (ballx <= 0)
			{
				Player2Score++;
				nPressed = 0;
				ballx_vel = Counter;
				ballx = 50;
				bally = (int)(((float)gos_rand()/(float)32767) * 480);
				hit_something = 3;
			}
			if (bally <= 0)
			{
				bally_vel = -bally_vel;
				bally = 0;
				hit_something = 3;
			}
			if (ballx+ball_w >= 639)
			{
				Player1Score++;
				nPressed = 0;
				ballx = 640 - ball_w - 50;
				bally = (int)(((float)gos_rand()/(float)32767) * 480);
				ballx_vel = -Counter;
				hit_something = 3;
			}
			if (bally+ball_h >= 479)
			{
				bally = 480 - ball_h;
				bally_vel = -bally_vel;		
				hit_something = 3;
			}
		}		

//
// Move ball
//
		double FrameLength=gos_GetElapsedTime()-LastTime;
		if( FrameLength>0.1 )
			FrameLength=0.1;
		LastTime=gos_GetElapsedTime();

		ballx += (int)(ballx_vel * FrameLength);
		bally += (int)(bally_vel * FrameLength);
//
// Send network packets
//		
		PaddleLocation	Paddle;
		BallLocation	Ball;

		Paddle.x = (WORD)pad_x[homeIndex];
		Paddle.y = (WORD)pad_y[homeIndex];

		DWORD OpponentID;
		if( gos_NetInformation(gos_MyID)==gos_NetInformation(gos_PlayerID,0) )
			OpponentID=(DWORD)gos_NetInformation(gos_PlayerID,1);
		else
			OpponentID=(DWORD)gos_NetInformation(gos_PlayerID,0);
//
// Send my paddle location
//
		NetPacket Packet;
		memset( &Packet, 0, sizeof(Packet) );
		Packet.ToID =OpponentID;
		Packet.Flags = 0;

		Packet.Type = NM_PADDLE_LOCATION;
		Packet.pData = &Paddle;
		Packet.Length = sizeof(Paddle);
		gos_NetSendMessage(&Packet);

//
// Send the ball location if it has hit somthing
//
		if ((gos_NetInformation(gos_AmITheServer) && hit_something == 1) ||
			(!gos_NetInformation(gos_AmITheServer) && hit_something == 2))
		{
			Ball.x = (WORD)ballx;
			Ball.y = (WORD)bally;
			Ball.vel_x = (WORD)ballx_vel;
			Ball.vel_y = (WORD)bally_vel;
			Packet.Type = NM_BALL_LOCATION;
			Packet.pData = &Ball;
			Packet.Length = sizeof(Ball);

			gos_NetSendMessage( &Packet );
		}
	}
}






void __stdcall UpdatePongRenderers()
{
//
// Draw scores on the screen - scripts updates the bat and ball
//
	gos_SetupViewport( false,1.0,false,0,0.0,0.0,1.0,1.0 );
	gos_TextSetAttributes( font3d, 0xffffffff, -8, 0, 1, 0, 0 );

	gos_TextSetPosition(100,10);
	gos_TextDraw("%0.3d",Player1Score);

	gos_TextSetPosition(590,10);
	gos_TextDraw("%0.3d",Player2Score);

}





void __stdcall TerminatePongGameEngine()
{
	CleanUpPong();
	ExitTestNetworkShell();
}



//
// Decode game specific data (in Test Pong, it's just an ASCII string anyway)
//
char* DecodeData( void* Data )
{
	if( Data )
	{
		return (char*)Data;
	}
	else
	{
		return "";
	}
}





DWORD InitPongGame()
{
//
// Setup environment
//
	Environment.NetworkMaxPlayers = 2;
	Environment.NetworkMinPlayers = 2;
//
// Test game specific data - fill game information with current time / date
//
	strncpy( (char*)Environment.NetGameInfo, gos_FileTimeString( gos_GetTimeDate() ), 15 );
//
// Test game specific data - pointer to a routine to decode data
//
	Environment.DecodeGameInfo=DecodeData;
//
// Start pong test
//
	if( !gos_NetStartGame(0) )
	{
		return 1;
	}
//
// Setup variables for game
//
	Environment.DoGameLogic=DoPongGameLogic;
	Environment.UpdateRenderers=UpdatePongRenderers;
	Environment.TerminateGameEngine=TerminatePongGameEngine;
	Environment.Key_Exit = 0;

	ball_w = ball_h = 16;
	pad_h = 128;
	pad_w = 32;

	gosScript_RegisterVariable("pad_x",&pad_x,GOSVAR_INT,2);
	gosScript_RegisterVariable("pad_y",&pad_y,GOSVAR_INT,2);
	gosScript_RegisterVariable("ballx",&ballx,GOSVAR_INT,0);
	gosScript_RegisterVariable("bally",&bally,GOSVAR_INT,0);

	if( gos_NetInformation(gos_AmITheServer) )
	{
		homeIndex = 0;
		awayIndex = 1;
	}
	else
	{
		homeIndex = 1;
		awayIndex = 0;
	}
//
// Start the game running (game is in a script)
//
	LastTime=gos_GetElapsedTime();

	PongScript=gosScript_ExecuteScript("assets\\scripts\\pong.script",0x0000);

	Running=true;

	return 0;
}


