#include "entity.h"

#include <cmath>
#include <set>
#include "SDL_opengl.h"

#include "random.h"
#include "util.h"
#include "container.h"
#include "game.h"
#include "config.h"
#include "audio.h"
#include "radio.h"

#include "models/all.h"

namespace PRJID {

static const int explosionduration=400;

Entity::Entity() {
	init();
}

void Entity::init() {
	container_=NULL;
	jumping=0;
	myticks=0;
	position.set_position(C3f(20,0,20));
	type=ET_NONE;
	remove=false;
	collision=false;
	collision_y=true;
	exploding=0;
	speed=1.0f;
	ttl=-1;

	m_forward=false;
	m_backward=false;
	m_left=false;
	m_right=false;
}

void Entity::set_type(e_entitytype t) {
	type=t;
	switch (t) {
		default: 
			std::cout << "invalid type " << t << std::endl;
			return;
		case ET_PLAYER: 
			break;
		case ET_PLANE1: 
			speed=Tweak::plane_1_speed_f(); 
			ttl=Tweak::plane_1_ttl_i();
			break;
		case ET_PLANE2: 
			speed=Tweak::plane_2_speed_f(); 
			ttl=Tweak::plane_2_ttl_i();
			break;
		case ET_PROJECTILE: 
			speed=Tweak::projectile_speed_f(); 
			ttl=Tweak::projectile_ttl_i();
			break;
		case ET_BOMB: 
			break;
	}
}
C3f Entity::get_position() const{
	return position.get_position();
}
void Entity::set_position(C3f p) {
	position.set_position(p);
}

void Entity::hit() {
	if (!isactive()) return;
	explode();
}

void Entity::explode() {
	exploding=explosionduration;
}


void Entity::draw() {
	if (remove) return;

	glPushMatrix();
	C3f pos=get_position();
	glTranslatef(pos.x,pos.y,pos.z);

	if (exploding>0) {
		int edist=explosionduration-exploding;
		
		glColor3f(1.0f,0.0f,0.0f);
		float size=0.2f+0.001f*edist;
		glScalef(size,size,size);
		glEnable(GL_COLOR_MATERIAL);
		models::sphere::dldraw();
		glDisable(GL_COLOR_MATERIAL);
		glPopMatrix();

		return;
	}

	C3f rot=position.get_rotation();
	if (rot.y==90) glRotatef(rot.y,0,1,0);

		float size;
		size=0.2f;
	switch (type) {
		default:break;
		case ET_PLANE1:
			models::plane1::dldraw();
			break;
		case ET_PLANE2:
			models::plane2::dldraw();
			break;
		case ET_PROJECTILE:
			glScalef(size*0.5,size*0.5,size*0.5);
			glColor3f(1,1,0);
			glEnable(GL_COLOR_MATERIAL);
			models::sphere::dldraw();
			glDisable(GL_COLOR_MATERIAL);
			break;
		case ET_BOMB:
			glScalef(size,size,size);
			models::bomb::dldraw();
			break;
	}

	
	glPopMatrix();
}

void Entity::drop_bomb() {
	Entity *n;
	n=clone();
	n->set_type(ET_BOMB);
}


void Entity::act(unsigned int ticks) {
	if (!isactive()) return;
	if (type==ET_PLAYER) return;
	if (type==ET_BOMB) return;
	if (type==ET_PLANE2||type==ET_PLANE1) {
		C3f pos=get_position();
		if (
			pos.z>-5&&
			pos.z<Tweak::blockscale_f()&&
			pos.x>-5&&
			pos.x<Tweak::blockscale_f()
		) {
			if (type==ET_PLANE1&&Random::sget(Tweak::plane_1_bombrandom_i())==0) {
				drop_bomb();
			} else 
			if (type==ET_PLANE2&&Random::sget(Tweak::plane_2_bombrandom_i())==0) {
				drop_bomb();
			}
		}
	}

	mark_move_forward();
}

bool Entity::trymove(C3f d) {
	if (fabs(d.x)+fabs(d.y)+fabs(d.z)<0.001) return true;

	C3f nextpos;
	nextpos=get_position()+d;

	if (!game_->isvalid(nextpos)) { 
		position.set_movement(C3f());
		return false; 
	}
	if (type==ET_PLAYER) {
		bool snap=false;
		if (nextpos.x<Tweak::player_min_pos_f()) snap=true;
		if (nextpos.z<Tweak::player_min_pos_f()) snap=true;
		if (nextpos.x>Tweak::player_max_pos_f()) snap=true;
		if (nextpos.z>Tweak::player_max_pos_f()) snap=true;

		if (snap) {
			Radio::instance()->trigger_once(RT_TOOFAR);
			Audio::play(AS_PROBLEM,AC_PROBLEM);
			position.set_movement(C3f());
			return false; 
		}
	}
	position.set_position(nextpos);
	position.set_movement(C3f());
	return true;
}

void Entity::tick(unsigned int ticks) {
	myticks+=ticks;

	if (ttl==0) { remove = true; return; }
	if (ttl>0) {
		if (ttl<=ticks) { ttl=0; remove=true; return; }
		ttl-=ticks;
	}

	if (exploding>0) {
		if (exploding<=ticks) { exploding=0; remove=true; return; }
		exploding-=ticks;
		return;
	}
	if (type==ET_PROJECTILE) {
		C3f p;
		p=position.get_position();
		float rad=1;
		if (container_->collision_with_plane(get_position(),rad)) {
			game_->incr_score();
			explode();
			Audio::play(AS_EXPLOSION_PLANE);
			return;
		}
	}

	float fticks=static_cast<float>(ticks);
	fticks*=0.10f;
	if (m_forward) { position.move_forward(fticks*speed); }
	if (m_backward) { position.move_backward(fticks*speed); }
	if (m_left) { 
		position.move_left(fticks*speed); 
	}
	if (m_right) { position.move_right(fticks*speed); }

	m_forward=false;
	m_backward=false;
	m_left=false;
	m_right=false;


	if (type==ET_PLAYER&&Tweak::debug_flymode_i()==0) {
		position.movement_clear(C3(0,1,0));
	}

	if (jumping>0) {
		if (jumping<=ticks) { jumping=0; } else { jumping-=ticks; }
		position.move_direction(C3f(-90,0,0),fticks*Tweak::phys_fallconst_f());
	} else if (type <ET_FLOATING) {
		float fallspeed=fticks;
		if (type==ET_BOMB) {
			fallspeed*=Tweak::phys_bombfallconst_f();
			position.move_direction(C3f(90,0,0),fallspeed);
		} else if (Tweak::debug_flymode_i()==0) {
			fallspeed*=Tweak::phys_fallconst_f();
			position.move_direction(C3f(90,0,0),fallspeed);
		}

	}
		
	C3f dir;

	dir=position.get_movement();	
	if (dir.sum()<0.01f) return;

	if (type==ET_PLANE1||type==ET_PLANE2) {
		position.set_position(get_position()+dir);
		position.set_movement(C3f());
		return;
	}






	collision=false;
	collision_y=false;


	if (trymove(dir)) { return; }
	C3f dp=get_position();
	collision_y=true;
	if (type==ET_BOMB) {

		C3f fp;
		C3 i;
		C3 p;

		game_->destroy(get_position());
		explode();
		dir=C3f();
		return;
	}

	if (type==ET_PROJECTILE) {
		Audio::play(AS_EXPLOSION_PLANE);
		explode();
		dir=C3f();
		return;
	}


	if (trymove(C3f(dir.x,0,dir.z))) { return; }
	if (type==ET_PLAYER) {
		if (trymove(C3f(dir.x,0,0))) { return; }
		if (trymove(C3f(0,0,dir.z))) { return; }
	}

	collision=true;
	
	dir=C3f();
}


bool Entity::isactive() {
	if (remove) return false;
	if (exploding>0) return false;
	return true;
}



void Entity::jump() {
	jumping=Tweak::player_jump_i();
}

void Entity::mark_move_right() { m_right=true; }
void Entity::mark_move_left() { m_left=true; }
void Entity::mark_move_forward() { m_forward=true; }
void Entity::mark_move_backward() { m_backward=true; }


void Entity::turn_right() { position.rotate(0,-0.1f); }
void Entity::turn_left() { position.rotate(0,0.1f); }
void Entity::turn_up() { position.rotate(0.1f,0); }
void Entity::turn_down() { position.rotate(-0.1f,0); }


void Entity::turn(int x,int y) {

	position.rotate(
		(static_cast<float>(x))/5.0f,
		((static_cast<float>(y))/5.0f)*Config::mouse_reverse()
	);	
}

Entity *Entity::clone() {
	Entity *n;
	n=container_->create(type);
	n->position=position;
	return n;
}


} // namespace
