#include <SDL/SDL.h>
#include <math.h>
#include "main.h"
#include "render.h"
#include "vector.h"
#include "blobs.h"
#include "atoms.h"
#include "images.h"
#include "particles.h"
#include "things.h"
#include "draw.h"
#include "panels.h"


base bases[MAXBASES];
int base_num = 2;

thing player;
thing crosshair;
thing gamecrosshair;

thing bullets[MAXBULLETS];
thing balls[MAXBALLS];
thing waste[MAXWASTE];
int bullet_num = 0, ball_num = 0, waste_num = 0;

image ballgfx[3][3];

int playerbase1 = 0, playerbase2 = 0;
float playerbase_track = 0.0;
float playerspeed = 0.01;

extern atom atoms[MAXATOMS];
extern int atom_num;

extern blob_bitmap blobgfx[4];
extern image balloonface[3];

extern unsigned char blobBuffer[GameScreenSize];

extern bool ballphysics;
extern float ballgravity;

extern int wasteballnum[3];

extern bool wait_level_restart;

colorARGB particle_color[3] = {255,255,255,127, 255,255,128,63, 255,255,0,0};
colorARGB shoot_color[1] = {255,127,191,255};
colorARGB sparkle_color[1] = {255, 255, 191, 0};

colorARGB base_active_color = {127,63,255,63};
colorARGB base_inactive_color = {127,0,127,0};
colorARGB base_choice_color = {127,255,255,255};
colorARGB base_hit_color = {127,255,255,0};
colorARGB base_critical_color = {127,255,0,0};

void InitPlayer()
{
    player.pos.x = bases[0].pos.x;
    player.pos.y = bases[0].pos.y;
    player.type = PLAYER;
}

void InitBases()
{
	int i;
	for (i=0; i<base_num; i++)
	{
		bases[i].radius = 96;
		bases[i].energy = MAXENERGY;
 		bases[i].pos.y = (9*GameScreenHeight)/10;
	}
	bases[0].pos.x = GameScreenWidth/10;
	bases[1].pos.x = 9*GameScreenWidth/10;
	bases[0].condition = ACTIVE;
	bases[1].condition = INACTIVE;
}

void InitCrosshair()
{
    crosshair.pos.x = GameScreenWidth/2 + PanelScreenWidth;
    crosshair.pos.y = GameScreenHeight/2 + PanelScreenHeight;
    crosshair.type = OTHER;
    SDL_WarpMouse(crosshair.pos.x, crosshair.pos.y);
}

void InitBallGfx()
{
    int i, j, x, y;
    int xp, yp;
	int c1, c2;
    int a1, r1, g1, b1;
    int a2, r2, g2, b2;
    int a, r, g, b;
    #define ca 64
    #define cb 64

	int blr[] = {128, ca/3, cb/3, 192, ca/2, cb/2, 255, ca, cb};
	int blg[] = {ca/3, 128, cb/3, ca/2, 192, cb/2, ca, 255, cb};
	int blb[] = {ca/3, cb/3, 128, ca/2, cb/2, 192, ca, cb, 255};

	int b1_width, b2_width;
	int fuck;

    unsigned int *src1, *dest;
    unsigned char *src2;
    float dv, ddv;
    for (i=0; i<3; i++)
    {
	    for (j=0; j<3; j++)
	    {
			ballgfx[i][j].width = blobgfx[i].width;
			ballgfx[i][j].height = ballgfx[i][j].width;
			ballgfx[i][j].bits = 32;
			ballgfx[i][j].size = ballgfx[i][j].width * ballgfx[i][j].height;
			ballgfx[i][j].data = (unsigned int*)malloc(ballgfx[i][j].size * sizeof(unsigned int));

			dest = ballgfx[i][j].data;
			src2 = blobgfx[i].bitmap;

			b1_width = balloonface[i].width;
			b2_width = ballgfx[i][j].width;

			dv = (float)(b1_width)/ (float)b2_width;
			src1 = balloonface[i].data;

			for (y=0; y<ballgfx[i][j].height; y++)
			{
			    for (x=0; x<ballgfx[i][j].width; x++)
			    {
					xp = x * dv;
					yp = y * dv;
					if (xp<0 || xp>=balloonface[i].width || yp<0 || yp>=balloonface[i].height)
					    c1 = 0;
					else
						c1 = *(src1 + xp + yp * balloonface[i].width);

					a1 = (c1 >> 24) & 255;
					r1 = (c1 >> 16) & 255;
					g1 = (c1 >> 8) & 255;
					b1 = c1 & 255;

					c2 = *src2++;
					a2 = c2;
					if (a2<64) a2 = 0;
					r2 = (blr[i*3 + j] * c2) >> 8;
					g2 = (blg[i*3 + j] * c2) >> 8;
					b2 = (blb[i*3 + j] * c2) >> 8;

					r = r2 + (((r1 - r2) * a1) >> 8);
					g = g2 + (((g1 - g2) * a1) >> 8);
					b = b2 + (((b1 - b2) * a1) >> 8);
					*dest++ = (a2<<24) | (r<<16) | (g<<8) | b;
				}
			}
	    }
	}
}

