#ifndef _CAR_C
#define _CAR_C

#include <stdio.h>
#include <stdlib.h>

#include "car.h"

car cars[MAX_CAR];
int car_w,car_l,car_rad;
int car_accel;
float car_break;
float car_friction;
int car_turn;
gp_spr *car_shadow;

int init_cars(level *l,char *name)
{
	int c,i,x,y,ang,xd,yd;
	char sprname[256];
	gp_spr **sprs;
	FILE *f;
	if (l == 0)
		return 1;
	printf("Loading car file: ");
	f = fopen(name,"r");
	if (f == 0)
	{
		printf("Couldn't open file\n");
		return 1;
	}
	if (fscanf(f,"%s\n%d\n%f\n%f\n%d\n",sprname,&car_accel,&car_break,&car_friction,&car_turn) != 5)
	{
		printf("Bad header\n");
		free(l);
		fclose(f);
		return 0;
	}
	fclose(f);
	printf("OK\n");
	printf("Loading car sprites: ");
	sprs = gp_spr_paint_loadfile(sprname,&c);
	if (sprs == 0)
	{
		printf("Couldn't load file\n");
		return 2;
	}
	if (c != MAX_CAR+1)
	{
		printf("Incorrect number of sprites\n");
		gp_spr_ar_delete(sprs,c);
		return 2;
	}
	printf("OK\n");
	car_l = (sprs[0]->f->getw)(sprs[0]); /* Car sprites point -> that way */
	car_w = (sprs[0]->f->geth)(sprs[0]);
	car_rad = int_sqrt(car_l*car_l+car_w*car_w);
	/* Identify start location & angle */
	x = l->checkpoints[0];
	y = l->checkpoints[1];
	i = l->blocks[l->map[x+l->w*y]].i;
	if (i == BLOCK_I_RIGHT)
	{
		ang = 0;
		x = (x+1)*l->sprw-(car_l/2);
		y = y*l->sprh+(l->sprh-(car_w+4))/2;
		xd = -(car_l+4);
		yd = car_w+4;
	}
	else if (i == BLOCK_I_UP)
	{
		ang = 270 << 16;
		x = x*l->sprw+(l->sprw-(car_w+4))/2;
		y = y*l->sprh+(car_l/2);
		xd = car_w+4;
		yd = car_l+4;
	}
	else if (i == BLOCK_I_LEFT)
	{
		ang = 180 << 16;
		x = x*l->sprw+(car_l/2);
		y = y*l->sprh+(l->sprh+car_w+4)/2;
		xd = car_l+4;
		yd = -(car_w+4);
	}
	else
	{
		ang = 90 << 16;
		x = x*l->sprw+(l->sprw+car_w+4)/2;
		y = (y+1)*l->sprh-(car_l/2);
		xd = -(car_w+4);
		yd = -(car_l/4);
	}
	printf("Converting sprites and initialising cars: ");
	for (c=0;c<MAX_CAR;c++)
	{
		cars[c].player = 0;
		cars[c].xd = cars[c].yd = cars[c].zd = 0;
		cars[c].lap = cars[c].check = cars[c].hint = 0;
		for (i=0;i<MAX_CAR+1;i++)
			if (atoi(gp_spr_paint_getname(sprs[i],0)) == c)
				break;
		if (i == MAX_CAR+1)
		{
			printf("Couldn't find sprite for car %d\n",c);
			gp_spr_ar_delete(sprs,MAX_CAR+1);
			while (c--)
				(cars[c].spr->f->delete)(cars[c].spr);
			return 2;
		}
		else
			cars[c].spr = (gp_spr_01.fromspr)(&gp_spr_01,sprs[i],0,0,car_l,car_w,car_l/2,car_w/2);
		cars[c].ang = ang;
		cars[c].x = x << 16;
		cars[c].y = y << 16;
		cars[c].z = 0;
		if ((ang == 0) || (ang == 180 << 16))
		{
			if (c & 1)
			{
				y -= yd;
				x += xd;
			}
			else
				y += yd;
		} else if (c & 1) {
			x -= xd;
			y += yd;
		} else
			x += xd;
		cars[c].check_time = cars[c].lap_time = 0;
	}
	/* Shadow */
	for (i=0;i<MAX_CAR+1;i++)
		if (atoi(gp_spr_paint_getname(sprs[i],0)) == MAX_CAR)
			break;
	if (i == MAX_CAR+1)
	{
		printf("Couldn't find car shadow sprite\n");
		gp_spr_ar_delete(sprs,MAX_CAR+1);
		while (c--)
			(cars[c].spr->f->delete)(cars[c].spr);
		return 2;
	}
	car_shadow = (gp_spr_02.fromspr)(&gp_spr_02,sprs[i],0,0,car_l,car_w,car_l/2,car_w/2);
	/* Transfer the pixels to the alpha channel */
	for (x=0;x<car_l;x++)
		for (y=0;y<car_w;y++)
		{
			i = (car_shadow->f->getpix)(car_shadow,x,y,2) & 255;
			(car_shadow->f->setpix)(car_shadow,x,y,0,0);
			(car_shadow->f->setmask)(car_shadow,x,y,GP_MT_GREY,0,i);
		}
	printf("Done\n");
	gp_spr_ar_delete(sprs,MAX_CAR+1);
	return 0;
}

void shutdown_cars()
{
	int c;
	for (c=0;c<MAX_CAR;c++)
		if (cars[c].spr)
		{
			(cars[c].spr->f->delete)(cars[c].spr);
			cars[c].spr = 0;
		}
	if (car_shadow)
		(car_shadow->f->delete)(car_shadow);
}

