/*
 * menu_entry_3.c
 * begin 20200807 true
 *
 * snek
 */

#include "ui/menu.h"

#include "render/draw_ssd1306.h"

#include "led/rgbled.h"

#include "misc/accel.h"
#include "misc/tinymt.h"

#include "global.h"
#include "user_config.h"

#include <stdio.h>
#include <string.h>




#define SNEK_SPEED_BASE		33
#define SNEK_SPEED_MODIFIER	3

#define SNEK_SPEED_MAXSEL	11

#define SNEK_DIR_NORTH		0x01
#define SNEK_DIR_SOUTH		0x02
#define SNEK_DIR_WEST		0x04
#define SNEK_DIR_EAST		0x08

#define SNEK_FUDGE			0x80



struct Snek {
	uint8_t  direction;
	uint8_t  speed;
	uint8_t  timeout;
	uint8_t  rsvd;
	int8_t   head_x;
	int8_t   head_y;
	int8_t   tail_x;
	int8_t   tail_y;
	int8_t   apple_x;
	int8_t   apple_y;
};



static struct Snek snek;
static uint16_t    playfield[64];

static uint8_t gamemode = 0xff;

static uint8_t banner;
static int8_t  banner_x[4];
static int8_t  banner_y[4];



void snek_renderframe()
{
	uint8_t i;
	uint8_t w;
	int8_t a, b;
	int8_t x, y;

	// render apple
	snek.direction += 0x40;
	w = snek.direction >> 6;

	x = snek.apple_x << 1;
	y = snek.apple_y << 1;
	switch (w) {
		case 1: x++; break;
		case 2: x++; y++; break;
		case 3: y++; break;
	}

	oled.state &= ~SSD1306_STATE_PIXEL_MASK;
	oled.state |= SSD1306_STATE_INVERT_PIXEL;
	ssd1306fb_draw_pix(x, y);

	// render
	oled.state &= ~SSD1306_STATE_PIXEL_MASK;
	oled.state |= SSD1306_STATE_SET_PIXEL;

	for (x = 0; x < 64; x++) {
		a = x << 1;
		w = playfield[x] >> 8;
		y = playfield[x] & 0xff;

		for (i = 0; i < 8; i++) {
			if (w & (1 << i)) {
				b = (i + 0) << 1;
				ssd1306fb_draw_vline(a+0, b, b + 1);
				ssd1306fb_draw_vline(a+1, b, b + 1);
			}
			if (y & (1 << i)) {
				b = (i + 8) << 1;
				ssd1306fb_draw_vline(a+0, b, b + 1);
				ssd1306fb_draw_vline(a+1, b, b + 1);
			}
		}
	}

	// do we do next step?
	snek.timeout += SNEK_SPEED_MODIFIER * snek.speed;
	if (snek.timeout < SNEK_SPEED_BASE) {
		return;
	}
	snek.timeout -= SNEK_SPEED_BASE;

	// move head based on direction
	switch (snek.direction) {
		case SNEK_DIR_NORTH:	snek.head_y--; break;
		case SNEK_DIR_SOUTH:	snek.head_y++; break;
		case SNEK_DIR_WEST:		snek.head_x--; break;
		case SNEK_DIR_EAST:		snek.head_x++; break;
	}

	snek.head_x += 64;
	snek.head_x %= 64;

	snek.head_y += 16;
	snek.head_y %= 16;

	playfield[snek.head_x] |= (1 << snek.head_y);

	// determine solids near tail location
	w = 0;

	// derender old tail

	// set new tail
}

void snek_fixapple()
{
	// uint8_t y_pos, y_off;

	while (1) {
		// is a pixel lit at this location
		if (playfield[snek.apple_x] & (1 << snek.apple_y)) {
			// if so, move our apple
			snek.apple_x += 3;
			snek.apple_x %= 64;
			snek.apple_y++;
			snek.apple_y %= 16;
		} else {
			break;
		}
	}
}

void snek_newgame()
{
	uint16_t rnd;
	uint8_t i;

	// set up snek default coordinates
	rnd = prng_get16();
	snek.head_x = ((rnd & 0xff) % 48) + 8;
	snek.head_y = ((rnd >>   8) %  8) + 4;

	// what way is the head moving?
	rnd = prng_get16();
	snek.direction = 1 << (rnd & 0x03);

	// set our initial tail based on this direction
	snek.tail_x = snek.head_x;
	snek.tail_y = snek.head_y;
	switch (snek.direction & 0xf) {
		case SNEK_DIR_NORTH: snek.tail_y++; break;
		case SNEK_DIR_SOUTH: snek.tail_y--; break;
		case SNEK_DIR_WEST:  snek.tail_x++; break;
		case SNEK_DIR_EAST:  snek.tail_x--; break;
	}

	// position the apple
	snek.apple_x = ((rnd & 0xfc) % 48) + 8;
	snek.apple_y = ((rnd >>   8) %  8) + 4;
	snek_fixapple();

	// clear the screen
	for (i = 0; i < 64; i++) {
		playfield[i] = 0;
	}

	// render initial frame
	playfield[snek.head_x] |= (1 << snek.head_y);
	playfield[snek.tail_x] |= (1 << snek.tail_y);

	snek_renderframe();

	// start the game
	gamemode = 0;
}

