#include "snake.h"

player::player()
{
    name = 0;
    alive = 1;
    score = 0;
    chainsaw = 0;
    burrow = 0;
    chainsaw_on = 0;
    burrow_on = 0;
    key_left = KEY_LEFT;
    key_right = KEY_RIGHT;
    key_action = KEY_SPACE;
    uses_mouse = 0;
    color_offset = 100;
    
    left_pressed = 0;
    right_pressed = 0;
    action_pressed = 0;

    map_x = 0;
    map_y = 0;

    scroll_margin = 100;
    view_port_width = 0;
    view_port_height = 0;
    
    view_bitmap = 0;
    redraw = 0;
    clear = 0;

    voice = -1;

    print_score = -1;
    print_bonus = -1;
    print_type = -1;
}

char const *player::get_name() const
{
    return name;
}

void player::set_name(char const *new_name)
{
    name = strdup(new_name);
}

void player::crash()
{
    alive = 0;

    // clear the bonus bar if it's still there
    rectfill(score_bitmap, 100, 0, view_port_width - 1, 9, black);
    textout(score_bitmap, font, "GAME OVER!!!!", 100, 1,
	    rgbpal[color_offset + 10]);
}

void player::clear_keys()
{
    left_pressed = 0;
    right_pressed = 0;
    action_pressed = 0;
}

void player::read_keys()
{
    if (!uses_mouse)
    {
	if (key[key_left] && !key[key_right])
	{
	    left_pressed = 1;
	}
	
	if (key[key_right] && !key[key_left])
	{
	    right_pressed = 1;
	}
	
	if (key[key_action])
	{
	    action_pressed = 1;
	}
    }
    else
    {
	if ((mouse_b & 1) && !(mouse_b & 2))
	{
	    left_pressed = 1;
	}
	if ((mouse_b & 2) && !(mouse_b & 1))
	{
	    right_pressed = 1;
	}
	if (mouse_b & 4)
	{
	    action_pressed = 1;
	}
    }
}

void player::add_dirty_clear(m_rect map_dirty)
{
    // convert coordinates of map_dirty to viewport coordinates
    dirty *tmp;
    tmp = new_dirty(map_dirty.top - map_y,     // top
		    map_dirty.bot - map_y,     // bottom
		    map_dirty.left - map_x,    // left
		    map_dirty.right - map_x);  // right

    // now add the dirty rectangle to the list
    push(&clear, tmp);
}

void player::add_dirty_clear(dirty *to_add)
{
    push(&clear, to_add);
}

void player::add_dirty_redraw(m_rect map_dirty)
{
    // convert coordinates of map_dirty to viewport coordinates
    dirty *tmp;
    tmp = new_dirty(map_dirty.top - map_y,     // top
		    map_dirty.bot - map_y,     // bottom
		    map_dirty.left - map_x,    // left
		    map_dirty.right - map_x);  // right

    // now add the dirty rectangle to the list
    push(&redraw, tmp);
}

void player::add_dirty_redraw(dirty *to_add)
{
    push(&redraw, to_add);
}

void player::dirty_border_clear()
{
    // add the border of the map to the dirty list
    m_rect r;
    
    // top line
    r.top = 0;
    r.bot = 0;
    r.left = 0;
    r.right = MAP_SIZE_X - 1;
    add_dirty_clear(r);
    
    // bottom line
    r.top = MAP_SIZE_Y - 1;
    r.bot = MAP_SIZE_Y - 1;
    add_dirty_clear(r);
    
    // left line
    r.top = 0;
    r.right = 0;
    add_dirty_clear(r);

    // right line
    r.left = MAP_SIZE_X - 1;
    r.right = MAP_SIZE_X - 1;
    add_dirty_clear(r);
}

void player::dirty_border_redraw()
{
    // add the border of the map to the dirty list
    m_rect r;
    
    // top line
    r.top = 0;
    r.bot = 0;
    r.left = 0;
    r.right = MAP_SIZE_X - 1;
    add_dirty_redraw(r);
    
    // bottom line
    r.top = MAP_SIZE_Y - 1;
    r.bot = MAP_SIZE_Y - 1;
    add_dirty_redraw(r);
    
    // left line
    r.top = 0;
    r.right = 0;
    add_dirty_redraw(r);

    // right line
    r.left = MAP_SIZE_X - 1;
    r.right = MAP_SIZE_X - 1;
    add_dirty_redraw(r);
}

void player::dirty_bonusses_clear()
{
    bonus *tmp = bonusses;
    while (tmp)
    {
	add_dirty_clear(tmp->get_rect());
	tmp = tmp->next;
    }
}

void player::dirty_bonusses_redraw()
{
    bonus *tmp = bonusses;
    while (tmp)
    {
	add_dirty_redraw(tmp->get_rect());
	tmp = tmp->next;
    }
}

void player::dirty_tickmarks_clear()
{
    for (int x = tick_spacing; x < MAP_SIZE_X; x += tick_spacing)
    {
	for (int y = tick_spacing; y < MAP_SIZE_Y; y += tick_spacing)
	{
	    add_dirty_clear(m_rect(y, y, x, x));
	}
    }
}

void player::dirty_tickmarks_redraw()
{
    for (int x = tick_spacing; x < MAP_SIZE_X; x += tick_spacing)
    {
	for (int y = tick_spacing; y < MAP_SIZE_Y; y += tick_spacing)
	{
	    add_dirty_redraw(m_rect(y, y, x, x));
	}
    }
}

