#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <allegro5/allegro_audio.h>
#include <allegro5/allegro_acodec.h>
#include <list>
#include <fstream>
#include <string>
#include <sstream>
#include <iostream>

#include "GameObject.h"
#include "Ship.h"
#include "HomeBase.h"
#include "Globals.h"
#include "Quadtree.h"

bool keys[] = {false, false, false, false, false};
enum KEYS{W, S, A, D, SPACE};

//globals
std::list<GameObject *> objects;
std::list<GameObject *>::iterator iter;
std::list<GameObject *>::iterator iter2;
std::list<HomeBase *> Bases;
std::list<HomeBase *>::iterator Baseiter;
std::list<HomeBase *>::iterator Baseiter2;
std::list<Ship *> ships;
std::list<Ship *>::iterator Shipiter;
std::list<Ship *>::iterator Shipiter2;
float mousex, mousey;

//ALLEGRO_SAMPLE_INSTANCE *songInstance;

//prototypes
void ChangeState(int &state, int newState);

int main(int argc, char **argv)
{
	//==============================================
	//SHELL VARIABLES
	//==============================================
	bool done = false;
	bool render = false;

	float gameTime = 0;
	int frames = 0;
	int gameFPS = 0;
	int spawner = -2;

	//==============================================
	//PROJECT VARIABLES
	//==============================================
	int state = -1;
	Quadtree *quad = new Quadtree(0, WIDTH, 0, HEIGHT, 20);  //where our units will be stored for col. detection
		

	//==============================================
	//ALLEGRO VARIABLES
	//==============================================
	ALLEGRO_DISPLAY *display = NULL;
	ALLEGRO_EVENT_QUEUE *event_queue = NULL;
	ALLEGRO_TIMER *timer;
	ALLEGRO_FONT *font18;
	
	//==============================================
	//ALLEGRO INIT FUNCTIONS
	//==============================================
	if(!al_init())										//initialize Allegro
		return -1;

	display = al_create_display(WIDTH, HEIGHT);			//create our display object

	if(!display)										//test display object
		return -1;

	//==============================================
	//ADDON INSTALL
	//==============================================
	al_install_keyboard();
	al_install_mouse();
	al_init_image_addon();
	al_init_font_addon();
	al_init_ttf_addon();
	al_init_primitives_addon();
	al_install_audio();
	al_init_acodec_addon();


	//==============================================
	//PROJECT INIT
	//==============================================
	font18 = al_load_font("arial.ttf", 18, 0);
	//al_reserve_samples(15);

	//spawns bases for player and 9 AI's (1 player, 4 hostile AI, 5 neutral AI)
	for(int i = 0; i != 10; i++)
	{
		HomeBase *Base = new HomeBase();
		Base->Init();
		Base->SetPlayerID(i+1);
		if(i < 5)
		{
			Base->SetX(100 + rand() % (WIDTH / 2 - 100));
			Base->SetY(100 + rand() % (HEIGHT / 2 - 100));
		}
		else
		{
			Base->SetX(100 + rand() % (WIDTH - 100));
			Base->SetY(100 + rand() % (HEIGHT - 100));
		}
		if(i == 0)
			Base->SetColor(al_map_rgb(255,0,0));
		else if(i == 2)
			Base->SetColor(al_map_rgb(0,255,0));
		else if(i == 4)
			Base->SetColor(al_map_rgb(0,0,255));
		else if(i == 6)
			Base->SetColor(al_map_rgb(150,150,0));
		else if(i == 8)
			Base->SetColor(al_map_rgb(0,150,150));
		else
			Base->SetColor(al_map_rgb(255,255,255));
							
		Bases.push_back(Base);
		objects.push_back(Base);
	}
	

	
	
	ChangeState(state, PLAYING);

	srand(time(NULL));

	//==============================================
	//TIMER INIT AND STARTUP
	//==============================================
	event_queue = al_create_event_queue();
	timer = al_create_timer(1.0 / 60);

	al_register_event_source(event_queue, al_get_timer_event_source(timer));
	al_register_event_source(event_queue, al_get_keyboard_event_source());
	al_register_event_source(event_queue, al_get_mouse_event_source());

	al_start_timer(timer);
	gameTime = al_current_time();
	while(!done)
	{
		ALLEGRO_EVENT ev;
		al_wait_for_event(event_queue, &ev);
		
		//==============================================
		//INPUT
		//==============================================
			if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
			{
				switch(ev.keyboard.keycode)
				{
				case ALLEGRO_KEY_ESCAPE:
					done = true;
					break;
				case ALLEGRO_KEY_A:
					keys[A] = true;
					break;
				case ALLEGRO_KEY_D:
					keys[D] = true;
					break;
				case ALLEGRO_KEY_W:
					keys[W] = true;
					break;
				case ALLEGRO_KEY_S:
					keys[S] = true;
					break;
				case ALLEGRO_KEY_SPACE:
					if(state == TITLE)
						ChangeState(state, PLAYING);
					else if(state == PLAYING)
					{
						keys[SPACE] = true;
					}
					else if(state == LOST)
						ChangeState(state, PLAYING);
					break;
				}
			}
			else if(ev.type == ALLEGRO_EVENT_KEY_UP)
			{
				switch(ev.keyboard.keycode)
				{
				case ALLEGRO_KEY_ESCAPE:
					done = true;
					break;
				case ALLEGRO_KEY_A:
					keys[A] = false;
					break;
				case ALLEGRO_KEY_D:
					keys[D] = false;
					break;
				case ALLEGRO_KEY_W:
					keys[W] = false;
					break;
				case ALLEGRO_KEY_S:
					keys[S] = false;
					break;
				case ALLEGRO_KEY_SPACE:
					keys[SPACE] = false;
					break;
				}
			}/*
			else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
			{
				keys[SPACE] = true;
				mousex = ev.mouse.x;
				mousey = ev.mouse.y;
			}
			else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)
			{
				keys[SPACE] = false;
			}*/
			else if(ev.type == ALLEGRO_EVENT_MOUSE_AXES)
			{				
				mousex = ev.mouse.x;
				mousey = ev.mouse.y;
			}
			//==============================================
			//GAME UPDATE
			//==============================================
			else if(ev.type == ALLEGRO_EVENT_TIMER)
			{
				render = true;
				quad->Clear();

				//UPDATE FPS===========
				
				//=====================

				if(state == PLAYING)
				{
					if(al_current_time() - gameTime >= 1)
					{
						gameTime = al_current_time();
						gameFPS = frames;
						frames = 0;
					}
		
				
					//Spawn More Ships
					spawner++;
					if (spawner >= 60) 
					{						
						for(Baseiter = Bases.begin(); Baseiter != Bases.end(); ++Baseiter)
						{
								if((*Baseiter)->GetShipCount() > 9) continue;   //increase this number to spawn more ships
								Ship *ship = new Ship();
								ship->Init();
								ship->SetPlayerID((*Baseiter)->GetPlayerID());
								ship->SetX(rand() % 40 + (*Baseiter)->GetX() - 20);
								ship->SetY(rand() % 40 + (*Baseiter)->GetY() - 20);
								ship->SetColor((*Baseiter)->GetColor());

								(*Baseiter)->SetShipCount(1);   //increase base ship count
								objects.push_back(ship);        //add ship to objects and to ships
								ships.push_back(ship);
						}						
						spawner = 0;
					}

					//used to upgrade ships (VERY BASIC----later will be used after leveling)
					if(keys[SPACE]) //used to move ships to location of mouse
					{
					
						for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter)
						{
							if((*Shipiter)->GetPlayerID() != 1) continue;
							(*Shipiter)->SetDestX(mousex);
							(*Shipiter)->SetDestY(mousey);
							(*Shipiter)->setMoving(true);
						}
					}
					if(keys[W])  //increase ship speed
					{
					
						for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter)
						{
							if((*Shipiter)->GetPlayerID() != 1) continue;
							(*Shipiter)->setSpeed(.05);
						}
					}
					
					if(keys[S])  //increase ship damage
					{
					
						for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter)
						{
							if((*Shipiter)->GetPlayerID() != 1) continue;
							(*Shipiter)->setAttack(5);
						}
						std::cout << std::endl;
					}
					
					if(keys[A])  //increase ship range
					{
					
						for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter)
						{
							if((*Shipiter)->GetPlayerID() != 1) continue;
							(*Shipiter)->setRange(5);
						}
						std::cout << std::endl;
					}
					
					if(keys[D])  //increase ship health
					{
					
						for(Shipiter = ships.begin(); Shipiter != ships.end(); ++Shipiter)
						{
							if((*Shipiter)->GetPlayerID() != 1) continue;
							(*Shipiter)->setHealth(5);
						}
						std::cout << std::endl;
					}

					
					//Update objects positions/animations and add to quad tree.
					for(iter = objects.begin(); iter != objects.end(); ++iter)
					{
						(*iter)->Update();
						if((*iter)->GetID() == BASE) continue;
						quad->AddObject(*iter);
					}

					//used for collision detection (if this is even somewhat correct) //At the moment, i just randomly move units until there is no collision
					for(iter = objects.begin(); iter != objects.end(); ++iter)
					{
						if((*iter)->GetID() == BASE) continue;
						vector<GameObject*> returnedObjects;
						returnedObjects = quad->GetObjectsAt((*iter)->GetX(), (*iter)->GetY());
						if(returnedObjects.size() < 2) continue;
						for(int i = 0; i != returnedObjects.size(); i++)
						{
							if((*iter) == returnedObjects[i]) continue;
							if((*iter)->CheckCollisions(returnedObjects[i]))
							{
								returnedObjects[i]->SetX(returnedObjects[i]->GetX() + (-5 + rand() % 10));
								returnedObjects[i]->SetY(returnedObjects[i]->GetY() + (-5 + rand() % 10));
							}
						}
					}
					
										
					
													
				//cull the dead
				for(Shipiter = ships.begin(); Shipiter != ships.end(); )
				{
					if(!(*Shipiter)->GetAlive())
					{						
						Shipiter = ships.erase(Shipiter);  //remove from ships
					}
					else
						Shipiter++;
				}
				for(iter = objects.begin(); iter != objects.end(); ) //remove from objects and give credit to base that killed
				{
					if(! (*iter)->GetAlive())
					{
						if((*iter)->GetID() == SHIP)
						{
							int id = (*iter)->GetPlayerID();
							for(Baseiter = Bases.begin(); Baseiter != Bases.end(); Baseiter++)
							{
								if((*Baseiter)->GetPlayerID() == id)
								{
									(*Baseiter)->SetShipCount(-1);
									int killer = (*iter)->GetShipKiller();
									for(Baseiter2 = Bases.begin(); Baseiter2 != Bases.end(); )
									{
										if((*Baseiter2)->GetPlayerID() == killer)
										{(*Baseiter2)->setScore(1); break;}
										else
											Baseiter2++;
									}
									break;
								}
							}

						}
						(*iter)->Destroy();
						delete (*iter);
						iter = objects.erase(iter);
						
					}
					else 
						iter++;
				}
				
				

				//==============================================
				//RENDER
				//==============================================

				if(render) //&& al_is_event_queue_empty(event_queue))
				{
					render = false;
					frames++;
			

					//BEGIN PROJECT RENDER================
					if(state ==TITLE)
					{}
					else if(state == PLAYING)
					{
						al_draw_textf(font18, al_map_rgb(255, 0, 255), 5, 35, 0, "FPS: %i   mouse: %f,%f", gameFPS, mousex, mousey);	//display FPS on screen
						
					

						for(iter = objects.begin(); iter != objects.end(); ++iter)
							(*iter)->Render();

						//quad->draw();    //used to draw visual for quadrants
					}
					else if(state == LOST)
					{}

					//FLIP BUFFERS========================
					al_flip_display();
					al_clear_to_color(al_map_rgb(0,0,0));
				}

				
			}
		}
	}
	//==============================================
	//DESTROY PROJECT OBJECTS
	//==============================================
	for(iter = objects.begin(); iter != objects.end(); )
	{
		(*iter)->Destroy();
		delete (*iter);
		iter = objects.erase(iter);
	}
	ships.clear();
	Bases.clear();


	//SHELL OBJECTS=================================
	al_destroy_font(font18);
	al_destroy_timer(timer);
	al_destroy_event_queue(event_queue);
	al_destroy_display(display);

	return 0;
	
}


//will later be used for menu, game and game-over screen
void ChangeState(int &state, int newState)
{
	if(state == TITLE)
	{}
	else if(state == PLAYING)
	{}
	else if(state == LOST)
	{}

	state = newState;

	if(state == TITLE)
	{}
	else if(state == PLAYING)
	{}
	else if(state == LOST)
	{}
}