//==============================================================================
//
// Snake
// -----
// http://www.masterspace.biz/andreschnabel/
// 2006 by André Schnabel (thefrogs@web.de)
//
//==============================================================================

// snake.c

#include "snake.h"

// Used to go through the list
block_t *iterator;

// So we can check if the snake bites itself
boolean AlreadyBlockOnPos(int x, int y, snake_t *snake)
{
	// Set the iterator the head of the list
	iterator = snake->blocks;
	
	// Iterate as long we haven't reached the end
	while(iterator)
	{
		if(iterator->x == x && iterator->y == y)
			return true;
		
		iterator = iterator->prev;
	}
	
	return false;
}

// Adds a new block at the beginning of the list
void AddBlock(int x, int y, snake_t *snake)
{
	// Create a new block in the heap
	block_t *newBlock = malloc(sizeof(block_t));
	
	// Initialize it with values
	
	// Out of the screen?
	if(x < 0)
		x = SCR_W - BLOCK_W;
	else if(x + BLOCK_W > SCR_W)
		x = 0;
	
	if(y < 0)
		y = SCR_H - BLOCK_H;
	else if(y + BLOCK_H > SCR_H)
		y = 0;
	
	newBlock->x = x;
	newBlock->y = y;
	
	if(AppleOnPos(x, y, snake))
	{
		snake->grow = true;
#ifdef SNAKE_GETTING_FASTER
		snake->speed += 0.25;
#endif
	}
	
	// Connect it with the rest of the list
	newBlock->prev = snake->blocks;
	
	// This block is now the head of the list
	snake->blocks = newBlock;
}

// This is useful to update the snake
void RemoveLastBlock(snake_t *snake)
{
	block_t *temp;
	// Begin our search for the last element at the first
	iterator = snake->blocks;
	
	// Iterate through the list until we've found the last element
	while(iterator->prev != NULL)
	{
		temp = iterator;
		iterator = iterator->prev;
	}
	
	// The next element of the last should be the last element now
	temp->prev = NULL;
	
	// Remove the unlinked element now from the heap
	free(iterator);
}

// Deletes all blocks
void DeleteBlocks(snake_t *snake)
{
	block_t *temp;
	// Delete from the head to the tail
	iterator = snake->blocks;
	
	// Check whether we've reached the tail
	while(iterator->prev != NULL)
	{
		temp = iterator;
		// Go to the previous element...
		iterator = iterator->prev;
		// ...and delete it, this way we don't loose our connection
		// to the list
		free(temp);
	}
	
	// And don't forget to delete the tail too
	free(iterator);
	
	snake->blocks = NULL;
}

// Initialized the linked list of blocks with a given length
void InitSnake(snake_t *snake, int length, int nr)
{
	int x, y;
	int i;
	
	snake->nr = nr;
	
	switch(nr)
	{
		case 1:
			y = 1*16;
			x = 16;
			snake->dir = DIR_RIGHT;
			break;
		case 2:
			y = 3*16;
			x = 16;
			snake->dir = DIR_RIGHT;
			break;
		case 3:
			y = 5*16;
			x = 16;
			snake->dir = DIR_RIGHT;
			break;
		case 4:
			y = 7*16;
			x = 16;
			snake->dir = DIR_RIGHT;
			break;
	}
	
	snake->kLeft = snake->kRight = KSTATE_RELEASED;
	
	snake->speed = SNAKE_SPEED_START;
	
	snake->grow = false;
	snake->lMove = SDL_GetTicks();
	
	snake->score = 0;
	snake->loser = false;
	
	for(i=0; i<length; i++)
	{
		if(snake->dir == DIR_RIGHT)
			AddBlock(x + i * 16, y, snake);
		else
			AddBlock(x - i * 16, y, snake);
	}
}

void DrawBlock(int x, int y, boolean head, snake_t *snake)
{
	static SDL_Rect rect;
	
	rect.w = BLOCK_W;
	rect.h = BLOCK_H;
	
	rect.x = x;
	rect.y = y;
	
	if(!head)
		SDL_BlitSurface(blockImgs[snake->nr-1], NULL, screen, &rect);
	else
		SDL_BlitSurface(headImgs[snake->nr-1][snake->dir], NULL, screen, &rect);
}

void UpdateSnake(snake_t *snake)
{
	if(snake->loser)
		return;
	
	// Move the snake after a specified time again
	if(SDL_GetTicks() - snake->lMove > 1000 / (int)snake->speed)
	{
		boolean hit;
		int newXpos, newYpos;
		int xAdd = 0, yAdd = 0;
		int i;
		
		switch(snake->dir)
		{
			case DIR_LEFT:
				xAdd = -BLOCK_W;
				break;
			case DIR_RIGHT:
				xAdd = BLOCK_W;
				break;
			case DIR_UP:
				yAdd = -BLOCK_H;
				break;
			case DIR_DOWN:
				yAdd = BLOCK_H;
				break;
		}
		
		// Add head at new dir
		newXpos = snake->blocks->x + xAdd;
		newYpos = snake->blocks->y + yAdd;
		
		hit = AlreadyBlockOnPos(newXpos, newYpos, snake) || TileOnPos(newXpos, newYpos);		
		
		for(i=0; i<numPlayers; i++)
		{
			if(i + 1 == snake->nr || players[i].loser)
				continue;
			
			hit = AlreadyBlockOnPos(newXpos, newYpos, &players[i]) || hit;
		}
		
		if(hit)
		{
			char outStr[100];
			
			sprintf(outStr, "Player %d lost!", snake->nr);
			
			playersLeft--;
			
			if(playersLeft == 0)
				gameOver = true;
			
			snake->loser = true;
			
			AddMessage(outStr);
			
#ifndef NO_SOUND
			Mix_PlayChannel(-1, gameOverSnd, 0);
#endif
		}
		else		
			AddBlock(newXpos, newYpos, snake);
		
		// Remove tail as long we're not making it longer
		if(snake->grow)
			snake->grow = false;
		else
			RemoveLastBlock(snake);	
		
#ifndef NO_SOUND
		Mix_PlayChannel(-1, movSnd, 0);
#endif
		
		snake->lMove = SDL_GetTicks();
	}
}

void DrawSnake(snake_t *snake)
{
	// Draw snake
	
	if(snake->loser)
		return;
	
	iterator = snake->blocks;
	while(iterator)
	{
		DrawBlock(iterator->x, iterator->y, (iterator == snake->blocks), snake);
		iterator = iterator->prev;
	}
}
