from pyspades.constants import *
from commands import add, admin, alias
from twisted.internet.task import LoopingCall
from twisted.internet import reactor
import random

#CONSTANTS - DON'T TOUCH THESE
HUMAN = 0
ZOMBIE = 1
HIDE_COORD = (0,0,63)

#CONFIGURABLES
LOBBY_TIME = 90 #The amount of time the lobby lasts in seconds
ROUND_TIME = 180 #The amount of time a round lasts in seconds
ZOMBIE_RIFLE_RESIST = 1 #Divisor for how much damage a zombie takes against rifle
ZOMBIE_SMG_RESIST = 2 #Divisor for how much damage a zombie takes against smg
ZOMBIE_SHOTGUN_RESIST = 1 #Divisor for how much damage a zombie takes against shotgun
ZOMBIE_SHOVEL_RESIST = 10 #Divisor for how much damage a zombie takes against shovel
ZOMBIE_HIT_PENALTY = 3 #Divisor for how much damage a zombie does with shovel
Z_FALL_IMMUNE = True #Are zombies immune to fall damage
#SERVER MESSAGES ## THE LINES MUST BE IN REVERSE ORDER (LAST LINE COMES FIRST, SECOND LAST LINE NEXT, FIRST LINE GOES LAST) PRE CONFIGURED MESSAGES SHOW EXAMPLE
INIT_SPAWN_MESSAGE = [("[!]for %s minutes as a human." % (ROUND_TIME/60)), ("[!]the game is to infect humans as a zombie, or survive"), ("[!]You are playing the Infection gamemode. The aim of")]
INIT_LOBBY_MESSAGE = [("[!]the round will begin."), ("[!]Currently the game is in lobby, after %s minute/s" % (LOBBY_TIME/60))]
INIT_ZOMBIE_MESSAGE = [("[!]win. Press 'V' when looking at a block to lunge"), ("[!]You must kill all humans in under %s minutes to" % (ROUND_TIME/60)), ("[!]You are a zombie, you can only use your spade to do damage.")]
INIT_HUMAN_MESSAGE = [("[!]against the zombie horde."), ("[!]You are a human, you must survive for %s minutes" % (ROUND_TIME/60))]


## FUNCTIONS
def startMatch(protocol):

	protocol.MATCH_RUNNING = True
	
def stopMatch(protocol):

	protocol.MATCH_RUNNING = False


def randomSpawn(spawns):

	i = random.randint(0,(len(spawns)-1))
	return spawns[i].getSpawnPosition()
	
def getTeamDead(team):
	for player in team.get_players():
		if player.state == HUMAN:
			return False
	return True
	
def roundWin(killer):

	if killer != None:

		killer.take_flag()
		killer.capture_flag()
		
def chooseZombie(protocol):

	protocol.PLAYER_LIST = []

	for player in protocol.blue_team.get_players():
		protocol.PLAYER_LIST.append(player)
	for player in protocol.green_team.get_players():
		protocol.PLAYER_LIST.append(player)
		
	i = random.randint(0,len(protocol.PLAYER_LIST)-1)
		
	protocol.PLAYER_LIST[i].state = ZOMBIE
	protocol.send_chat(protocol.PLAYER_LIST[i].name + " is the starting zombie")
	
def setAllHuman(protocol):

	protocol.PLAYER_LIST = []

	for player in protocol.blue_team.get_players():
		protocol.PLAYER_LIST.append(player)
	for player in protocol.green_team.get_players():
		protocol.PLAYER_LIST.append(player)
	
	for i in xrange(0, len(protocol.PLAYER_LIST)-1):
		protocol.PLAYER_LIST[i].state = HUMAN
		
def resetMessageStatus(protocol):

	for player in protocol.blue_team.get_players():
		protocol.PLAYER_LIST.append(player)
	for player in protocol.green_team.get_players():
		protocol.PLAYER_LIST.append(player)
	
	for i in xrange(0, len(protocol.PLAYER_LIST)-1):
		protocol.PLAYER_LIST[i].joinMessage = False
		protocol.PLAYER_LIST[i].zombieMessage = False
		protocol.PLAYER_LIST[i].humanMessage = False
		
def sendPackedMessage(connection, message):

	for line in message:
		connection.send_chat(line)
## FUNCTIONS

## CLASSES
class ZombieSpawn:
	
	def __init__(self,x,y,z):
		self.spawn = (x,y,z)
		
	def getSpawnPosition(self):
		return self.spawn
		
class HumanSpawn:

	def __init__(self,x,y,z):
		self.spawn = (x,y,z)
	
	def getSpawnPosition(self):
		return self.spawn
## CLASSES


## COMMANDS	

