/*    Zonic the Hog.  A silly Sonic-like game written for Speedhack 2007
 *    Copyright (c) 2007 Steven Wallace / Chedda Cheeze
 *    email: steven.t.wallace @ gmail. com
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
 
#include <allegro.h>
#include "Player.h"
#include "data.h"
#include "main.h"
#include "Joystick.h"
#include "Level.h"
#include "Cutscene.h"
#include <math.h>
#include "EndOfLevel.h"
#include "Game.h"
#include "GlCompat.h"


void Player::Restart() {
	x = y = dx = dy = 0;
	//apples = 0;
	face = 0;
	angle = 0;
	oldscore = score;
	memset(items, 0, sizeof(*items) * 4);
}

Player::Player() {
	Restart();
	score = 0;
	
}




void Player::Jump() {
	if(fabs(y - level->GetHeight((int)x)) < 5.0) {
		SAMPLE *samp = (SAMPLE*)data[S_JUMP].dat;
		dy = -750.0;
		stop_sample(samp);
		int volume = SQUEEZE(0, 255 - fabs(x - player.GetX()) / 25, 255);
		int pan;
		if(x==player.GetX()) pan = 127;
		else pan = SQUEEZE(0, 127 + (x-player.GetX()) / 25, 255);
		play_sample(samp, volume, pan, 1000, FALSE);
	}
}


// How many points each item is worth (ITEM_APPLE, etc)
int scorevalue[4] = { 100, 280, 40, 500 };

void Player::LoseItem(float dt) {
	// We want to lose 1 item every 1/600 of a second
	static float time = 0;
	static float time60 = 1/600.0;
	time += dt;
	
	
	while(time >= time60) {
		time -= time60;
		int i = rand()%4;
		int j = 0;
		while(items[i]==0) { 
			i++;	// increment index 
			j++;	// increment counter
			if(i >= TOTAL_ITEMS)	i=0; 
				
			if(j >= TOTAL_ITEMS) return;
		}
		
		items[i]--;
		score -= scorevalue[i] / 4;
		level->SpawnItem(i, (int)player.GetX(), (int)player.GetY());
		
		SAMPLE *samp = (SAMPLE*)data[S_LOSEITEM].dat;
		stop_sample(samp);
		play_sample(samp, 255, 127, 1000, FALSE);
	}
}


void Player::GainItem(int item) {
	score += scorevalue[item];
	items[item]++;
}



bool Player::UpdatePig(float dt) {
	// clear all flags
	memset(inputflag, 0, 4);
	
	
	// set appropriate flags
	if(key[KEY_LEFT] || joystick.Left())  inputflag[IF_LEFT] = TRUE;
	if(key[KEY_RIGHT]|| joystick.Right()) inputflag[IF_RIGHT] = TRUE;
	if(key[KEY_SPACE]|| joystick.B0())    inputflag[IF_JUMP] = TRUE;

		
	UpdateGeneric(dt);
	
	// The player and the AI pig have different max speeds now.
	if(dx > MAX_PLAYER_SPEED) dx = MAX_PLAYER_SPEED;
	else if(dx < -MAX_PLAYER_SPEED) dx = -MAX_PLAYER_SPEED;
	
	// end of level condition
	if(x > level->GetWidthInPixels()-BLOCK_W*4) {
		toProcess.clear();
		endoflevel.Reset();
		
		toProcess.push_back(&endoflevel);
		return true;
	}
	return false;
}


bool Player::UpdateAI(float dt) {
	// clear all flags
	memset(inputflag, 0, 4);
	
	// Get a little bit of distorted reality for the evil piggy chasing you
	dt *= 1.1;
	
	// Set appropriate flags
	if(player.GetX() > x /*&& dx < 450*/) inputflag[IF_RIGHT] = TRUE;
	else /*if(dx > -150)*/ inputflag[IF_LEFT] = TRUE;	
		
	// determine if we should jump or not
	if(fabs(dx) < 250 && (fabs(x-player.GetX() > 64) || y-player.GetY() > 64)) {
		if((dx > 0 && player.GetX() > x)
		|| (dx < 0 && player.GetX() < x))
			inputflag[IF_JUMP] = TRUE;
	}
	
	// If we're sufficiently far, give us a boost.  >:-)
	if(fabs(x-player.GetX()) > 2048) dt *= 2;
	UpdateGeneric(dt);
	
	if(dx > MAX_PLAYER_SPEED*1.25) dx = MAX_PLAYER_SPEED*1.25;
	else if(dx < -MAX_PLAYER_SPEED*1.25) dx = -MAX_PLAYER_SPEED*1.25;
	
	// Condition if we grab the pig
	if(fabs(x - player.GetX()) < 16 && fabs(y - player.GetY()) < 16 && game.GetTime() > 5) {
		if(player.CountItems() > 0) {
			//printf("Lose: %d\n", player.CountItems());
			player.LoseItem(dt);
		}
		else {
			toProcess.clear();
			toProcess.push_back(&cutscene);
			cutscene.GameOver();
			return true;
		}
	}
	
	// Keep him held off until we get a head start
	if(game.GetTime() < 3) {
		y = -500;		// Hold him in the sky
		x -= dx * dt;		// Undo any movements he makes
		dx = dy = 0;
	}
		
	return false;
}



