#include <c64/joystick.h>
#include <c64/vic.h>
#include <c64/sprites.h>
#include <stdlib.h>
#include <string.h>

byte landersprites[] = {
#embed "../resources/landersprites.bin"	
};

// Screen and color ram address

#define Screen ((byte *)0x0400)
#define Color ((byte *)0xd800)

struct Lander
{
	float	px, py, vx, vy;
};

enum GameState
{
	GS_READY,		// Getting ready
	GS_PLAYING,		// Playing the game
	GS_LANDED,		// Landed on pad
	GS_COLLIDE		// Collided with something
};

// State of the game
struct Game
{
	GameState	state;	
	byte		count;
	Lander		lander;	// use an array for multiplayer

}	TheGame;	// Only one game, so global variable

// Put one  char on screen
inline void screen_put(byte x, byte y, char ch, char color)
{
	__assume(y < 25);

	Screen[40 * y + x] = ch;
	Color[40 * y + x] = color;
}

// Get one char from screen
inline char screen_get(byte x, byte y)
{
	__assume(y < 25);

	return Screen[40 * y + x];
}

void screen_init(void)
{
	// Fill screen with spaces
	memset(Screen, ' ', 1000);

	for(char i=0; i<100; i++)
		screen_put(rand() % 40, rand() % 25, '.', VCOL_WHITE);

	sbyte	height[41];
	for(char i=0; i<41; i+=8)
		height[i] = 4 + rand() % 16;

	for(char step = 8; step > 1; step /= 2)
	{
		for(char i=0; i<40; i+=step)
		{
			char	p = (height[i] + height[i + step]) >> 1;
			p += rand() % step - (step / 2);
			height[i + step / 2] = p;
		}
	}

	char xp = 2 + rand() % 33;
	char yp = height[xp];
	for(char i=1; i<4; i++)
		if (height[xp + i] < yp)
			yp = height[xp + i];

	for(char i=0; i<4; i++)
		height[xp + i] = yp;

	for(char x=0; x<40; x++)
	{
		char h = height[x];
		for(char y=0; y<h; y++)
			screen_put(x, 24 - y, 160, VCOL_YELLOW);
	}


	for(char i=0; i<4; i++)
	{
		screen_put(xp + i, 24 - yp, 128 + 86, VCOL_MED_GREY);
		screen_put(xp + i, 23 - yp, 100, VCOL_WHITE);
	}

}

void lander_init(Lander * lander)
{
	lander->px = 160;
	lander->py = 50;
	lander->vx = 0;
	lander->vy = 0;

	spr_set(0, true, (int)lander->px, (int)lander->py, 0x0380 / 64, VCOL_DARK_GREY, false, false, false);
	spr_set(1, true, (int)lander->px, (int)lander->py, 0x0340 / 64, VCOL_LT_GREY, true, false, false);
	spr_set(2, false, (int)lander->px, (int)lander->py + 20, 0x03c0 / 64, VCOL_WHITE, false, false, false);
}	

char ExhaustColor[] = {VCOL_YELLOW, VCOL_WHITE, VCOL_ORANGE, VCOL_LT_BLUE};

void lander_move(Lander * lander, sbyte jx, sbyte jy)
{
	lander->px += lander->vx;
	lander->py += lander->vy;
	lander->vx += jx * 0.02;
	lander->vy += jy * 0.1 + 0.01;
}

void lander_show(Lander * lander, sbyte jx, sbyte jy)
{
	vic.color_border++;

	int	ix = (int)lander->px, iy = (int)lander->py;

	spr_move(0, ix, iy);
	spr_move(1, ix, iy);
	if (jy < 0)
	{
		spr_move(2, ix, iy + 20);
		spr_color(2, ExhaustColor[rand() & 3]);
		spr_show(2, true);
	}
	else
		spr_show(2, false);

	vic.color_border--;
}

void lander_flash(Lander * lander, char c)
{
	spr_color(0, rand() & 1);
}

enum LanderCollision
{
	LCOL_FREE,
	LCOL_GROUND,
	LCOL_PAD
};

LanderCollision lander_check(Lander * lander)
{
	sbyte	ix = (sbyte)((lander->px - 24) * 0.125);
	sbyte	iy = (sbyte)((lander->py - 29) * 0.125);

	if (iy > 24)
		return LCOL_GROUND;
	if (iy < 0)
		return LCOL_FREE;

	LanderCollision	col = LCOL_FREE;
	for(char i=0; i<4; i++)
	{
		if (ix >= 0 && ix < 40)
		{
			char ch = screen_get(ix, iy);
			if (ch == 160)
				return LCOL_GROUND;
			else if (ch == 128 + 86)
				col = LCOL_PAD;
		}
		ix++;
	}

	return col;
}

void game_state(GameState state)
{
	// Set new state
	TheGame.state = state;

	switch(state)
	{
	case GS_READY:
		// Clear the screen
		lander_init(&TheGame.lander);
		screen_init();
		TheGame.count = 32;
		break;

	case GS_PLAYING:
		break;

	case GS_LANDED:
		TheGame.lander.py = (sbyte)((TheGame.lander.py - 29) * 0.125) * 8 + 28;
		lander_show(&TheGame.lander, 0, 0);
		TheGame.count = 16;
		break;

	case GS_COLLIDE:
		TheGame.count = 32;
		lander_show(&TheGame.lander, 0, 0);
		break;
	}
}

// Main game loop, invoked every vsync
void game_loop(void)
{
	switch (TheGame.state)
	{
	case GS_READY:
		// Countdown ready to start
		if (!--TheGame.count)
			game_state(GS_PLAYING);
		break;
	case GS_PLAYING:
	{
		// Check player input on every frame
		joy_poll(0);
		lander_move(&TheGame.lander, joyx[0], joyy[0]);
		LanderCollision col = lander_check(&TheGame.lander);
		if (col == LCOL_GROUND)
			game_state(GS_COLLIDE);
		else if (col == LCOL_PAD)
			game_state(GS_LANDED);
		else
			lander_show(&TheGame.lander, joyx[0], joyy[0]);
	}	break;
	case GS_LANDED:
		if (!--TheGame.count)
			game_state(GS_READY);
		break;
	case GS_COLLIDE:
		lander_flash(&TheGame.lander, TheGame.count);
		if (!--TheGame.count)
			game_state(GS_READY);
		break;
	}

}



int main(void)
{	
	// Screen color to black
	vic.color_border = VCOL_BLACK;
	vic.color_back = VCOL_BLACK;

	vic.spr_mcolor0 = VCOL_MED_GREY;
	vic.spr_mcolor1 = VCOL_YELLOW;

	memcpy((char *)0x0340, landersprites, 192);

	spr_init(Screen);

	// Start the game in ready state	
	game_state(GS_READY);

	// Forever
	for(;;)
	{
		// One game loop iteration
		game_loop();

		// Wait one vsync
		vic_waitFrame();
	}

	// Never reached
	return 0;
}