#Get if MATCH_RUNNING is true or false
@alias("gs")
@admin
def getstatus(connection):

	if connection.protocol.MATCH_RUNNING:
		connection.send_chat("Match Running")
	elif not connection.protocol.MATCH_RUNNING:
		connection.send_chat("Match Not Running")
add(getstatus)

#Get you current infection status
@alias("state")
@admin
def getstate(connection):

	if connection.state == HUMAN:
		connection.send_chat("You are a human")
	elif connection.state == ZOMBIE:
		connection.send_chat("You are a zombie")
add(getstate)

#Set your state to zombie
@alias("zom")
@admin
def becomezombie(connection):
	
	connection.state = ZOMBIE
	connection.send_chat("You have become a zombie")
add(becomezombie)

#Set your state to human
@alias("hum")
@admin
def becomehuman(connection):

	connection.state = HUMAN
	connection.send_chat("You have become a human")
add(becomehuman)

#Set MATCH_RUNNING true
@alias("sr")
@admin
def startround(connection):
	startMatch(connection.protocol)
	connection.send_chat("Setting MATCH_RUNNING to true")
add(startround)

#Set MATCH_RUNNING false
@alias("er")
@admin
def endround(connection):
	stopMatch(connection.protocol)
	connection.send_chat("Setting MATCH_RUNNING to false")
add(endround)

#Pick zombies
@alias("pz")
@admin
def pickzombies(connection):
	chooseZombie(connection.protocol)
add(pickzombies)

#Send me a message
@alias("sm")
def sendmessage(connection):
	sendPackedMessage(connection, INIT_SPAWN_MESSAGE)
	sendPackedMessage(connection, INIT_LOBBY_MESSAGE)
	sendPackedMessage(connection, INIT_ZOMBIE_MESSAGE)
	sendPackedMessage(connection, INIT_HUMAN_MESSAGE)
add(sendmessage)
## COMMANDS