void InitThings()
{
    InitBases();
    InitPlayer();
    InitCrosshair();
    InitBallGfx();
}


void ShowDirection(unsigned int *vram)
{
	int c1 = 0xFFFF00FF;
	int c2 = 0;
	int c;
	float dc;

	float r, dx, dy;
	int x1, y1, x2, y2;

	float l = 0, dl = 16, lend;

	r = sqrt(pow((gamecrosshair.pos.x - player.pos.x),2) + pow((gamecrosshair.pos.y - player.pos.y),2));
	lend = r - 2*dl;
	dx = (gamecrosshair.pos.x - player.pos.x) / r;
	dy = (gamecrosshair.pos.y - player.pos.y) / r;
	int t = (SDL_GetTicks() / 16) % (int)(2*dl);

	c = c1;
	dc = (float)(c2 - c1) / (lend - l);

	while(l<lend)
	{
		x1 = player.pos.x + (l + t) * dx;
		y1 = player.pos.y + (l + t) * dy;
		x2 = player.pos.x + (l+t+dl) * dx;
		y2 = player.pos.y + (l+t+dl) * dy;

		DrawLine(x1, y1, x2, y2, vram, c, true);
		DrawLine(x1+1, y1, x2+1, y2, vram, c, true);
		DrawLine(x1, y1+1, x2, y2+1, vram, c, true);
		DrawLine(x1+1, y1+1, x2+1, y2+1, vram, c, true);
		l+=2*dl;
		c+=2*dl*dc;
	}
}

void ShowShoot(unsigned int *vram)
{
	int i;
	for (i=0; i<bullet_num; i++)
	{
		if (bullets[i].exists)
		{
            DoParticleExplosion(&bullets[i].pos, 4, 1, 0.01, 1, 0.05, 4, shoot_color[0], false);
		}
	}
}

void ShowBalls(unsigned int *vram)
{
	int i;
	for (i=0; i<ball_num; i++)
	{
		if (balls[i].exists)
		{
            RenderSprite(balls[i].pos.x, balls[i].pos.y, &ballgfx[balls[i].size][balls[i].color], vram, GameScreenWidth, GameScreenHeight);
		}
	}
}

int k = 0;
void ShowWaste(unsigned int *vram)
{
	k = k + 1;
	float sx = sin(k / 4.0) * 0.75 + 1.25;
	float sy = cos(k / 3.0) * 0.75 + 1.25;

	int i;
	for (i=0; i<waste_num; i++)
	{
		if (waste[i].exists)
		{
            RenderZoomSprite(waste[i].pos.x, waste[i].pos.y, sx, sy, &ballgfx[0][waste[i].color], vram, GameScreenWidth, GameScreenHeight, false);
		}
	}
}

void ShowBases(unsigned int *vram)
{
	int i;
	float t = SDL_GetTicks()/192.0;
	vector pos1, pos2, pos3, pos4, vel, acl;

	for (i=0; i<base_num; i++)
	{
       	pos1.x = bases[i].pos.x + 96*sin(t);
		pos1.y = bases[i].pos.y + 96*cos(t);
		pos2.x = bases[i].pos.x - 96*sin(t);
		pos2.y = bases[i].pos.y - 96*cos(t);
		vel.x = vel.y = 0;
		acl.x = acl.y = 0;

        CreateParticle(&pos1, &vel, &acl, &bases[i].col, 0, bases[i].glow, 0.01);
        CreateParticle(&pos2, &vel, &acl, &bases[i].col, 0, bases[i].glow, 0.01);
	}
}

