#include "cat_api.h"
#include "shared.h"
#include "rendering.c"
#include <time.h>

/* TODO:
	- *forces* as linked list struct? entity can have a list of forces acting on it
		if force is 0N cleanup remove from list every tick.
		or every few ticks do a cleanup routine that also frees memory and stuff
*/

void bye(void)
{
	printf("bye!\n");
	exit(0);
}

struct keydowns
{
	char k;
	struct keydowns* next;
};

struct keydowns* first_keydown = NULL;

void add_keydown(char k)
{
	struct keydowns* new = malloc(sizeof(struct keydowns));
	new->k = k;
	new->next = NULL;
	if (first_keydown == NULL) first_keydown = new;
	else
	{
		struct keydowns* head;
		for (head = first_keydown; head->next != NULL; head = head->next)
			if (head->k == k) return;
		head->next = new;
	}
}

void lit_remove_keydown(struct keydowns* entry)
{
	if (first_keydown == entry) first_keydown = entry->next;
	else
	{
		struct keydowns* head;
		for (head = first_keydown; head->next != entry; head = head->next) ;
		head->next = entry->next;
	}
}

void remove_keydown(char k)
{
	struct keydowns* head;
	for (head = first_keydown; head != NULL; head = head->next)
		if (head->k == k) lit_remove_keydown(head);
}

int is_held(char k)
{
	struct keydowns* head;
	for (head = first_keydown; head != NULL; head = head->next)
		if (head->k == k) return 1;
	return 0;
}


struct entity* first_entity = NULL;

void add_entity(int is_player, int is_static, int x, int y, int width, int height, unsigned char r,
		unsigned char g, unsigned char b, float vx, float vy, float ax, float ay)
{
	struct entity* new = malloc(sizeof(struct entity));
	new->is_player = is_player;
	new->is_static = is_static;
	new->x = x;
	new->y = y;
	new->width = width;
	new->height = height;
	new->r = r;
	new->g = g;
	new->b = b;
	new->vx = vx;
	new->vy = vy;
	new->ax = ax;
	new->ay = ay;
	new->next = NULL;
	if (first_entity == NULL) first_entity = new;
	else
	{
		struct entity* head;
		for (head = first_entity; head->next != NULL; head = head->next) ;
		head->next = new;
	}
}

struct entity* get_player(void)
{
	struct entity* head;
	for (head = first_entity; head != NULL; head = head->next)
		if (head->is_player) return head;
	return NULL;
}

void key_down(char k)
{
	add_keydown(k);
	printf("%c pressed!\n", k);
	switch (k)
	{
		case 'a': get_player()->ax = -2; break;
		case 'd': get_player()->ax = 2; break;
	}
}

void key_up(char k)
{
	remove_keydown(k);
	printf("%c un-pressed\n", k);
	switch (k)
	{
		case 'a': get_player()->ax = 0; break;
		case 'd': get_player()->ax = 0; break;
	}
}

static int mousex = 0;
static int mousey = 0;

void mouse_move(int x, int y)
{
	/* clear old mouse icn */
	mousex = x;
	mousey = y;
	printf("mouse moved! (%d, %d)\n", x, y);
}

void mouse_down(unsigned char button)
{
	printf("mouse button %02x clicked!\n", button);
	/* TODO struct for this too?? that seems like a lot. what if we *don't* use mouse for anything... */
}

void mouse_up(unsigned char button)
{
	printf("mouse button %02x un-clicked!\n", button);
}

void process_input(void)
{
	/* mode switch game or menus */
	struct cat_event* e = cat_get_event();
	if (e == NULL) return;
	if (e->type == QUIT) bye();
	else if (e->type == KEYDOWN)	key_down(e->key);
	else if (e->type == KEYUP)	key_up(e->key);
	else if (e->type == MOUSEMOVE)	mouse_move(e->mousex, e->mousey);
	else if (e->type == MOUSEDOWN)	mouse_down(e->mouse_button);
	else if (e->type == MOUSEUP)	mouse_up(e->mouse_button);
}

void erase_entity(struct entity* e)
{
	/* get sprite info, etc */
	/* FIXME */
	if (e->x < 0 || e->y < 0 || e->x >= screen_width || e->y >= screen_height) return;
	cat_wireframe(1, 0, e->x, e->y, e->width, e->height, 0, 0, 0);
}

void draw_entity(struct entity* e)
{
	/* get sprite info, etc */
	/* FIXME */
	if (e->x < 0 || e->y < 0 || e->x >= screen_width || e->y >= screen_height) return;
	cat_wireframe(1, 1, e->x, e->y, e->width, e->height, e->r, e->g, e->b);
	render_entity(e);
	/* if out of bounds do not draw */
}

void erase_entities(void)
{
	struct entity* head;
	for (head = first_entity; head != NULL; head = head->next)
		erase_entity(head);
}

void draw_entities(void)
{
	struct entity* head;
	for (head = first_entity; head != NULL; head = head->next)
		draw_entity(head);
}

static long last_sec = 0;
static long last_nsec = 0;

#define NS_PER_TICK 30000000

void apply_friction(void) /* FIXME */
{
	struct entity* p = get_player();
	if (p->vx > 0) p->vx -= 1;
	else if (p->vx < 0) p->vx += 1;
	if (p->vy > 0) p->vy -= 1;
	else if (p->vy < 0) p->vy += 1;
}

/* FIXME */ static int tick_count = 0;

void tick(void)
{
	/* run every N milliseconds... nanoseconds? idk */
	struct timespec t;
	clock_gettime(CLOCK_REALTIME, &t);
	if (last_sec == 0) goto yes;
	else if ((t.tv_sec - last_sec) > 0) goto yes;
	else if ((t.tv_nsec - last_nsec) > NS_PER_TICK) goto yes;
	else return;
	/* do a tick */
	yes:	last_sec = t.tv_sec;
		last_nsec = t.tv_nsec;
	printf("tick %d!\n", tick_count++);

	/* code to run every tick goes here */
	/* something like "for i in fns_to_run_per_tick (linked list) run ->fn()
	   and somewhere there's an add_fn_to_run_every_tick() thing... or every n ticks... idk */
	erase_entities();
	struct entity* p = get_player();
	if (is_held(' ')) p->ay = -5;
	else p->ay = 9.81;
	p->vx += p->ax;
	p->vy += p->ay;
	/* FIXME */
	if (p->y >= screen_height - 100 && p->vy > 0) p->vy = 0;
	/* end fixme */
	apply_friction(); /* FIXME */
	p->x += (int)p->vx;
	p->y += (int)p->vy;
	draw_entities();
}

void game_loop(void)
{
	cat_render();
	process_input();
	/* mode check for in game or in menu */
	tick();
}

int main(int argc, char** argv)
{
	cat_init();
	cat_create_window(1920, 1080);
	//cat_play_wav("etc/example.wav");
	//cat_play_wav("etc/cat_quick.wav");
	//cat_play_wav("etc/cat_beep_box.wav");
	cat_play_wav("etc/cat_beep_slower.wav");
		/* layer, visibility, x, y, width, height, r, g, b */
	cat_fill(0, 1, 0, 0, screen_width - 1, screen_height - 1, 132, 155, 132);
	/* create the player */ add_entity(1, 0, 0, 0, 50, 50, 100, 50, 0, 0, 0, 0, 0);
	/* create a random entity */ add_entity(0, 0, 20, 20, 50, 50, 100, 100, 50, 0, 0, 0, 0);
	while (1) game_loop();
	return 0;
}