void draw_cars(level *l,gp_screen *s,int x,int y)
{
	int c,cx,cy,b;
	f1616 g;
	x -= s->width/2; /* Get top-left corner of screen */
	y -= s->height/2;
	for (c=0;c<MAX_CAR;c++)
		if ((cars[c].player) && (cars[c].spr))
		{
			b = map_read(l,(cars[c].x >> 16)/l->sprw,(cars[c].y >> 16)/l->sprh);
			g = ground_height(l,b,cars[c].x % (l->sprw << 16),cars[c].y % (l->sprh << 16));
			cx = (cars[c].x >> 16) - x;
			cy = (cars[c].y >> 16) - y;
			if ((cars[c].z > g+CAR_HEIGHT) && (g >= 0)) /* Significantly above ground? */
				gp_spr_plot_rotscale(car_shadow,cx,cy,s,f1616_cos(cars[c].ang),-f1616_sin(cars[c].ang));
			if (cars[c].z > 0) {
				cx += cars[c].z >> 14;
				cy -= cars[c].z >> 14;
			}
			if ((cx + car_rad >= 0) && (cx - car_rad < s->width) && (cy + car_rad >= 0) && (cy - car_rad < s->height))
			{
				if (cars[c].z >= 0)
					gp_spr_plot_rotscale(cars[c].spr,cx,cy,s,f1616_cos(cars[c].ang),-f1616_sin(cars[c].ang)); /* Plot car centered at cx,cy */
				else
				{
					g = -cars[c].z/4;
					if (g < 65536)
						g = 65536;
					if (cars[c].z > -65536*32)
						gp_spr_plot_rotscale(cars[c].spr,cx,cy,s,f1616_mul(f1616_cos(cars[c].ang),g),f1616_mul(-f1616_sin(cars[c].ang),g));
				}
			}
		}
}

int sideofline(f1616 x1,f1616 y1,f1616 x2,f1616 y2,f1616 x,f1616 y)
{
	/* Treat the line as a plane: n1x+n2y+d=0
	   Where N is normal, d=-A.N, A is a point on the line
	   Dot product A.B = a1b1+a2b2
	   So with x=x, y=y, A=(x1,y1), N=(y2-y1,x1-x2) (Line rotated 90 deg)
	   Equation is (y2-y1)x+(x1-x2)y-(x1(y2-y1)+y1(x1-x2))=0
	   Then simplify:
	   xA+yB-x1A-y1B=0
	   (x-x1)A+(y-y1)B=0
	   (x-x1)(y2-y1)+(y-y1)(x1-x2)=0 */
	return f1616_mul(x-x1,y2-y1)+f1616_mul(y-y1,x1-x2); /* >0 if on left, =0 if on line, <0 if on right */
} 

#define ABS(x) ((x)<0?-(x):(x))

int car_collide(int a,int b)
{
	f1616 xd,yd;
	f1616 xa[4],ya[4];
	f1616 xb[4],yb[4];
	int l,i;
	if ((cars[a].player == 0) || (cars[b].player == 0))
		return 0;
	/* Check bounding boxes */
	if (ABS(cars[a].x-cars[b].x) > car_rad << 16)
		return 0;
	if (ABS(cars[a].y-cars[b].y) > car_rad << 16)
		return 0;
	if (ABS(cars[a].z-cars[b].z) > CAR_HEIGHT)
		return 0;
	/* Check whether each corner of each car lies inside the other car */
	xd = f1616_cos(cars[a].ang);
	yd = f1616_sin(cars[a].ang);
	/* 0,0 */
	xa[0] = cars[a].x-xd*(car_l/2)+yd*(car_w/2);
	ya[0] = cars[a].y-yd*(car_l/2)-xd*(car_w/2);
	/* w,0 */
	xa[1] = cars[a].x+xd*(car_l/2)+yd*(car_w/2);
	ya[1] = cars[a].y+yd*(car_l/2)-xd*(car_w/2);
	/* w,h */
	xa[2] = cars[a].x+xd*(car_l/2)-yd*(car_w/2);
	ya[2] = cars[a].y+yd*(car_l/2)+xd*(car_w/2);
	/* 0,h */
	xa[3] = cars[a].x-xd*(car_l/2)-yd*(car_w/2);
	ya[3] = cars[a].y-yd*(car_l/2)+xd*(car_w/2);
	xd = f1616_cos(cars[b].ang);
	yd = f1616_sin(cars[b].ang);
	/* 0,0 */
	xb[0] = cars[b].x-xd*(car_l/2)+yd*(car_w/2);
	yb[0] = cars[b].y-yd*(car_l/2)-xd*(car_w/2);
	/* w,0 */
	xb[1] = cars[b].x+xd*(car_l/2)+yd*(car_w/2);
	yb[1] = cars[b].y+yd*(car_l/2)-xd*(car_w/2);
	/* w,h */
	xb[2] = cars[b].x+xd*(car_l/2)-yd*(car_w/2);
	yb[2] = cars[b].y+yd*(car_l/2)+xd*(car_w/2);
	/* 0,h */
	xb[3] = cars[b].x-xd*(car_l/2)-yd*(car_w/2);
	yb[3] = cars[b].y-yd*(car_l/2)+xd*(car_w/2);
	/* Now check */
	for (i=0;i<4;i++)
	{
		for (l=0;l<4;l++)
			if (sideofline(xa[l],ya[l],xa[(l+1)&3],ya[(l+1)&3],xb[i],yb[i]) > 0)
				break;
		if (l == 4)
			return 1; /* One point lies inside all four lines */
	}
	for (i=0;i<4;i++)
	{
		for (l=0;l<4;l++)
			if (sideofline(xb[l],yb[l],xb[(l+1)&3],yb[(l+1)&3],xa[i],ya[i]) > 0)
				break;
		if (l == 4)
			return 1;
	}
	return 0;
}

#endif
