/*
Copyright (C) 2016-2019 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file LICENSE for details.
*/

#include "library/string.h"
#include "library/syscalls.h"
#include "library/stdio.h"
#include "library/nwindow.h"

#define SNAKE_MAX 128
#define THICK 5

struct coords { int x; int y; };

struct nwindow *nw = 0;

int randint(int min, int max)
{
	// Could be a lot better but at the moment it works
	uint32_t tm;
	syscall_system_time(&tm);
	int state = tm;
	if(state % 5 == 0) {
		state += 50000;
	} else if(state % 4 == 0) {
		state += 1000;
	} else if(state % 3 == 0) {
		state += 10000;
	} else if(state % 2 == 0) {
		state += 25000;
	} else {
		state += 16000;
	}
	int diff = max - min;

	return state % diff + min;
}

void init_snake_coords(struct coords *snake, int *length, int xsteps, int ysteps )
{
	*length = 3;

	snake[0].x = xsteps/2;
	snake[0].y = ysteps/2;

	int i;
	for(i=1;i<*length++;i++) {
		snake[i].x = snake[i-1].x-1;
		snake[i].y = snake[0].y;
	}

}

void scaled_rect( struct nwindow *nw, int x, int y, int scale )
{
	nw_rect(nw,x*scale,y*scale,scale,scale);
}

void update_snake(struct coords *snake, int *length, int x, int y, int grow )
{
	int i;

	if(!grow) {
		nw_fgcolor(nw,0,0,0);
		scaled_rect(nw,snake[*length-1].x,snake[*length-1].y,THICK);
	}

	*length += grow;

	for(i=*length-1;i>0;i--) {
		snake[i] = snake[i-1];
	}

	snake[0].x = x;
	snake[0].y = y;

	nw_fgcolor(nw,0,255,0);
	scaled_rect(nw,x,y,THICK);

}

int check_snake_collision( struct coords *snake, int length )
{
	int i;
	for(i=1;i<length;i++) {
		if(snake[i].x==snake[0].x && snake[i].y==snake[0].y) return 1;
	}
	return 0;
}

void init_apple( struct coords *apple, int xsteps, int ysteps )
{
	apple->x = randint(0,xsteps);
	apple->y = randint(0,ysteps);

	nw_fgcolor(nw,255, 0, 0);
	scaled_rect(nw,apple->x,apple->y,THICK);
}

int move_snake(struct coords *snake, int *length, struct coords *apple, int xsteps, int ysteps, char dir)
{
	int x, y;

	switch (dir) {
	case 'a':
		x = snake[0].x - 1;
		y = snake[0].y;
		break;
	case 'd':
		x = snake[0].x + 1;
		y = snake[0].y;
		break;
	case 'w':
		x = snake[0].x;
		y = snake[0].y - 1;
		break;
	case 's':
		x = snake[0].x;
		y = snake[0].y + 1;
		break;
	default:
		x = snake[0].x;
		y = snake[0].y;
		break;
	}

	if(x<=-1 || x>=xsteps || y<=-1 || y>=ysteps ) return -1;


	if(x==apple->x && y==apple->y) {
		update_snake(snake, length, x, y, 5);
		init_apple(apple,xsteps,ysteps);
	} else {
		update_snake(snake, length, x, y, 0);
	}

	if(check_snake_collision(snake,*length)) {
		return -1;
	}

	return 0;
}

int main(int argc, char *argv[])
{
	struct coords snake[SNAKE_MAX];
	struct coords apple;

	int snake_length = 3;
	int speed = 100;

	nw = nw_create_default();

	int width = nw_width(nw);
	int height = nw_height(nw);

	// Board dimensions in snake blocks
	int xsteps = width / THICK;
	int ysteps = height / THICK;

	nw_clear(nw,0, 0, width, height);
	nw_string(nw,THICK * 3, THICK * 4, "Press any key to start");
	nw_string(nw,THICK * 3, THICK * 8, "w: up");
	nw_string(nw,THICK * 3, THICK * 12, "s: down");
	nw_string(nw,THICK * 3, THICK * 16, "a: left");
	nw_string(nw,THICK * 3, THICK * 20, "d: right");
	nw_flush(nw);

	char c = nw_getchar(nw,1);


	while(1) {
		char dir = 'd';
		nw_clear(nw,0,0,width,height);
		init_snake_coords(snake, &snake_length, xsteps, ysteps);
		init_apple(&apple,xsteps, ysteps);

		while(1) {
			syscall_process_sleep(speed);
			c = nw_getchar(nw,0);

			if((c == 'a' && dir == 'd') || (c == 'd' && dir == 'a') || (c == 'w' && dir == 's') || (c == 's' && dir == 'w')) {
				// reject conflicting input
			} else if(c == 'w' || c == 'a' || c == 's' || c == 'd') {
				dir = c;
			} else if(c=='q') {
				break;
			}

			int status = move_snake(snake, &snake_length, &apple, xsteps, ysteps, dir);
			nw_flush(nw);
			if(status<0) break;
		}

		nw_flush(nw);
		nw_fgcolor(nw,255, 255, 255);
		nw_string(nw,THICK * 3, THICK * 4, "You lose!");
		nw_string(nw,THICK * 3, THICK * 8, "Enter q to quit");
		nw_string(nw,THICK * 3, THICK * 12, "Press any key to start");
		nw_flush(nw);

		c = nw_getchar(nw,1);
		if (c == 'q') return 0;
	}

}