#include "Zombie.h"
#include "Human.h"
#include <stdio.h>      
#include <stdlib.h>     
#include <time.h>
using namespace std;

//Default Constructor
Zombie::Zombie() {}

//CREATION!!
Zombie::Zombie(City *city, int width, int height)
{
	this->city = city;
	this->width = width;
	this->height = height;
	this->starve = 0;
	srand(time(NULL));
}

 //DESTRUCTION!!
Zombie::~Zombie() {}


void Zombie::move() {

	//ZOMBIE TRIES TO EAT
	vector<int> moveDirections;
	//checks for humans to nom on
	if (y - 1 >= 0 && dynamic_cast<Human*>(city->getOrganism(x, y - 1)) != NULL) {
		
		moveDirections.push_back(NORTH); 
	
	}

	if (y + 1 < height && dynamic_cast<Human*>(city->getOrganism(x, y + 1)) != NULL) { 
		
		moveDirections.push_back(SOUTH); 
	
	}

	if (x + 1 < width && dynamic_cast<Human*>(city->getOrganism(x + 1, y)) != NULL) {
		
		moveDirections.push_back(EAST); 
	
	}

	if (x - 1 >= 0 && dynamic_cast<Human*>(city->getOrganism(x - 1, y)) != NULL) { 
		
		moveDirections.push_back(WEST);
	
	}

	if (y - 1 >= 0 && x + 1 < width && dynamic_cast<Human*>(city->getOrganism(x + 1, y - 1)) != NULL) { 
		
		moveDirections.push_back(NORTH_EAST);
	
	}

	if (y + 1 < height && x + 1 < width && dynamic_cast<Human*>(city->getOrganism(x + 1, y + 1)) != NULL) {
		
		moveDirections.push_back(SOUTH_EAST); 
	
	}

	if (y - 1 >= 0 && x - 1 >= 0 && dynamic_cast<Human*>(city->getOrganism(x - 1, y - 1)) != NULL) { 
		
		moveDirections.push_back(NORTH_WEST); 
	
	}

	if (y + 1 < height && x - 1 >= 0 && dynamic_cast<Human*>(city->getOrganism(x - 1, y + 1)) != NULL) { 
		
		moveDirections.push_back(SOUTH_WEST); 
	
	}

	//no humans make a sad zombie
	if (!moveDirections.empty())
		this->setStarve(0);
	else
	{
		//if no humans to eat zombie wanders around in sadness
		this->setStarve(this->getStarve() + 1); 

												
		if (y - 1 >= 0 && city->getOrganism(x, y - 1) == NULL) {
			
			moveDirections.push_back(NORTH);
		}

		if (y + 1 < height && city->getOrganism(x, y + 1) == NULL) {
			
			moveDirections.push_back(SOUTH);
		
		}

		if (x + 1 < width && city->getOrganism(x + 1, y) == NULL) {
			
			moveDirections.push_back(EAST); 
		
		}

		if (x - 1 >= 0 && city->getOrganism(x - 1, y) == NULL) {
			
			moveDirections.push_back(WEST);
		
		}
	}

	 //picks a random direction to move in
	if (!moveDirections.empty()) {
		int move = moveDirections.at(rand() % moveDirections.size());

		//clears previous space in grid
		city->setOrganism(NULL, x, y);

		//Sets the new position
		if (move == NORTH) { 
			
			this->setPosition(x, y - 1); 
		
		}

		else if (move == SOUTH) { 
			
			this->setPosition(x, y + 1);
		
		}

		else if (move == EAST) { 
			
			this->setPosition(x + 1, y);
		
		}

		else if (move == WEST) { 
			
			this->setPosition(x - 1, y);
		
		}

		else if (move == NORTH_EAST) { 
			
			this->setPosition(x + 1, y - 1); 
		
		}

		else if (move == SOUTH_EAST) {
			
			this->setPosition(x + 1, y + 1); 
		
		}

		else if (move == NORTH_WEST) { 
			
			this->setPosition(x - 1, y - 1); 
		
		}

		else if (move == SOUTH_WEST) { 
			
			this->setPosition(x - 1, y + 1);
		
		}

		//put zombie back into the grid
		city->setOrganism(this, x, y);
	}

	//sets the zombie moved to tru
	this->setHasMoved(true);
	//checks to see if zombie has starved
	this->starveZombie(); 

}