bool Player::UpdateGeneric(float dt) {
	float pl_rev_accel = PLAYER_REVERSE_ACCEL * dt;
	float pl_accel = ((this==&player)?PLAYER_ACCEL * dt: PLAYER_ACCEL * dt * 1.5);
	float pl_slowdown = dx * 1.25 * dt;
	
	if(level->GetType((int)x) == 1) {
		pl_rev_accel *= 0.25;
		pl_accel *= 0.35;
		pl_slowdown = 0.0f;
	}
	if(inputflag[IF_LEFT]) {
		if(dx > 0.0) dx -= pl_rev_accel;
		else dx -= pl_accel;
		face = 1;
	}
	else if(inputflag[IF_RIGHT]) {
		if(dx < 0.0) dx += pl_rev_accel;
		else dx += pl_accel;
		face = 0;
	}
	// slow down
	else {
		dx -= pl_slowdown;
		if(fabs(dx) <= 1) dx = 0.0;
	}
	

	bool jump;
	if(inputflag[IF_JUMP]) {
		Jump();
		jump = true;
	}
	else jump = false;
		
	
	bool wasOnGroundLastFrame;
	if(level->GetHeight((int)x) - y < 4) wasOnGroundLastFrame = true;
	else wasOnGroundLastFrame = false;
	
	x += dx * dt;
	y += dy * dt;


	// Ensure that the player is always above ground
	if(y > level->GetHeight((int)x)) {
		float yy = y - level->GetHeight((int)x);
		if(dx > 0) dx -= yy * 2.25;
		else dx += yy * 2;
			
		//dy = -yy / dt;
		//if(dy < -1000) dy = -1000;
		y = level->GetHeight((int)x);
		if(dy > 0) dy *= -0.35f;
	}
	else if(wasOnGroundLastFrame && !jump) {
		int threshold;
		switch(level->GetType((int)x)) {
		case 0: // Grass
			threshold = 64;
			break;
		
		case 1: // Ice
			threshold = 4;
			break;
		
		case 2: // Hay
		case 3: // Wood
			threshold = 16;
			break;
		}
		
		if(level->GetHeight((int)x) - y < threshold) y = level->GetHeight((int)x);
	}


	
	// Gravity 
	dy += GRAVITY * dt;


	
	if(x < 3*BLOCK_W) x = 3*BLOCK_W;
	else if(x > level->GetWidthInPixels() - BLOCK_W) x = level->GetWidthInPixels() - BLOCK_W;


	// Update animation frame
	frame += dx * dt / 10;
	return false;
}



void Player::Draw(BITMAP *buffer, int cx, int cy) {
	// Determine which frame of animation we should play for the piggy
	static int anim1[8]  = { 0, 1, 2, 1, 0, 9, 10, 9 };
	static int anim2[12] = { 0, 1, 2, 3, 2, 1, 0, 9, 10, 11, 10, 9 };
	static int anim3[8] = { 0, 7, 8, 4, 5, 4, 8, 7 };
	static int *anim, mod;
	
	if(fabs(dx) < 10)
		{ anim = anim1; mod=1; }
	else if(fabs(dx) < 100) 
		{ anim = anim1;	mod=8; }
	else if(fabs(dx) < 400)
		{ anim = anim2; mod=12; }
	else
		{ anim = anim3; mod=8; }
		
	// determine angle of slope under piggy's feet
	if(fabs(y - level->GetHeight((int)x)) < 10) {
		int h1 = level->GetHeight((int)x-8);
		int h2 = level->GetHeight((int)x+8);
		angle = atan((h2-h1)/16.0);
	}
	else if(fabs(dx) < 400) {
		anim = &anim3[3]; mod=1;
	}
	
	int fr = anim[((int)frame)%mod];
	
	
	// Draw the player sprite
	static int pl_w = ((BITMAP*)data[pig000].dat)->w;
	static int pl_h = ((BITMAP*)data[pig000].dat)->h;
	if(face == 0)
		gl_pivot_sprite_ex(pig000 + fr, (int)x - cx, (int)y - cy, pl_w/2, pl_h, angle);
	else
		gl_pivot_sprite_h_flip_ex(pig000 + fr, (int)x - cx, (int)y - cy, pl_w/2, pl_h, angle);
		
	
	// And for debugging purposes, little circles showing important radii
	#ifdef _DEBUG
	//circle(buffer, (int)(x - cx), (int)(y - cy - 32), RADIUS_OF_INFLUENCE, 0);
	#endif
}



bool Player::IsOnScreen(int cx, int cy) {
	if(x > cx && x < cx+SCREEN_W && y > cy && y < cy+SCREEN_H) return true;
	return false;
}