def apply_script(protocol, connection, config):

	class InfConnection(connection):
		
		def on_join(self):
			
			#If a match is running, people who join become zombies
			if self.protocol.MATCH_RUNNING:
				self.state = ZOMBIE
			else:
				self.state = HUMAN
				
			self.joinMessage = False
			self.zombieMessage = False
			self.humanMessage = False
				
			return connection.on_join(self)
			
		
		def on_spawn_location(self, pos):
		
			if not self.joinMessage:
				self.initJoinMessage = reactor.callLater(5, sendPackedMessage, self, INIT_SPAWN_MESSAGE)
				self.joinMessage = True
				if not self.protocol.MATCH_RUNNING:
					self.initLobbyMessage = reactor.callLater(3, sendPackedMessage, self, INIT_LOBBY_MESSAGE)
			
			if self.protocol.MATCH_RUNNING:
				if self.state == ZOMBIE:
					if not self.zombieMessage:
						sendPackedMessage(self, INIT_ZOMBIE_MESSAGE)
						
				if self.state == HUMAN:
					if not self.humanMessage:
						sendPackedMessage(self, INIT_HUMAN_MESSAGE)
					
		
			if not self.protocol.MATCH_RUNNING:
				setAllHuman(self.protocol)
		
			ext = self.protocol.map_info.extensions
			
			#Humans on humans team, Zombies on zombie team
			if self.protocol.MATCH_RUNNING:
				if self.state == HUMAN:
					self.team = self.protocol.blue_team
				elif self.state == ZOMBIE:
					self.team = self.protocol.green_team
			
			#While match is running place people in appropriate spawn locations
			if self.protocol.MATCH_RUNNING:
			
				if self.state == ZOMBIE:
					return randomSpawn(self.protocol.zombieSpawns)
					
				elif self.state == HUMAN:
					return self.protocol.humanSpawn
					
			elif not self.protocol.MATCH_RUNNING:
				
				return self.protocol.humanSpawn
					
					
		def on_hit(self, hit_amount, hit_player, type, grenade):
			
			#Stop damage if match is not running
			if not self.protocol.MATCH_RUNNING:
				return False
			else:
				
				#ZOMBIE DAMAGE MODIFIERS
				if self.state == HUMAN and hit_player.state == ZOMBIE:
					
					if self.tool == WEAPON_TOOL:
					
						if self.weapon == RIFLE_WEAPON:
							return hit_amount/ZOMBIE_RIFLE_RESIST
						elif self.weapon == SMG_WEAPON:
							return hit_amount/ZOMBIE_SMG_RESIST
						elif self.weapon == SHOTGUN_WEAPON:
							return hit_amount/ZOMBIE_SHOTGUN_RESIST
							
							
					elif self.tool == SPADE_TOOL:
						return hit_amount/ZOMBIE_SHOVEL_RESIST
				
				#HUMAN DAMAGE MODIFIERS
				elif self.state == ZOMBIE and hit_player.state == HUMAN:
					
					if self.tool == SPADE_TOOL:
						return hit_amount/ZOMBIE_HIT_PENALTY
					else: 
						return False
				
				else:
					
					return connection.on_hit(self, hit_amount, hit_player, type, grenade)
					
			
		def on_kill(self, killer, type, grenade):
		
			#If a match is running, handle player infections
			if self.protocol.MATCH_RUNNING:
			
				if self.state == HUMAN:
					
					if killer != None:
					
						if killer.state == ZOMBIE:
					
							self.state = ZOMBIE
							self.send_chat("You have been infected by %s" % killer.name)
							killer.send_chat("You infected " + self.name)
							
				self.protocol.check_round_end(killer)
				print("CHECKING ROUND END")
			return connection.on_kill(self, killer, type, grenade)
			
		def on_fall(self, damage):
		
			if not self.protocol.MATCH_RUNNING:
				return False
			
			if self.protocol.MATCH_RUNNING:
				if self.state == ZOMBIE:
					return False
				else:
					return connection.on_fall(self, damage)
			
				
		def on_block_build_attempt(self, x, y, z):
			
			#Stop building
			return False
			
		def on_line_build_attempt(self, points):
		
			#Stop building
			return False
			
		
		def on_block_destroy(self, x, y, z, mode):
		
			#Stop block destruction
			return False
			
			
		def on_grenade(self, time_left):
		
			#Stop zombies using grenade
			if self.state == ZOMBIE:
				return False
			else:
				return connection.on_grenade(self, time_left)
			
			
	class InfProtocol(protocol):
		
		game_mode = CTF_MODE
		
		def on_advance(self, map):
		
			self.PLAYER_LIST = []
		
			setAllHuman(self)
			
			self.timer = []
			
			self.countdown = reactor.callLater(LOBBY_TIME, self.lobbyTimer)
			self.timer = []
			for i in xrange(1,6):
				self.timer.append(reactor.callLater((LOBBY_TIME-i), self.send_chat, ("Round Begins in %s" % i)))
				print(len(self.timer))

		
		def lobbyTimer(self):
		
			count = 0
		
			for team in (self.green_team, self.blue_team):
				count += team.count()
				
			if count >= 2:
				self.beginRound()
			else:
				self.send_chat("A GAME REQUIRES ATLEAST 2 PEOPLE")
				print("NOT ENOUGH PLAYERS")
				self.countdown = reactor.callLater(LOBBY_TIME, self.lobbyTimer)
				self.timer = []
				for i in xrange(1,6):
					self.timer.append(reactor.callLater((LOBBY_TIME-i), self.send_chat, ("Round Begins in %s" % i)))
					print(len(self.timer))
			
		
		def on_map_change(self, map):
		
			self.PLAYER_LIST = []
		
			self.MATCH_RUNNING = False
		
			if self.map_info.extensions['infection']:
				
				ext = self.map_info.extensions
				self.zombieSpawns = []
				self.humanSpawn = ext['human_spawn']
				
				for spawns in ext['zombie_spawns']:
					self.zombieSpawns.append(ZombieSpawn(*spawns))
					
		def check_round_end(self, killer = None, message = True):
		
			if self.MATCH_RUNNING:
				
				if getTeamDead(self.blue_team):
					self.winRound(killer)
					return
				
		
		def winRound(self, killer):
			
			if killer.state == ZOMBIE:
				self.roundTimer.cancel()
				for i in xrange(0,5):
					self.timer[i].cancel()
		
			print("ROUND WON")
			roundWin(killer)
			setAllHuman(self)
			resetMessageStatus(self)
			self.MATCH_RUNNING = False
			return
			
		def beginRound(self):
		
			chooseZombie(self)
			self.roundTimer = reactor.callLater(ROUND_TIME, self.humanWin)
			self.timer = []
			for i in xrange(1,6):
				self.timer.append(reactor.callLater((ROUND_TIME-i), self.send_chat, ("Round Ends in %s" % i)))
				print(len(self.timer))
		
			for player in self.PLAYER_LIST:
				player.kill()
			
			self.MATCH_RUNNING = True
		
		def humanWin(self):
		
			for player in self.blue_team.get_players():
				if player.state == HUMAN:
					self.winRound(player)
					return
			
		def on_base_spawn(self, x, y, z, base, entity_id):
			return HIDE_COORD

		def on_flag_spawn(self, x, y, z, flag, entity_id):
			return HIDE_COORD

	return InfProtocol, InfConnection or None