void Zombie::spawn() {

	//check if the zombie spawnCount limit is met
	if (this->getSpawnCount() >= ZOMBIE_BREED) {
		vector<int> moveDirections;
		srand(time(NULL));

		//Check if human in adjacent cell or diagonal
		if (y - 1 >= 0 && dynamic_cast<Human*>(city->getOrganism(x, y - 1)) != NULL) {

			moveDirections.push_back(NORTH);
		}

		if (y + 1 < height && dynamic_cast<Human*>(city->getOrganism(x, y + 1)) != NULL) {

			moveDirections.push_back(SOUTH);
		}

		if (x + 1 < width && dynamic_cast<Human*>(city->getOrganism(x + 1, y)) != NULL) {

			moveDirections.push_back(EAST);
		}

		if (x - 1 >= 0 && dynamic_cast<Human*>(city->getOrganism(x - 1, y)) != NULL) {

			moveDirections.push_back(WEST);
		}

		if (y - 1 >= 0 && x + 1 < width && dynamic_cast<Human*>(city->getOrganism(x + 1, y - 1)) != NULL) {

			moveDirections.push_back(NORTH_EAST);
		}

		if (y + 1 < height && x + 1 < width && dynamic_cast<Human*>(city->getOrganism(x + 1, y + 1)) != NULL) {

			moveDirections.push_back(SOUTH_EAST);
		}

		if (y - 1 >= 0 && x - 1 >= 0 && dynamic_cast<Human*>(city->getOrganism(x - 1, y - 1)) != NULL) {

			moveDirections.push_back(NORTH_WEST);
		}

		if (y + 1 < height && x - 1 >= 0 && dynamic_cast<Human*>(city->getOrganism(x - 1, y + 1)) != NULL) {

			moveDirections.push_back(SOUTH_WEST);
		}

		//if there is stuff in vector
		if (!moveDirections.empty()) {
			//gets a random direction to move in
			int spawnGridSpot = moveDirections.at(rand() % moveDirections.size());

			//New Zombie friend(Actually just moves the current zombie into the new space)													  
			Zombie *myZombie = new Zombie(city, GRID_WIDTH, GRID_HEIGHT);

			//then gets the new zombies postition
			if (spawnGridSpot == NORTH) {
				myZombie->setPosition(x, y - 1);
			}

			else if (spawnGridSpot == SOUTH) {
				myZombie->setPosition(x, y + 1);
			}

			else if (spawnGridSpot == EAST) {
				myZombie->setPosition(x + 1, y);
			}

			else if (spawnGridSpot == WEST) {
				myZombie->setPosition(x - 1, y);
			}
			else if (spawnGridSpot == NORTH_EAST) {
				myZombie->setPosition(x + 1, y - 1);
			}

			else if (spawnGridSpot == SOUTH_EAST) {
				myZombie->setPosition(x + 1, y + 1);
			}

			else if (spawnGridSpot == NORTH_WEST) {
				myZombie->setPosition(x - 1, y - 1);
			}

			else if (spawnGridSpot == SOUTH_WEST) {
				myZombie->setPosition(x - 1, y + 1);
			}

			city->setOrganism(myZombie, myZombie->x, myZombie->y);

			//zombie has now moved
			myZombie->setHasMoved(true);
			myZombie->setSpawnCount(0);
			myZombie->setHasSpawned(true);

			//reset if zombie has spawned
			this->setSpawnCount(0);
		}

	}

	else {
		//increases spawn count
		this->setSpawnCount(this->getSpawnCount() + 1);
	}
}

//Getters and Setters
void Zombie::setStarve(int eat) { 

	this->starve = eat; 
}

int Zombie::getStarve() { 

	return this->starve; 
}

void Zombie::starveZombie()
{
	//if zombies doesnt eat quick enough he defies the rules of zombies and becomes a human again
	if (this->getStarve() >= ZOMBIE_STARVE)
	{
		Human *myHuman = new Human(city, GRID_WIDTH, GRID_HEIGHT);
		myHuman->setPosition(x, y);
		city->setOrganism(myHuman, x, y);
		
	}
}