void ShowEnergy(unsigned int *vram)
{
	RenderBox(8+1, ((float)(MAXENERGY - bases[0].energy)/MAXENERGY) * (GameScreenHeight-1), 24-1, GameScreenHeight-1-1, 0xFF40FF80, vram);
	RenderBox(GameScreenWidth - 24+1, ((float)(MAXENERGY - bases[1].energy)/MAXENERGY) * (GameScreenHeight-1), GameScreenWidth - 8-1, GameScreenHeight-1-1, 0xFF40FF80, vram);
	RenderBox(8+1, 0+1, 24-1, ((float)(MAXENERGY - bases[0].energy)/MAXENERGY) * (GameScreenHeight-1)-1, 0xFF008040, vram);
	RenderBox(GameScreenWidth - 24+1, 0+1, GameScreenWidth - 8-1, ((float)(MAXENERGY - bases[1].energy)/MAXENERGY) * (GameScreenHeight-1)-1, 0xFF008040, vram);
	RenderBoxLine(8, 0, 24, GameScreenHeight-1, 0xFF00FF00, vram, GameScreenWidth);
	RenderBoxLine(GameScreenWidth - 24, 0, GameScreenWidth - 8, GameScreenHeight-1, 0xFF00FF00, vram, GameScreenWidth);
}

void ClearShoot(unsigned int *vram)
{
	blob bl;
	int i;
	for (i=0; i<bullet_num; i++)
	{
		if (bullets[i].exists)
		{
			bl.x = bullets[i].pos.x;
			bl.y = bullets[i].pos.y;
			bl.size = 0;
            ClearBlob(&bl, blobBuffer, vram);
		}
	}
}

void ClearBalls(unsigned int *vram)
{
	blob bl;
	int i;
	for (i=0; i<ball_num; i++)
	{
		if (balls[i].exists)
		{
			bl.x = balls[i].pos.x;
			bl.y = balls[i].pos.y;
			bl.size = balls[i].size;
            ClearBlob(&bl, blobBuffer, vram);
		}
	}
}


void ClearThings(unsigned int *vram)
{
	ClearShoot(vram);
	ClearBalls(vram);
}

void ShowThings(unsigned int *vram)
{
    ShowShoot(vram);
    ShowBalls(vram);
    ShowWaste(vram);
	ShowEnergy(vram);
	ShowBases(vram);
	ShowDirection(vram);
}


bool TestShootCollisionWithBlobs(thing *a1)
{
    float r, l, l1, l2;
    vector d1, d2;
    atom *a2;

    int collidespace, r1, r2;
	bool notcollide = true;

    int i;
    r1 = a1->radius/2;
    for (i=0; i<atom_num; i++)
    {
        a2 = &atoms[i];
        if (a2->exists)
        {
	        r2 = a2->radius/2;
	        collidespace = r1 + r2;
	        r = Vdist(&a2->pos, &a1->pos);
	        if (r<collidespace)
	        {
				notcollide = false;

	            Vsub(&a2->pos, &a1->pos, &d1);
	            Vneg(&d1, &d2);
	            l = d1.x * d1.x + d1.y * d1.y;

	            // Project a1 on d1
	            l1 = (a1->vel.x * d1.x + a1->vel.y * d1.y) / l;

	            if (l1>=0)
	            {
	                Vscale(&d1, &d1, l1);

	                // Project a2 on d2
	                l2 = (a2->vel.x * d2.x + a2->vel.y * d2.y) / l;
	                Vscale(&d2, &d2, l2);

	                Vadd(&a1->vel, &d2, &a1->vel);
	                Vsub(&a1->vel, &d1, &a1->vel);

	                Vadd(&a2->vel, &d1, &a2->vel);
	                Vsub(&a2->vel, &d2, &a2->vel);
	            }
			}
        }
    }
    return notcollide;
}


