#include <stdio.h>
#include <math.h>
#include <time.h>

#include "MAIN.h"
#include "UTIL.h"
#include "EGBCTRL.h"
#include "PAGECTRL.h"
#include "BGM.h"

#include "../airforce/airforce.h"
#include "../tomahawk/tomahawk.h"

#include "BALLDEMO.h"

static unsigned int IntegerSqrt(unsigned int N)
{
	unsigned int x=N;
	for(;;)
	{
		auto y=N/x;
		if(y>=x)
		{
			return y;
		}
		x=(x+y)/2;
	}
	return 0;
}


struct Ball
{
	int x,y,vx,vy;
	int c;
};

static int writePage=0;
static int nBall;
#define maxNBall 32
static struct Ball ball[maxNBall];
static const int gravityAccel=2;

void BallDemoInit(void)
{
	nBall=4;
	ball[0].x=320;
	ball[0].y=300;
	ball[0].vx=8;
	ball[0].vy=0;
	ball[0].c=15;

	ball[1].x=320;
	ball[1].y=200;
	ball[1].vx=-8;
	ball[1].vy=0;
	ball[1].c=15*32;

	ball[2].x=200;
	ball[2].y=30;
	ball[2].vx=0;
	ball[2].vy=0;
	ball[2].c=15*1024;

	ball[3].x=440;
	ball[3].y=30;
	ball[3].vx=0;
	ball[3].vy=0;
	ball[3].c=15*1024+15;
}

void BallCollision(struct Ball *a,struct Ball *b)
{
	int nx=b->x-a->x;
	int ny=b->y-a->y;

	int rvx=b->vx-a->vx;
	int rvy=b->vy-a->vy;

	if(rvx*nx+rvy*ny>0 || nx*nx+ny*ny>3600)
	{
		return;
	}

	short nl=IntegerSqrt(nx*nx+ny*ny);
	if(0<nl)
	{
		nx=nx*128/nl;
		ny=ny*128/nl;

		int k1=nx*a->vx+ny*a->vy;
		int k2=nx*b->vx+ny*b->vy;

		int nextVX1=a->vx+nx*(k2-k1)/16384;
		int nextVY1=a->vy+ny*(k2-k1)/16384;

		int nextVX2=b->vx+nx*(k1-k2)/16384;
		int nextVY2=b->vy+ny*(k1-k2)/16384;

		a->vx=nextVX1;
		a->vy=nextVY1;
		b->vx=nextVX2;
		b->vy=nextVY2;
	}
}

void BallDemoOneStep(void)
{
	int i;
	int area[256*16],minmax[4];
	for(i=0; i<nBall; ++i)
	{
		ball[i].x+=ball[i].vx;
		ball[i].y+=ball[i].vy;
		ball[i].vy+=gravityAccel;

		if(380<=ball[i].y && 0<ball[i].vy)  // Physical coord 0<Y<400, Rendered to Y/2 in FM-7
		{
			if(30<ball[i].vy)  // Take some energy off
			{
				ball[i].vy=30;
			}
			ball[i].y=380;
			ball[i].vy=-ball[i].vy;
		}
		if(ball[i].x<=20 && ball[i].vx<0)
		{
			ball[i].x=20;
			ball[i].vx=-ball[i].vx;
		}
		if(ball[i].x>=620 && ball[i].vx>0)
		{
			ball[i].x=620;
			ball[i].vx=-ball[i].vx;
		}
	}


	for(i=0; i<nBall; ++i)
	{
		for(int j=i+1; j<nBall; ++j)
		{
			BallCollision(&ball[i],&ball[j]);
		}
	}


	YGH_clearPage(&rp[writePage],0);

	for(i=0; i<nBall; ++i)
	{
		YGH_color(&rp[writePage],ball[i].c);
		YGH_circleArea(&rp[writePage],area,minmax,ball[i].x/2,ball[i].y/2,10);
		YGH_areaPaint(&rp[writePage],area,minmax);
	}

	ShowBuffer(writePage);
	writePage=1-writePage;
}

void BallDemoCleanUp(void)
{
	// Don't clear the ball coordinates for resuming!
}

void BallDemo(int bgmStop,int resume)
{
	int i=0;
	clock_t lastClk;

	SetUpEGB320x240_320x240();
	SetUpPage320x240_320x240();

	lastClk=clock();

	if(0==resume)
	{
		BallDemoInit();
	}
	for(;;)
	{
		// clock() shouldn't be used for real-time clock in most of the platforms.
		// It's fine on TOWNS OS native app.
		clock_t newClk;
		for(newClk=clock(); (newClk-lastClk)*20<CLOCKS_PER_SEC; newClk=clock())
		{
		}
		lastClk=newClk;

		if(0==(i&31) && nBall<maxNBall)
		{
			ball[nBall].x=320;
			ball[nBall].y=0;
			ball[nBall].vx=0;
			ball[nBall].vy=0;
			ball[nBall].c=rand()&32767;
			++nBall;
		}

		BallDemoOneStep();

		if(0!=CheckButtonPress() || bgmStop<=GetBGMCounter())
		{
			break;
		}

		++i;
	}
	BallDemoCleanUp();
}

