#include <sstream>
#include "SDL_opengl.h"

#include "config.h"
#include "game.h"
#include "util.h"
#include "entity.h"
#include "random.h"
#include "font_ogl.h"
#include "help.h"
#include "skybox.h"
#include "audio.h"
#include "formation.h"
#include "light.h"
#include "floor.h"
#include "radio.h"

#include "tweak/tweak.h"
#include "models/all.h"

namespace PRJID {


typedef enum e_ingamemenuids {
	IG_POP,
   IG_NONE,
   IG_BACK,
   IG_QUIT
};

Game::~Game() {
}

Game::Game(Game *g,E_Difficulty diffi) {


	container_.set_game(this);
	player_=container_.create(ET_PLAYER);

	difficulty.set_current(diffi);
	level=1;
	score.set_current(0);
	if (g) {
		level=g->get_level()+1;
		score.set_current(g->get_score());
		difficulty.set_current(g->get_difficulty());
	}

	regen_interval=Interval(Tweak::balance_regen_interval_i());


	score=Score();
	score.dim(
      C3(50,10,0),10
   );
	score.label("Score:",C3(10,10));

	destruction=Energy(Tweak::balance_max_destruction_i());
	destruction.dim(
		Tweak::ui_destruction_color(),
      C3f(1,5,0),
      C3f(10,30,0)
   );
	weapon=Energy(
		Tweak::balance_weapon_energy_i()
	);
	weapon.dim(
		Tweak::ui_weapon_color(),
      C3f(1,5,0),
      C3f(10,50,0)
   );
	jumpjets=Energy(
		Tweak::balance_jumpjets_energy_i()
	);
	jumpjets.dim(
		Tweak::ui_jumpjets_color(),
      C3f(1,5,0),
      C3f(10,70,0)
   );

	map2.init("glysen.ell");

	player_->set_position(C3f(40,3,40));

	ticks_=0;
	menu_active=false;

	menu=Menu(2,C3(250,100));

   menu.add(Menuitem("Resume game",IG_BACK));
   menu.add(Menuitem("End game",IG_QUIT));

	state=GS_PLAYING;

	nextwave=Timeleft(
		difficulty.linear_lower_with_diffi_i(
			Tweak::balance_wave_interval_min_i(),
			Tweak::balance_wave_interval_max_i()
		)
	);

	cap();

	Radio::instance()->trigger(RT_TEST);

	dominating_direction=Random::sget(2);
}

bool Game::isactive() {
	if (Tweak::playmode_i()==0) return true;
	if (ticks_>Tweak::balance_win_time_i()) {
		return false;
	}
	if (destruction.empty()) {
		return false;
	}
	return true;
}

int Game::action(E_Action id,Entity *who,int x,int y) {
	if (!isactive()&&id!=A_TURN) { return 0; }
	if (state!=GS_PLAYING&&id!=A_TURN) return 0;
	if (who==NULL) who=player_;
	Entity *n=NULL;
	C3f pos,rot;
	e_entitytype t=ET_PLANE1;
	switch (id) {
		case A_FORWARD: player_->mark_move_forward(); break;
		case A_BACKWARD: player_->mark_move_backward(); break;
		case A_LEFT: player_->mark_move_left(); break;
		case A_RIGHT: player_->mark_move_right(); break;


		case A_TURN: player_->turn(x,y); break;
		case A_JUMP:
			if (jumpjets.empty())  {
				Audio::play(AS_PROBLEM,AC_PROBLEM);
				break;
			}
			jumpjets.reduce(1);
			player_->jump();
			break;
		case A_FIRE: {
			if (weapon.empty())  {
				Audio::play(AS_PROBLEM,AC_PROBLEM);
				break;
			}
			weapon.reduce(1);
			Audio::play(AS_LASER,AC_LASER);
			n=player_->clone();
			n->set_type(ET_PROJECTILE);
			n->mark_move_forward();
			pos=n->position.get_position();
			pos.y+=Tweak::projectile_starty_f();
			n->position.set_position(pos);
			n->position.set_movement(C3f());
			n->position.move_forward(Tweak::projectile_forward_f());
			pos=n->position.nextpos();
			n->position.set_position(pos);
			} break;
		case A_ADDENEMY: {
			if (Tweak::debug_addenemies_i()==0) break;
			if (Random::sboolean()) t=ET_PLANE2;
			n=container_.create(t);
			int min=0;
			int max=static_cast<int>(Tweak::blockscale_f());
			int dist=Tweak::formation_spawn_i();
			pos.x=static_cast<float>(Random::sget(min,max));
			pos.z=-dist;
			rot.y=Tweak::debug_planerot_f();
			pos.y=Tweak::plane_y_base_f()+
				Random::sgetf()*Tweak::plane_y_random_f();
			n->position.set_position(pos);
			n->position.set_rotation(rot);
			Formation::apply(n);
			} break;
		case A_ADDENEMY2: {
			if (Tweak::debug_addenemies_i()==0) break;
			if (Random::sboolean()) t=ET_PLANE2;
			n=container_.create(t);
			int min=0;
			int max=static_cast<int>(Tweak::blockscale_f());
			int dist=Tweak::formation_spawn_i();
			pos.z=static_cast<float>(Random::sget(min,max));
			pos.x=-dist;
			rot.y=Tweak::debug_planerot_f()-90;
			pos.y=Tweak::plane_y_base_f()+
				Random::sgetf()*Tweak::plane_y_random_f();
			n->position.set_position(pos);
			n->position.set_rotation(rot);
			Formation::apply2(n);
			} break;
		default:
			break;
	}
	return 0;
}
void Game::destroy(C3f p) {
	unsigned int r;
	r=map2.destroy(
		p,
		Tweak::bomb_radius_f()+
		Random::sgetf()*Tweak::bomb_radius_random_f()
	);
	Audio::play(AS_EXPLOSION_BUILDING);

	destruction.reduce(r);
	if (destruction.percent()<4) {
		Radio::instance()->trigger_once(RT_NEARLYDEAD);
	}
	if (destruction.empty()) {
		Audio::schedule_play(2000,AS_FAILED,AC_TITLE);
		Radio::instance()->trigger_once(RT_DEAD);
	}
}
void Game::cap() {
}

void Game::tick(unsigned int ticks) {
	if (menu_active) return;
	ticks_+=ticks;
	if (state!=GS_PLAYING) return;
	if (!isactive()) return;

	Radio::instance()->tick(ticks);
	container_.act(ticks);
	container_.tick(ticks);
	destruction.tick(ticks);
	weapon.tick(ticks);
	jumpjets.tick(ticks);
	nextwave.tick(ticks);
	unsigned int choice;
	if (ticks_<Tweak::balance_win_time_i()-20000&&nextwave.over()) {
		nextwave.reset();
		int num=1;

		if (ticks_>Tweak::balance_extrawave1_i()&&Random::sget(Tweak::balance_extrawave_random_i())==0) { num++; }
		if (ticks_>Tweak::balance_extrawave2_i()&&Random::sget(Tweak::balance_extrawave_random_i())==0) { num++; }
		for (int i=0;i<num;i++) {
			choice=Random::sget(3);
			if (choice==2) {
				choice=dominating_direction;
			}
			switch (choice) {
				case 0:
					action(A_ADDENEMY);
					break;
				case 1:
					action(A_ADDENEMY2);
					break;
			}
		}
	}
	if (ticks_>Tweak::balance_win_time_i()-1500) {
		Radio::instance()->trigger_once(RT_WIN);
	}

	if (
		ticks_>Tweak::balance_inbetweenwave_i()&&
		Random::sget(Tweak::balance_inbetweenwave_random_i())==0&&
		ticks_<Tweak::balance_win_time_i()-20000
	) {
		choice=dominating_direction;
		switch (choice) {
			case 0: action(A_ADDENEMY); break;
			case 1: action(A_ADDENEMY2); break;
		}
	}

	regen_interval.tick(ticks);
	if (regen_interval.check()) {
		weapon.gain(Tweak::balance_weapon_regen_i());
		jumpjets.gain(Tweak::balance_jumpjets_regen_i());
	}
	cap();
}

void Game::incr_score() {
	score.gain(Tweak::plane_score_i());
	if (
		ticks_>(Tweak::balance_win_time_i()/2)&&
		destruction.percent()>50
	) {
		Radio::instance()->trigger_once(RT_GOOD);
	}
}



void Game::rotrans() {
	glLoadIdentity();

	Light::start_frame();
	//Light::light(0);
	player_->position.rotate();
	Light::light(1);

	if (Tweak::skybox_draw_i()) Skybox::draw();

	//Light::light(2);


	player_->position.translate();

	glTranslatef(0.0f,-Tweak::player_height_f(),0.0f);
	//Light::light(3);
}


void Game::draw_hud() {
	ortho2d();

	Radio::instance()->draw();
	destruction.draw();
	jumpjets.draw();
	weapon.draw();
	score.draw();
	crosshair();
	if (destruction.empty()) {
		Help::gameover();
	}
	if (ticks_>Tweak::balance_win_time_i()) {
		Help::fin();
	}

	if (ticks_<Tweak::ui_mouselock_i()) {
		if (ismouselocked()) {
			Help::mouselock_off();
		} else {
			Help::mouselock();
		}
	}

	

	ortho2d_off();
}


void Game::draw() {
	Light::fog();

	rotrans();


	Frustum::instance()->stat_reset();
	Frustum::instance()->calculate();
	if (!Tweak::debug_hidemap_i()) {
		map2.draw();
	}
	container_.draw();
	Floor::draw();
	
	draw_hud();
}

bool Game::isvalid(C3f p) {
	if (Tweak::debug_collisionoff_i()==1) return true;
	if (p.y<0) return false;
	return !map2.does_collide(p,0.12);
}

bool Game::handle_event(SDL_Event event) {
	if (menu_active) {
		e_ingamemenuids id=(e_ingamemenuids)menu.handle_event(event);
		switch (id) {
			case MI_NONE: return true; break;
			case MI_POP:
			case IG_BACK: menu_active=false; return true; break;
			case IG_QUIT: state=GS_QUIT; break;
			default: break;
		}
	} else {
		return handle_game_event(event);
	}
	return false;
}


bool Game::handle_game_event(SDL_Event event) {
	switch (event.type) {
		case SDL_QUIT: return false; break;
		case SDL_MOUSEBUTTONDOWN:
			if (Tweak::editmode_i()==1)  {
				map2.select( event.button.x, Config::dy()-event.button.y,event.button.button);
				break;
			}
			if (ismouselocked()) {
				action(A_FIRE,player_,Config::dx()/2,Config::dy()/2);
				break;
			}
			lockmouse();
			break;
		case SDL_MOUSEMOTION:
			if (!ismouselocked()) break;
			action(A_TURN,player_,event.motion.xrel,event.motion.yrel);
			break;
		case SDL_KEYDOWN:

			switch (event.key.keysym.sym) {
				case SDLK_n: action(A_ADDENEMY); break;
				case SDLK_b: player_->drop_bomb(); break;

				case SDLK_1: map2.toggle_selected(); break;
				case SDLK_2: map2.split_selected(); break;
				case SDLK_3: map2.merge_selected(); break;
				case SDLK_4: map2.select_parent(); break;
				case SDLK_5: map2.cycle_material(); break;
				case SDLK_6: map2.set_material(); break;
				case SDLK_7: map2.copy(); break;
				case SDLK_8: map2.paste(); break;


				case SDLK_F2: map2.save(); break;
				case SDLK_F4: map2.load("save"); break;
				case SDLK_F5: map2.check(); break;
				case SDLK_F6: map2.debug(); break;
				//case SDLK_c: std::cout << "coll: " << map2.does_collide(player_->get_position(),0.1) << std::endl; break;
				case SDLK_x: Frustum::instance()->stat_print(); break;
				case SDLK_y: {C3f p; p=player_->get_position(); std::cout << "pos: " <<  p << std::endl; }break;

				default:
					return false;
					break;
			}
			break;

	}
	return true;
}
void Game::handle_state() {
	Uint8 *keys;
	keys = SDL_GetKeyState(NULL);


	if (ismouselocked()) {
		if (keys[SDLK_a]) {action(A_LEFT); }
		if (keys[SDLK_LEFT]) {action(A_LEFT); }
		if (keys[SDLK_d]) {action(A_RIGHT); }
		if (keys[SDLK_RIGHT]) {action(A_RIGHT); }
	} else {
		if (keys[SDLK_a]) {action(A_TURNLEFT); }
		if (keys[SDLK_LEFT]) {action(A_TURNLEFT); }
		if (keys[SDLK_d]) {action(A_TURNRIGHT); }
		if (keys[SDLK_RIGHT]) {action(A_TURNRIGHT); }
	}

	if (keys[SDLK_w]) {action(A_FORWARD); }
	if (keys[SDLK_s]) {action(A_BACKWARD); }
	if (keys[SDLK_UP]) {action(A_FORWARD); }
	if (keys[SDLK_DOWN]) {action(A_BACKWARD); }

	if (keys[SDLK_SPACE]) {action(A_JUMP); }
}

std::ostream& operator<<(std::ostream&s, Game sk) {
	return s;
}

} //namespace