bool TestShootCollisionWithBalls(thing *a1)
{
    float r;
    vector v;
    vector d1, d2;
    float l, l1, l2;
    thing *a2;

    int collidespace, r1, r2;
	bool notcollide = true;

    int i;
    r1 = a1->radius/2;
    for (i=0; i<ball_num; i++)
    {
        a2 = &balls[i];
        if (a2->exists)
        {
	        r2 = a2->radius/2;
	        collidespace = r1 + r2;
	        r = Vdist(&a2->pos, &a1->pos);
	        if (r<collidespace)
	        {
				notcollide = false;
				a2->exists = false;
				if (a2->size>MINBALLSIZE)
				{
					DoParticleExplosion(&a2->pos, 32, 0, 0.25, 1, 0.025, 8, particle_color[a2->size], false);
					CreateBall(&a2->pos, &a2->vel, a2->size-1, a2->radius/2, a2->color);
					Vneg(&a2->vel, &v);
					CreateBall(&a2->pos, &v, a2->size-1, a2->radius/2, a2->color);
					return notcollide;
				}
				else if (a2->size==MINBALLSIZE)
				{
					if (wasteballnum[a2->color]<2)
					{
                        wasteballnum[a2->color]++;
	                    DoParticleExplosion(&a2->pos, 16, 0, 0.01, 1, 0.01, 6, particle_color[a2->size], false);
	                    CreateWaste(&a2->pos, &a2->vel, a2->color);
						return notcollide;
					}
					else
					{
						a2->exists = true;

			            Vsub(&a2->pos, &a1->pos, &d1);
			            Vneg(&d1, &d2);
			            l = d1.x * d1.x + d1.y * d1.y;

			            // Project a1 on d1
			            l1 = (a1->vel.x * d1.x + a1->vel.y * d1.y) / l;

			            if (l1>=0)
			            {
			                Vscale(&d1, &d1, l1/2);

			                // Project a2 on d2
			                l2 = (a2->vel.x * d2.x + a2->vel.y * d2.y) / l;
			                Vscale(&d2, &d2, l2/2);

			                Vadd(&a1->vel, &d2, &a1->vel);
			                Vsub(&a1->vel, &d1, &a1->vel);

			                Vadd(&a2->vel, &d1, &a2->vel);
			                Vsub(&a2->vel, &d2, &a2->vel);

							DoParticleImpactExplosion(&a2->pos, &a1->vel, 45, 24, 0, 0.01, 1, 0.025, 6, sparkle_color[0], true);

						    return notcollide;
						}
					}
				}
			}
        }
    }
    return notcollide;
}


bool TestBallCollisionWithBalls(thing *a1)
{
    float r, l, l1, l2;
    vector d1, d2;
    thing *a2;

    int collidespace, r1, r2;
	bool notcollide = true;

    int i;
    r1 = a1->radius/2;
    for (i=0; i<ball_num; i++)
    {
        a2 = &balls[i];
        if (a2->exists)
        {
	        r2 = a2->radius/2;
	        collidespace = r1 + r2;
	        r = Vdist(&a2->pos, &a1->pos);
	        if (r<collidespace)
	        {
				notcollide = false;

	            Vsub(&a2->pos, &a1->pos, &d1);
	            Vneg(&d1, &d2);
	            l = d1.x * d1.x + d1.y * d1.y;

	            // Project a1 on d1
	            l1 = (a1->vel.x * d1.x + a1->vel.y * d1.y) / l;

	            if (l1>=0)
	            {
	                Vscale(&d1, &d1, l1);

	                // Project a2 on d2
	                l2 = (a2->vel.x * d2.x + a2->vel.y * d2.y) / l;
	                Vscale(&d2, &d2, l2);

	                Vadd(&a1->vel, &d2, &a1->vel);
	                Vsub(&a1->vel, &d1, &a1->vel);

	                Vadd(&a2->vel, &d1, &a2->vel);
	                Vsub(&a2->vel, &d2, &a2->vel);
	            }
			}
        }
    }
    return notcollide;
}

bool previouslycollide[MAXBALLS];