void snek_banner_jiggle()
{
	int8_t x_off, y_off;
	uint16_t rnd;

	rnd = prng_get16();

	x_off = (rnd     ) & 0x03;
	y_off = (rnd >> 8) & 0x01;

	switch (banner) {
		case 0: {	// welcome
			banner_x[0] = 31;
			banner_y[0] = 0 + y_off;
			break;
		}
		case 1: {	// snek
			banner_x[1] = 29;
			banner_y[1] = 7 + y_off;
			break;
		}
		case 2: {	// to
			banner_x[2] = 78;
			banner_y[2] = 0 + y_off;
			break;
		}
		case 3: {	// speed
			banner_x[3] = -1;
			banner_y[3] = 11 + y_off;
			break;
		}
	}

	banner_x[banner] += x_off;
}

void snek_btn_prev(uint8_t idx)
{
	if (!gamemode) {

	} else {
		if (snek.speed > SNEK_SPEED_MAXSEL) {
			snek.speed = SNEK_SPEED_MAXSEL;
		}
		if (snek.speed > 1) {
			snek.speed--;
		}
	}
}

void snek_btn_next(uint8_t idx)
{
	if (!gamemode) {

	} else {
		if (snek.speed < SNEK_SPEED_MAXSEL) {
			snek.speed++;
		}
	}
}

void snek_btn_enter(uint8_t idx)
{
	if (gamemode != 0) {
		snek_newgame();
	}
}

void snek_btn_exit(uint8_t idx)
{
	if (!gamemode) {
		gamemode = 1;
	} else {
		gamemode = 0xff;

		menu_btn_use_std();
		menu_btn_exit(idx);
	}
}

void snek_btn_use()
{
	btn[0].cb_push = &snek_btn_prev;
	btn[0].cb_hold = &snek_btn_prev;
	btn[0].cb_release = 0;
	btn[0].hold = 300 / HOLD_PERIOD;
	btn[0].repeat = 80 / HOLD_PERIOD;

	btn[1].cb_push = &snek_btn_exit;
	btn[1].cb_hold = 0;
	btn[1].cb_release = 0;
	btn[1].hold = 0;
	btn[1].repeat = 0;

	btn[2].cb_push = &snek_btn_enter;
	btn[2].cb_hold = 0;
	btn[2].cb_release = 0;
	btn[2].hold = 0;
	btn[2].repeat = 0;

	btn[3].cb_push = &snek_btn_next;
	btn[3].cb_hold = &snek_btn_next;
	btn[3].cb_release = 0;
    btn[3].hold   = btn[0].hold;
    btn[3].repeat = btn[0].repeat;

    btn_commit_hold();
}

void snek_init()
{
	uint8_t i;

	snek_btn_use();

	for (i = 0; i < 5; i++) {
		banner = i;
		snek_banner_jiggle();
	}

	gamemode = 17;
}


__HIGH_CODE
void snek_disp(uint8_t idx)
{
	// uint8_t i;
	int8_t  w;
	char txt[12];

	if (gamemode == 0xff) {
		snek_init();
	}

	// banner jiggle
	if (!banner) {
		banner = 4;
	}
	banner--;

	snek_banner_jiggle();

	if (gamemode) {
		ssd1306_cls(&oled);

		// main menu
		strcpy(txt, "play");
		w = ssd1306fb_get_str_width(font_Dialog_plain_8, txt, strlen(txt), 0);
		ssd1306fb_set_cursor(116 - w, -2);
		ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);

		strcpy(txt, "exit");
		w = ssd1306fb_get_str_width(font_Dialog_plain_8, txt, strlen(txt), 0);
		ssd1306fb_set_cursor(116 - w, 24);
		ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);

		// speed
		ssd1306fb_set_cursor(-1,  6);
		ssd1306fb_draw_str(font_table[0].font, "spd", 1);
		ssd1306fb_set_cursor( 0, 15);
		txt[0] = 0x30 + (snek.speed / 10);
		txt[1] = 0x30 + (snek.speed % 10);
		txt[2] = 0x00;
		ssd1306fb_draw_str(font_table[1].font, txt, 1);

		// welcome
		ssd1306fb_set_cursor(banner_x[0], banner_y[0]);
		ssd1306fb_draw_str(font_table[0].font, "welcom", 1);

		// to
		if (gamemode <= 9) {
			ssd1306fb_set_cursor(banner_x[2], banner_y[2]);
			ssd1306fb_draw_str(font_table[0].font, "to", 1);
		}

		// snek
		if (gamemode == 1) {
			ssd1306fb_set_cursor(banner_x[1], banner_y[1]);
			ssd1306fb_draw_str(font_table[3].font, "snek", 1);
		}

		if (gamemode > 1) {
			gamemode--;
		}
	} else {
		// running the game
		snek_renderframe();
	}
}
