/*
 * snake.c
 *
 *  Created on: 18-12-2012
 *      Author: Mirosław Kardaś
 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdlib.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

#include "../MK_USART/mkuart.h"
#include "../LCD/lcd44780.h"
#include "../IR_UNI/ir_config.h"
#include "snake.h"

TSNAKE snake_tab[2];	// tablica dwuelementowa węży

uint8_t jedzenie[2];	// tablica ze współrzędnymi jedzenia

TCFG eem_cfg EEMEM;

TCFG ram_cfg;

const TCFG pgm_cfg PROGMEM = {
		-64,		// max score
		40,			// game speed
		1			// users cnt
};


// drfinicje wyglądu segmentów węży oraz pokarmu
uint8_t face1[] = {32,14,27,29,31,14,32,32};
uint8_t face2[] = {32,14,21,31,21,14,32,32};
uint8_t pokarm[] = {32,32,10,4,10,32,32,32};


uint8_t restart;
int8_t snake_cnt;

static void (*start_screen_callback)(uint8_t usr_cnt);
static void (*end_screen_callback)(uint8_t usr_cnt);

void register_start_screen_callback(    void (*callback)(uint8_t usr_cnt)     ) {
	start_screen_callback = callback;
}

void register_end_screen_callback( void (*callback)(uint8_t usr_cnt) ) {
	end_screen_callback = callback;
}


// inicjalizacja wszystkich węży
void snake_init( uint8_t cnt ) {

	snake_cnt = cnt;		// ustalamy ilo!ść węży w grze

	snakes(-1);				// inicjalizacja 1-go węża

	if( 2 == cnt ) snakes(-2);	// inicjalizacja 2-go węża

}

// inicjalizacja pierwszego węża
void init_snake1(void) {



	snakes(0)->speed = ram_cfg.speed;
	snakes(0)->byx[0] = (0<<8)+0;		// pozycja startowa Y=0, X=0
	snakes(0)->body_len=1;
	snakes(0)->direction = right;
	snakes(0)->draw_snake_fun = (void*)draw_snake;
	snakes(0)->keys[left] = CUR_LEFT;
	snakes(0)->keys[right] = CUR_RIGHT;
	snakes(0)->keys[up] = CUR_UP;
	snakes(0)->keys[down] = CUR_DOWN;
	snakes(0)->face = 0x80;
	snakes(0)->head=0;
	snakes(0)->punkty=0;

	jedzenie[0] = random() % 4;

	jedzenie[1] = random() % 16;

	lcd_defchar(0, face1);
	lcd_defchar(1, face2);
	lcd_defchar(2, pokarm);

	if( start_screen_callback ) start_screen_callback( snake_cnt );

}


// inicjalizacja drugiego węża
void init_snake2(void) {
	snakes(1)->byx[0] = (3<<8)+15;		// pozycja startowa Y=3, X=15
	snakes(1)->body_len=1;
	snakes(1)->direction = left;
	snakes(1)->draw_snake_fun = (void*)draw_snake;
	snakes(1)->keys[left] = CUR1_LEFT;
	snakes(1)->keys[right] = CUR1_RIGHT;
	snakes(1)->keys[up] = CUR1_UP;
	snakes(1)->keys[down] = CUR1_DOWN;
	snakes(1)->face = 0x81;
	snakes(1)->head=0;
	snakes(1)->punkty=0;
}



TSNAKE * snakes( int8_t n ) {

	if( n == -1 ) init_snake1();
	if( n == -2 && snake_cnt == 2 ) init_snake2();

	if( snake_cnt == 1 && n == 1 ) return 0;

	return &snake_tab[n];
}




uint8_t check_body(TSNAKE * tsn, int8_t y, int8_t x) {

	uint8_t i;
	uint8_t head = tsn->head;

	if( 1 == tsn->body_len ) return 0;

	for(i=0; i<tsn->body_len; i++) {
		if( x == get_x( tsn->byx[head] ) && y == get_y( tsn->byx[head] ) ) return 1;
		head = (head-1) & 63;
	};
	return 0;
}


uint8_t check_error(TSNAKE * tsn, int8_t y, int8_t x) {

	uint8_t y1=0, x1=0;

	if( y==jedzenie[0] && x==jedzenie[1] ) {

		while(1) {

			y1 = random() % 4;
			x1 = random() % 16;

			if( y1==jedzenie[0] && x1==jedzenie[1] ) continue;

			if( !check_body(tsn, y1, x1) ) break;
		}

		jedzenie[0] = y1;
		jedzenie[1] = x1;
		tsn->punkty++;
		return 2;
	}

	if(y>3 || y<0) return 1;
	if(x>15 || x<0) return 1;


	return check_body(tsn, y,x);
}




void draw_snake(TSNAKE * tsn) {

	uint8_t head = tsn->head, tail=0;

	int8_t x = get_x( tsn->byx[head] );
	int8_t y = get_y( tsn->byx[head] );

	uint8_t check_result;

	switch( tsn->direction ) {
		case left: x--; break;
		case right: x++; break;
		case up: y--; break;
		case down: y++; break;
	}

	check_result = check_error( tsn, y, x );

	if( check_result == 1 ) {

		restart=0;

		tsn->punkty--;

		if( tsn->punkty > ram_cfg.max_score ) {
			ram_cfg.max_score = tsn->punkty;
			copy_ram_to_eem();
		};

		// ---- wywołanie ekranu końcowego użytkownika -----
		if( end_screen_callback ) end_screen_callback( snake_cnt );

		uint8_t znak;
		while(1) {

			IR_EVENT();

			znak = uart_getc() & 0xdf;

			if( znak == 'C' )  restart = 1;

			if( restart ) {
				restart=0;
				lcd_cls();
				snakes(-1);
				if( snake_cnt == 2 ) snakes(-2);
				return;
			}
		}
	}


	head = (head + 1) & 63;

	tsn->head = head;

	tsn->byx[head] = (y<<8) | x;

	lcd_locate( get_y( tsn->byx[head] ), get_x(tsn->byx[head] ) );
	lcd_char(tsn->face);

	tail = (head - tsn->body_len) & 63;
	lcd_locate( get_y( tsn->byx[tail] ), get_x( tsn->byx[tail] ) );
	lcd_char(' ');


	if( check_result == 2 ) tsn->body_len++;


	lcd_locate( jedzenie[0], jedzenie[1] );
	lcd_char( 0x82 );


}



//----------------------- obsługa eeprom

void copy_eem_to_ram( void ) {
	eeprom_read_block( &ram_cfg, &eem_cfg, sizeof(ram_cfg) );
}

void copy_ram_to_eem( void ) {
	eeprom_write_block( &ram_cfg, &eem_cfg, sizeof(ram_cfg) );
}

void copy_pgm_to_ram( void ) {
	memcpy_P( &ram_cfg, &pgm_cfg, sizeof(ram_cfg) );
}

void load_defaults( void ) {
	copy_pgm_to_ram();
	copy_ram_to_eem();
}

void check_and_load_defaults( void ) {
	uint8_t i, len = sizeof( ram_cfg );
	uint8_t * ram_wsk = (uint8_t*)&ram_cfg;

	copy_eem_to_ram();
	for(i=0; i<len; i++) {
		if( 0xff == *ram_wsk++ ) continue;
		break;
	}

	if( i == len ) {
		load_defaults();
	}



}