// draw all dirty rectangles
// in a separate loop, on purpose, so all drawing to the screen
// happens at the same time

void player::update_list(dirty **list)
{
    dirty *tmp;

    tmp = *list;
    
    while (tmp)
    {
	int left, right, top, bottom;
	
	// if part of the blit falls outside of the map,
	// make it black first
	// this is not possible anymore : viewport stops at the edge of the map


	left = map_x + tmp->left;
	right = map_x + tmp->right;
	top = map_y + tmp->top;
	bottom = map_y + tmp->bot;

	/*
	if (top < 0 || bottom >= MAP_SIZE_Y
	    ||
	    left < 0 || right >= MAP_SIZE_X)
	{
	    rectfill (view_bitmap, tmp->left, tmp->top,
		      tmp->right, tmp->bot, black);
	}
	*/
	
	// blit the dirty rectangle
	// yes, the computation is necessary because the viewport has
	// moved since the dirty rectangles were stored, so the original
	// map coordinates are not the same as the ones we want here.

	blit(scratch, view_bitmap,
	     left,
	     top,
	     tmp->left,
	     tmp->top,
	     tmp->right - tmp->left + 1,
	     tmp->bot - tmp->top + 1);
	
	// take the next rectangle
	tmp = tmp->next;
    }

    // toss the dirty list into the recycle bin
    push_list(&recycled, pop_list(list));
}

void player::move_viewport(body *b)
{
    int view_x = 0;        // viewport coordinates of the head
    int view_y = 0;
    
    // move the viewport
    // only move it if the snake is getting too close to the edge
    // do not move the viewport over the edge of the map

    view_x = b->get(b->head).x - map_x;
    view_y = b->get(b->head).y - map_y;

    if (view_x < scroll_margin)
    {
	map_x -= (scroll_margin - view_x);
    }
    if (view_x > view_port_width - scroll_margin - 1)
    {
	map_x += view_x - view_port_width + scroll_margin + 1;
    }
    if (view_y < scroll_margin)
    {
	map_y -= (scroll_margin - view_y);
    }
    if (view_y > view_port_height - scroll_margin - 1)
    {
	map_y += view_y - view_port_height + scroll_margin + 1;
    }

    // now restrict the viewport so it doesn't pass the map edges
    if (map_x < 0)
	map_x = 0;
    if (map_y < 0)
	map_y = 0;
    if (map_x > MAP_SIZE_X - view_port_width)
	map_x = MAP_SIZE_X - view_port_width;
    if (map_y > MAP_SIZE_Y - view_port_height)
	map_y = MAP_SIZE_Y - view_port_height;
}

void player::set_viewport(int top, int bot, int left, int right)
{
    // set the viewport
    view_port.top = top;
    view_port.bot = bot;
    view_port.left = left;
    view_port.right = right;
    
    view_port_height = view_port.bot - view_port.top + 1;
    view_port_width = view_port.right - view_port.left + 1;

    // create a sub bitmap to draw on
    view_bitmap = create_sub_bitmap(screen,
				    view_port.left,
				    view_port.top,
				    view_port_width,
				    view_port_height);

    clear_to_color(view_bitmap, black);

    // create the score_bitmap
    score_bitmap = create_bitmap(view_port_width, 10);
    clear_to_color(score_bitmap, black);
}

void player::init_map(int x, int y)
{
    // x and y are the map coordinates of the head of the snake
    map_x = x - (view_port_width / 2);
    map_y = y - (view_port_height / 2);
}


void player::play(SAMPLE *s)
{
    if (voice != -1)
    {
	// stop the running sample
	voice_stop(voice);
	deallocate_voice(voice);
	voice = -1;
    }
    
    voice = allocate_voice(s);
    if (voice == -1)
	return;         // no free voice

    voice_start(voice);
    release_voice(voice);
    voice = -1;
}

void player::loop(SAMPLE *s)
{
    if (voice != -1)
    {
	// stop the running sample
	voice_stop(voice);
	deallocate_voice(voice);
	voice = -1;
    }
    
    voice = allocate_voice(s);
    if (voice == -1)
	return;         // no free voice

    // set the voice to loop
    voice_set_playmode(voice, PLAYMODE_LOOP);
    
    voice_start(voice);

    // do not release the voice
}

void player::stop()
{
    if (voice != -1)
    {
	voice_stop(voice);
	deallocate_voice(voice);

	voice = -1;
    }
}

void player::draw_score()
{
    int go = 0;
    char buf[15];

    if (!alive) 
	return;      // alive snakes have GAME_OVER in the bar
    
    if (score != print_score)
	go = 1;

    if (print_type == bonus::chainsaw && chainsaw == 0)
	go = 1;
    if (print_type == bonus::burrow && burrow == 0)
	go = 1;
    if (print_type == -1 && (burrow > 0 || chainsaw > 0))
	go = 1;

    if (go)
    {
	sprintf(buf, "SCORE: %d", score);
	clear_to_color(score_bitmap, black);
	textout(score_bitmap, font, buf, 1, 1,
		rgbpal[color_offset + 10]);

	if (burrow > 0)
	{
	    rectfill(score_bitmap, 100, 2, 100 + 5 * burrow, 7, green);
	}
	if (chainsaw > 0)
	{
	    rectfill(score_bitmap, 100, 2, 100 + 5 * chainsaw, 7, red);
	}
    }
}