void TestBallCollisionWithBases(thing *a1, int n)
{
    float r, l, l1, l2;
    vector d1, d2;
    base *a2;

    int collidespace, r1, r2;
	bool notcollide = true;

    int i;
    int con;
    r1 = a1->radius/2;
    for (i=0; i<base_num; i++)
    {
        a2 = &bases[i];
        con = bases[i].condition & ACTIVE;
        if (con!=0)
        {
	        r2 = a2->radius;
	        collidespace = r1 + r2;
	        r = Vdist(&a2->pos, &a1->pos);
	        if (r<collidespace)
	        {
				notcollide = false;

				if (previouslycollide[n]) bases[i].energy -= ENERGYLOSS;
   				if (bases[i].energy<0) bases[i].energy = 0;

	            Vsub(&a1->pos, &a2->pos, &d1);
				Vnormalize(&d1);
				l = sqrt(a1->vel.x * a1->vel.x + a1->vel.y * a1->vel.y);
				Vscale(&d1,&d1,l);
				Vset(&a1->vel, &d1);
			}
        }
    }
    previouslycollide[n] = notcollide;
}

void CollisionShoots()
{
	int i;
	vector v;
	int degrees = 30;

	for (i=0; i<bullet_num; i++)
	{
		if (bullets[i].exists)
		{
			if ((bullets[i].pos.x < 0) || (bullets[i].pos.x >= GameScreenWidth))
			{
				if (bullets[i].pos.x<0) bullets[i].pos.x = 0;
				    else bullets[i].pos.x = GameScreenWidth-1;

					v.x = -bullets[i].vel.x;
					v.y = bullets[i].vel.y;
	                DoParticleImpactExplosion(&bullets[i].pos, &v, degrees, 64, 0, 0.02, 0.75, 0.015, 4, sparkle_color[0], false);
				    bullets[i].exists = false;
			}
			else if ((bullets[i].pos.y < 0) || (bullets[i].pos.y >= GameScreenHeight))
			{
				if (bullets[i].pos.y<0) bullets[i].pos.y = 0;
				    else bullets[i].pos.y = GameScreenHeight-1;

					v.x = bullets[i].vel.x;
					v.y = -bullets[i].vel.y;
	                DoParticleImpactExplosion(&bullets[i].pos, &v, degrees, 64, 0, 0.02, 0.75, 0.015, 4, sparkle_color[0], false);
				    bullets[i].exists = false;
			}
			else
			{
				bullets[i].exists = TestShootCollisionWithBlobs(&bullets[i]);
				if (bullets[i].exists)
					bullets[i].exists = TestShootCollisionWithBalls(&bullets[i]);
			}
		}
	}
}

void CollisionBalls()
{
	int i;
	vector v;

	for (i=0; i<ball_num; i++)
	{
		if (balls[i].exists)
		{
			TestBallCollisionWithBalls(&balls[i]);
			TestBallCollisionWithBases(&balls[i], i);
			
		}
	}
}

void UpdatePlayer()
{
	int x1 = bases[playerbase1].pos.x;
	int x2 = bases[playerbase2].pos.x;

	if (playerbase1!=playerbase2)
	{
		bases[playerbase1].condition &= (~ACTIVE);
  		bases[playerbase2].condition &= (~ACTIVE);
		playerbase_track+=playerspeed;

		player.pos.x = playerbase_track * x2 + (1.0 - playerbase_track) * x1;

		if (playerbase_track>=1.0)
		{
			playerbase_track = 0.0;
			bases[playerbase1].condition &= (~ACTIVE);
  			bases[playerbase2].condition |= ACTIVE;
			playerbase1 = playerbase2;
		}
	}
}

void UpdateShoots()
{
	int i;
	for (i=0; i<bullet_num; i++)
	{
		if (bullets[i].exists)
		{
			bullets[i].pos.x += bullets[i].vel.x;
			bullets[i].pos.y += bullets[i].vel.y;
		}
	}
}

void UpdateBalls()
{
	thing *b;
	int i;
	for (i=0; i<ball_num; i++)
	{
		b = &balls[i];
		if (b->exists)
		{
			b->pos.x += b->vel.x;
			b->pos.y += b->vel.y;
	        if (b->pos.x<0 || b->pos.x>GameScreenWidth) {b->pos.x-=b->vel.x; b->vel.x = - b->vel.x;}
	        if (b->pos.y>GameScreenHeight || b->pos.y<0) {b->pos.y-=b->vel.y; b->vel.y = - b->vel.y;}
			b->vel.y += ballgravity;
		}
	}
}

void UpdateBases()
{
	int i, con;
	for (i=0; i<base_num; i++)
	{
		con = bases[i].condition & ACTIVE;
		if (con==INACTIVE)
		{
            CopyColor(base_inactive_color, &bases[i].col);
			bases[i].glow = 1.0;
   			bases[i].energy+=4;
   			if (bases[i].energy>MAXENERGY) bases[i].energy = MAXENERGY;
			con = bases[i].condition & CHOICE;
			if (con==CHOICE)
			{
	            CopyColor(base_choice_color, &bases[i].col);
				bases[i].glow = 1.0;
			}
		}

		if (con==ACTIVE)
		{
            CopyColor(base_active_color, &bases[i].col);
			bases[i].glow = 1.0;
			bases[i].energy--;
			if (bases[i].energy<0) bases[i].energy = 0;
			if (bases[i].energy==0) wait_level_restart = true;
		}
	}
}

void UpdateWaste()
{
	vector v;
	thing *b;
	int i;
	for (i=0; i<waste_num; i++)
	{
		b = &waste[i];
		if (b->exists)
		{
			v.x = 8 * ((player.pos.x - b->pos.x) / Vdist(&player.pos, &b->pos));
			v.y = 8 * ((player.pos.y - b->pos.y) / Vdist(&player.pos, &b->pos));

			b->pos.x += b->vel.x + v.x;
			b->pos.y += b->vel.y + v.y;

	        if (b->pos.x<0 || b->pos.x>GameScreenWidth) {b->pos.x-=b->vel.x; b->vel.x = - b->vel.x;}
	        if (b->pos.y<0 || b->pos.y>GameScreenWidth) {b->pos.y-=b->vel.y; b->vel.y = - b->vel.y;}

			b->vel.y += 2*ballgravity;

			if (Vdist(&player.pos, &b->pos)<=4)
			{
				b->exists = false;
				CollectWasteball(b->color);
			}
		}
	}
}

void RunThings()
{
	UpdatePlayer();
	UpdateBases();
    UpdateBalls();
	UpdateShoots();
    UpdateWaste();
	CollisionShoots();
	if (ballphysics) CollisionBalls();
}

void CreateShoot()
{
	bullets[bullet_num].exists = true;
	bullets[bullet_num].pos.x = player.pos.x;
	bullets[bullet_num].pos.y = player.pos.y;

    float velspeed = 16;
    float velx = gamecrosshair.pos.x - player.pos.x;
    float vely = gamecrosshair.pos.y - player.pos.y;
    float r = sqrt(velx*velx + vely*vely);

    bullets[bullet_num].vel.x = (velx / r) * velspeed;
    bullets[bullet_num].vel.y = (vely / r) * velspeed;

	bullets[bullet_num].mass = 256.0;
	bullets[bullet_num].radius = 16;

	bullet_num++;
	if (bullet_num>=MAXBULLETS) bullet_num = 0;
}

void CreateBall(vector *pos, vector *vel, int size, int radius, int color)
{
	balls[ball_num].exists = true;
	balls[ball_num].pos.x = pos->x;
	balls[ball_num].pos.y = pos->y;
	if (vel->x>=0) balls[ball_num].vel.x = BALLVELX;
	    else balls[ball_num].vel.x = -BALLVELX;
	balls[ball_num].vel.y = -BALLVELY;

	balls[ball_num].radius = radius;
	balls[ball_num].size = size;
	balls[ball_num].type = ENEMY;
	balls[ball_num].color = color;
	ball_num++;
}

void CreateWaste(vector *pos, vector *vel, int color)
{
	waste[waste_num].exists = true;
	waste[waste_num].pos.x = pos->x;
	waste[waste_num].pos.y = pos->y;
	if (vel->x>=0) waste[waste_num].vel.x = BALLVELX;
	    else waste[waste_num].vel.x = -BALLVELX;
	waste[waste_num].vel.y = -BALLVELY;

	waste[waste_num].type = WASTE;
	waste[waste_num].color = color;
	waste_num++;
}
