import host
import bf2
import math
import tokenize
from game.scoringCommon import addScore, RPL
from game.content import getVehicleType, getCameraType, getWeaponType
from game.imports.demoRecording import demoChat
from bf2 import g_debug

g_controlPoints = [] # cache, as this map won't change
		
class Nuke_Components:
	def __init__(self):
		self.nuke_count_down = None #30 minute game, 1 minute = 60, 10min =600
		self.nuke_PCO = None
		self.nuke_initialized = 0
		self.gotObjects = 0
		self.animateNukePos1 = None
		self.animateNukePos2 = None
		self.animateNukePos3 = None
		self.animateTime1 = 210
		self.animateTime2 = 210
		self.animateTime3 = 210
		self.animateTimer1 = None
		self.animateTimer2 = None
		self.animateTimer3 = None
		self.nuke_launched = 0
		self.nuke_destroyed = 0
		self.nuke_Tkd = 0
		self.defend_nuke_score = 4
		self.destroyed_nuke_score = 10
		self.tk_nuke_score = -30
		self.NukeStatusUpdaterTimer = None
		self.nukeMissleObj = None
		self.nukeAttackers = []
		self.nukeAttackersUpdater_Timer = None
		self.nuke_trigger_id = 0
		self.nukeMissleObjResetPos = None
		self.nukeTimeLimit = None
	
class Nuke_Sounds:
	def __init__(self):
		self.nukeRepairTimer = None
		self.nuke_state1_sound = None
		self.nuke_state2_sound = None
		self.nuke_state3_sound = None
		self.nuke_destroyed_sound = None
		self.nuke_missle_sound = None
		self.nuke_sounds_repair = []
		self.nuke_state1_sound_played = 0
		self.nuke_state2_sound_played = 0
		self.nuke_state3_sound_played = 0
		self.nuke_missle_sound_played = 0

NukeComponents = Nuke_Components()
NukeSounds = Nuke_Sounds()

usingLauncher = None
try:
	usingLauncher = GetSetting("poe2Launcher")
	usingLauncher = int(usingLauncher[0])
except:
	pass #not using POE2 Launcher

DOME_OF_DEATH = None
NUKE_TIME_LIMIT = None
DEMO_RECORDING = None

def handle_token(type, token, (srow, scol), (erow, ecol), line):
	global DOME_OF_DEATH
	global NUKE_TIME_LIMIT
	global DEMO_RECORDING
	if srow % 2 == 1:
		pass
	else:
		if DOME_OF_DEATH is None:	
			try: DOME_OF_DEATH = int(token)
			except: pass
		elif NUKE_TIME_LIMIT is None:
			try: NUKE_TIME_LIMIT = int(token)
			except: pass
		elif DEMO_RECORDING is None:
			try: DEMO_RECORDING = int(token)
			except: pass

def initSettings():
	global DOME_OF_DEATH
	global NUKE_TIME_LIMIT
	global DEMO_RECORDING
	if usingLauncher:
		try:
			DOME_OF_DEATH = GetSetting("domeOfDeath")
			DOME_OF_DEATH = int(DOME_OF_DEATH[0])
		
			DEMO_RECORDING = GetSetting("demoRecording")
			DEMO_RECORDING = int(DEMO_RECORDING[0])
		except:
			if g_debug: print 'Error in returning game mode settings'
	else:
		if g_debug: print 'not using laucher'
		try:
			settingsList = open("mods/poe2/settings/POE2Settings.con".replace('/', '\\'), "r")
			tokenize.tokenize(settingsList.readline, handle_token)
			settingsList.close()
			
		except IOError:
			host.rcon_invoke('echo "WARNING! POE2Settings.con not found!"')
			DOME_OF_DEATH = 0
			NUKE_TIME_LIMIT = 0
			DEMO_RECORDING = 0

initSettings()

def init():
	# events hook
	host.registerGameStatusHandler(onGameStatusChanged)
	if host.sgl_getIsAIGame() == 1:
		host.sh_setEnableCommander(0)
	else:
		host.sh_setEnableCommander(0)
		
	host.registerHandler('TimeLimitReached', onTimeLimitReached, 1)	
		
def deinit():
	bf2.triggerManager.destroyAllTriggers()
	host.unregisterGameStatusHandler(onGameStatusChanged)

def onGameStatusChanged(status):
	if bf2.serverSettings.getGameMode() != "gpm_nuke": return
	if g_debug: print 'nuke gamemode playing'
	if status == bf2.GameStatus.Playing:
		ticketsTeam1 = 999
		ticketsTeam2 = 999
		
		bf2.gameLogic.setTickets(1, ticketsTeam1)
		bf2.gameLogic.setTickets(2, ticketsTeam2)
		bf2.gameLogic.setTicketState(1, 0)
		bf2.gameLogic.setTicketState(2, 0)
		host.registerHandler('TicketLimitReached', onTicketLimitReached)
		# player events
		host.registerHandler('PlayerDeath', onPlayerDeathCQ)
		host.registerHandler('PlayerKilled', onPlayerKilledCQ)
		host.registerHandler('PlayerSpawn', onPlayerSpawn)
		host.registerHandler('VehicleDestroyed', onVehicleDestroyed)
		host.registerHandler('EnterVehicle', onEnterVehicle)
		if DEMO_RECORDING: host.registerHandler('ChatMessage', onChatMessage)
		NukeComponents.nukeTimeLimit = host.rcon_invoke("sv.timeLimit")
	else:
		nukeCleanUp()
		bf2.triggerManager.destroyAllTriggers()
		
def setTicketLimits():
	ticketsTeam2 = bf2.gameLogic.getTickets(2)
	bf2.gameLogic.setTicketLimit(1, 1, 0)
	bf2.gameLogic.setTicketLimit(2, 1, 0)
	bf2.gameLogic.setTicketLimit(2, 2, 10)
	bf2.gameLogic.setTicketLimit(2, 3, int(ticketsTeam2*0.1))
	bf2.gameLogic.setTicketLimit(2, 4, int(ticketsTeam2*0.2))
	
def nukeCleanUp():
	NukeComponents.nuke_count_down = None
	NukeComponents.nuke_PCO = None
	NukeComponents.nuke_initialized = 0
	NukeComponents.gotObjects = 0
	NukeComponents.nukeMissleObj = None
	NukeComponents.nuke_launched = 0
	NukeComponents.nuke_destroyed = 0
	NukeComponents.nuke_tkd = 0
	try:
		NukeComponents.NukeStatusUpdaterTimer.destroy()
	except:
		pass
	NukeComponents.NukeStatusUpdaterTimer = None
	NukeComponents.defend_nuke_score = 4
	NukeComponents.destroyed_nuke_score = 10
	NukeComponents.tk_nuke_score = -30
	NukeComponents.animateNukePos1 = None
	NukeComponents.animateNukePos2 = None
	NukeComponents.animateNukePos3 = None
	NukeComponents.animateTime1 = 210
	NukeComponents.animateTime2 = 210
	NukeComponents.animateTime3 = 210
	NukeComponents.nukeAttackers = []
	try:
		NukeComponents.nukeAttackersUpdater_Timer.destroy()
	except:
		pass
	NukeComponents.nukeAttackersUpdater_Timer = None
	NukeComponents.nuke_trigger_id = 0
	NukeComponents.animateTimer1 = None
	NukeComponents.animateTimer2 = None
	NukeComponents.animateTimer3 = None
	NukeComponents.nukeMissleObjResetPos = None
	
	NukeSounds.nuke_attack_sound = None
	NukeSounds.nuke_state1_sound = None
	NukeSounds.nuke_state2_sound = None
	NukeSounds.nuke_state3_sound = None
	NukeSounds.nuke_destroyed_sound = None

def onChatMessage(playerId, text, channel, flags):
	player = bf2.playerManager.getPlayerByIndex(playerId)
	demoChat(player, text)

#trigger bound to the nuke object, used for defence scoring events
def nukeTrigger(triggerId, cp, vehicle, enter, userdata):
	if enter:
		occPlayers = vehicle.getOccupyingPlayers()
		for p in occPlayers:
			if p.getTeam() == 2:
				pass		

def clearOldAttackers(data):
	index = 0
	NukeComponents.nukeAttackers = []
	pcos = bf2.triggerManager.getObjects(NukeComponents.nuke_trigger_id)
	for obj in pcos:
		if not obj: continue
		occupyingPlayers = obj.getOccupyingPlayers()
		for p in occupyingPlayers:	
			if p.getTeam() == 2:	
				NukeComponents.nukeAttackers.append(p)

def isStoredNukeAttacker(player):
	found = 0
	for storedAttacker in NukeComponents.nukeAttackers:
		if storedAttacker.getName() == player.getName():	
			found = 1
	return found

#controls the nuke countdown and plays the count down messages and sounds
def nukeStatusUpdater(data):
	checkNuke()
	team2Tickets = bf2.gameLogic.getTickets(2)
	print team2Tickets
	print NukeSounds.nuke_missle_sound_played

	if team2Tickets == NukeComponents.nukeTimeLimit - 30 and NukeSounds.nuke_state1_sound_played != 1:
		playSounds(NukeSounds.nuke_state1_sound)
		NukeSounds.nuke_state1_sound_played = 1
			
	elif team2Tickets == (NukeComponents.nukeTimeLimit / 3) * 2 and NukeSounds.nuke_state2_sound_played != 1:
		playSounds(NukeSounds.nuke_state2_sound)
		NukeSounds.nuke_state2_sound_played = 1
		
	elif team2Tickets == NukeComponents.nukeTimeLimit / 3 and NukeSounds.nuke_state3_sound_played != 1:
		playSounds(NukeSounds.nuke_state3_sound)
		NukeSounds.nuke_state3_sound_played = 1	
	
	elif team2Tickets == 2 and NukeSounds.nuke_missle_sound_played != 1: #give the animations 30 seconds to run
		playSounds(NukeSounds.nuke_missle_sound)
		NukeSounds.nuke_missle_sound_played = 1
		
		#start the nuke launch animation system
		getAnimateNukePos()
	
	elif team2Tickets == 1:
		
		#nuke countdown has reached zero, so kill this status updater
		NukeComponents.NukeStatusUpdaterTimer.destroy()
		NukeComponents.NukeStatusUpdaterTimer = None

#see if the nuke has been destroyed or tk'd
def checkNuke():
	if NukeComponents.nuke_destroyed or NukeComponents.nuke_tkd:
		
		NukeComponents.NukeStatusUpdaterTimer.destroy()
		NukeComponents.NukeStatusUpdaterTimer = None
		#it has been destoryed or tk'd, stop the nuke status update timer
		
#get the positions of the nuke launcher and the missle object, then create the animation timers
def getAnimateNukePos():
	nPos1 = NukeComponents.nuke_PCO.getPosition()
	nPos2 = (nPos1[0] + 0.03, nPos1[1], nPos1[2] + 0.03)
	nPos3 = (nPos1[0] - 0.03, nPos1[1], nPos1[2] + 0.03)
	NukeComponents.animateNukePos1 = nPos1
	NukeComponents.animateNukePos2 = nPos2
	NukeComponents.animateNukePos3 = nPos3
	
	NukeComponents.animateNuke_Timer1 = bf2.Timer(animateNuke1, 0, 1)
	NukeComponents.animateNuke_Timer1.setRecurring(0.1)
	NukeComponents.animateNuke_Timer2 = bf2.Timer(animateNuke2, 0, 1)
	NukeComponents.animateNuke_Timer2.setRecurring(0.1)
	NukeComponents.animateNuke_Timer3 = bf2.Timer(animateNuke3, 0, 1)
	NukeComponents.animateNuke_Timer3.setRecurring(0.1)
	
	NukeComponents.nuke_launched = 1

#animations that first shakes the launcher then moves the missle
def animateNuke1(data):
	NukeComponents.animateTime1 -= 1
	#nuke launcher animation
	if NukeComponents.animateTime1 >= 180:
		if NukeComponents.nuke_PCO.getPosition() == NukeComponents.animateNukePos1:
			NukeComponents.nuke_PCO.setPosition(NukeComponents.animateNukePos2)
		else:
			if NukeComponents.nuke_PCO.getPosition() == NukeComponents.animateNukePos2:
				NukeComponents.nuke_PCO.setPosition(NukeComponents.animateNukePos3)
			else:
				NukeComponents.nuke_PCO.setPosition(NukeComponents.animateNukePos2)
				
	#nuke missle animation
	elif NukeComponents.animateTime1 < 180 and NukeComponents.animateTime1 > 0:
		nPos1 = NukeComponents.nukeMissleObj.getPosition()
		nPos2 = (nPos1[0], nPos1[1] + 0.5, nPos1[2])
		NukeComponents.nukeMissleObj.setPosition(nPos2)
	else:
		NukeComponents.animateNuke_Timer1.destroy()
		NukeComponents.animateNuke_Timer1 = None

#nuke missle animation, starts moving missle 2 seconds after anmateNuke1 has started moving the missle
def animateNuke2(data):
	NukeComponents.animateTime2 -= 1
	if NukeComponents.animateTime2 < 160 and NukeComponents.animateTime2 > 0:
		nPos1 = NukeComponents.nukeMissleObj.getPosition()
		nPos2 = (nPos1[0], nPos1[1] + 0.5, nPos1[2])
		NukeComponents.nukeMissleObj.setPosition(nPos2)
	else:
		NukeComponents.animateNuke_Timer2.destroy()
		NukeComponents.animateNuke_Timer2 = None


#nuke missle adnimation, starts moving missle 2 seconds after adnimateNuke2 has started moving the missle
def animateNuke3(data):
	NukeComponents.animateTime3 -= 1
	if NukeComponents.animateTime3 < 140 and NukeComponents.animateTime3 > 0:
		nPos1 = NukeComponents.nukeMissleObj.getPosition()
		nPos2 = (nPos1[0], nPos1[1] + 0.5, nPos1[2])
		NukeComponents.nukeMissleObj.setPosition(nPos2)
	else:
		NukeComponents.animateNuke_Timer3.destroy()
		NukeComponents.animateNuke_Timer3 = None		

#sounds need 'repairing' after the sound is played
def Nuke_RepairSounds(obj):
	try:
		obj.setDamage(1)
		obj.timer.destroy()
		obj.timer = None
	except:
		pass

#sets damage to the nuke sound objects to play their sounds
def playSounds(obj):
	try:
		obj.setDamage(-1)
		obj.timer = bf2.Timer(Nuke_RepairSounds, 0.2, 1,obj)
	except:
		pass
	
#find the nuke object and the nuke missle
def getSetNuke():
	objects = bf2.objectManager.getObjectsOfType('dice.hfe.world.ObjectTemplate.PlayerControlObject')
	for obj in objects:
		if obj.templateName == 'maz7310_rt2pm':
			NukeComponents.nuke_PCO = obj
			findNukeMissle(obj) #find the nuke missle in the nuke launcher
			#store the position of the nuke, needed later for the animation system
			NukeComponents.nukeMissleObjResetPos = NukeComponents.nukeMissleObj.getPosition()

#recursuve method to find the nuke missle in the nuke launcher object
def findNukeMissle(obj):
	# Get children of nuke vehicle and find missile object
	children = obj.getChildren() 
	for child in children: 
		if child.templateName == 'maz7310_rt2pm_missile':
			NukeComponents.nukeMissleObj = child

#scan all of the player control objects on the map and find the sound objects
def getSetNukeSounds():
	NukeSounds.nuke_state1_sound_played = 0
	NukeSounds.nuke_state2_sound_played = 0
	NukeSounds.nuke_state3_sound_played = 0
	NukeSounds.nuke_missle_sound_played = 0
	objects = bf2.objectManager.getObjectsOfType('dice.hfe.world.ObjectTemplate.PlayerControlObject')
	for obj in objects:
		#nuke feulling begun
		if obj.templateName == 'nuke_state1_sound':
			NukeSounds.nuke_state1_sound = obj
			
		#nuke feulling complete
		elif obj.templateName == 'nuke_state2_sound':
			NukeSounds.nuke_state2_sound = obj
			
		#nuke acquiring target guidance
		elif obj.templateName == 'nuke_state3_sound':
			NukeSounds.nuke_state3_sound = obj
			
		#nuke has been destroyed
		elif obj.templateName == 'nuke_destroyed_sound':
			NukeSounds.nuke_destroyed_sound = obj
			
		#nuke blast-ff sound
		elif obj.templateName == 'missle_sound':
			NukeSounds.nuke_missle_sound = obj

def onEnterVehicle(player, vehicle, freeSoldier = False):
	if bf2.serverSettings.getGameMode() != "gpm_nuke": return

def onVehicleDestroyed(vehicle, attacker):
	if bf2.serverSettings.getGameMode() != "gpm_nuke": return
	if vehicle.templateName == NukeComponents.nuke_PCO.templateName:
		
		#if the nuke has already been launched, ignore everything
		if not NukeComponents.nuke_launched:
			
			#a friendly destoryed the nuke, punish him
			if attacker.getTeam() == 1:
				
				addScore(attacker, NukeComponents.tk_nuke_score, RPL)
				NukeComponents.nuke_tkd = 1
				NukeComponents.nuke_PCO = None
				host.rcon_invoke("game.sayAll \"" + "---------------Warning---------------"  + "\"")
				host.rcon_invoke("game.sayAll \"" + "."  + "\"")
				host.rcon_invoke("game.sayAll \"" + "Punishing " + attacker.getName() + " for TK'ing the nuke!"  + "\"")
				host.rcon_invoke("game.sayAll \"" + "."  + "\"")
				host.rcon_invoke("game.sayAll \"" + "-------------------------------------"  + "\"")

			#an enemy destroyed the nuke, reward him
			elif attacker.getTeam() == 2:
				
				addScore(attacker, NukeComponents.destroyed_nuke_score, RPL)
				NukeComponents.nuke_destroyed = 1
				NukeComponents.nuke_PCO = None
				host.rcon_invoke("game.sayAll \"" + "---------------Warning---------------"  + "\"")
				host.rcon_invoke("game.sayAll \"" + "."  + "\"")
				host.rcon_invoke("game.sayAll \"" + attacker.getName() + " has destroyed the nuke!"  + "\"")
				host.rcon_invoke("game.sayAll \"" + "."  + "\"")
				host.rcon_invoke("game.sayAll \"" + "-------------------------------------"  + "\"")

			#play the sound for the nuke being destroyed, if it can't play it, bypass it so the method completes
			playSounds(NukeSounds.nuke_destroyed_sound)
				
			bf2.gameLogic.setTickets(1, 200)
			updateTicketLoss()

#if the time limit is reached, the defending team wins
def onTimeLimitReached(value):
	if bf2.serverSettings.getGameMode() != "gpm_nuke": return
	nukeCleanUp()
	winner = 1
	victoryType = 3
	host.sgl_endGame(winner, victoryType)

# update ticket system
def updateTicketLoss():
	if NukeComponents.nuke_initialized == 1:
		# update ticket loss
		ticketLossPerSecTeam1 = calcTicketLossForTeam(1)
		ticketLossPerSecTeam2 = calcTicketLossForTeam(2)
		
		bf2.gameLogic.setTicketChangePerSecond(1, -ticketLossPerSecTeam1)
		bf2.gameLogic.setTicketChangePerSecond(2, -ticketLossPerSecTeam2)
		
		if NukeComponents.nuke_tkd or NukeComponents.nuke_destroyed:
			bf2.gameLogic.setTicketChangePerSecond(1, -20.0)
			bf2.gameLogic.setTicketChangePerSecond(2, 0)
			
		elif NukeComponents.nuke_launched:
			bf2.gameLogic.setTicketChangePerSecond(2, -5.0)
	else:
		return

def calcTicketLossForTeam(team):
	if team == 1:
		return 0
	else:
		ticketLossPerSecond = 0.1 #0.1 tickets lost per second = 1 ticket every 10 secs = 6 per min
		return ticketLossPerSecond
	
def onTicketLimitReached(team, limitId):
	if bf2.serverSettings.getGameMode() != "gpm_nuke": return
	if (limitId == -1):
		if (team == 2):
			winner = 1
		else:
			winner = 2
		if NukeComponents.nuke_launched:
			try:
				NukeComponents.nukeMissleObj.setPosition(NukeComponents.nukeMissleObjResetPos)
			except:
				pass
			try:
				NukeComponents.nuke_PCO.setDamage(-1)
			except:
				pass
			
		nukeCleanUp()	
		bf2.gameLogic.setTicketState(1, 0)
		bf2.gameLogic.setTicketState(2, 0)
	
		host.sgl_endGame(winner, 3)
	else:
		updateTicketWarning(team, limitId)
		
def updateTicketWarning(team, limitId):
	oldTicketState = bf2.gameLogic.getTicketState(team)
	newTicketState = 0
	
	if (oldTicketState >= 10):
		newTicketState = 10		

	if (limitId == -2):
		newTicketState = 10
	
	elif (limitId == 2):
		newTicketState = 0		

	elif (limitId == -3):
		newTicketState += 2

	elif (limitId == -4):
		newTicketState += 1

	if (oldTicketState != newTicketState):
		bf2.gameLogic.setTicketState(team, newTicketState)
		
	
def onPlayerDeathCQ(victim, vehicle):	
	if bf2.serverSettings.getGameMode() != "gpm_nuke": return
	# check if it was the last player
	foundLivingPlayer = False
	for p in bf2.playerManager.getPlayers():
		if p != victim and p.getTeam() == victim.getTeam() and p.isAlive():
			foundLivingPlayer = True
	
	if not foundLivingPlayer:
		updateTicketLoss()
	
#when a player is killed, also used tor scoring a nuke defense
def onPlayerKilledCQ(victim, attacker, weapon, assists, object):
	if bf2.serverSettings.getGameMode() != "gpm_nuke": return
	if not victim: 
		return
	
	victim.killed = True
	#if this player was in the radius of the nuke, give the attacker 4 points, and show that he defended the nuke once
	if not NukeComponents.nuke_launched:
		if isStoredNukeAttacker(victim):
			if victim.getTeam() == attacker.getTeam():
				pass # he tk'd someone, give him nothing
			else:
				host.rcon_invoke("game.sayAll \"" + attacker.getName() + " has defended the nuke from " + victim.getName() + "\"")
				attacker.score.cpDefends += 1
				addScore(attacker, 4, RPL)
		
def onPlayerSpawn(player, soldier):
	if bf2.serverSettings.getGameMode() != "gpm_nuke": return
	player.killed = False
	
	
	#when the player spawns see if the nuke system has been initialized already
	if not NukeComponents.nuke_initialized:
		#print 'nuke not init'
		NukeComponents.nuke_initialized = 1
		try:
			#find and store the sounds and nuke objects
			getSetNuke()
			getSetNukeSounds()
		except:
			pass
		#set the scoring trigger to the nuke object
		NukeComponents.nuke_trigger_id = bf2.triggerManager.createRadiusTrigger(NukeComponents.nuke_PCO, nukeTrigger, '<<PCO>>', 20, 1)
		
		#timer to clear players no longer in the radius of the nuke
		NukeComponents.nukeAttackersUpdater_Timer = bf2.Timer(clearOldAttackers, 0, 1)
		NukeComponents.nukeAttackersUpdater_Timer.setRecurring(2.0)
		
		#timer to display the status of the nuke, and play the required sounds
		NukeComponents.nukeStatusUpdater_Timer = bf2.Timer(nukeStatusUpdater, 0, 1)
		NukeComponents.nukeStatusUpdater_Timer.setRecurring(1.0)
		team2Tickets = ((NUKE_TIME_LIMIT * 60) / 10) #team 2 tickets will be the timelimit / 10
		bf2.gameLogic.setTickets(2, team2Tickets)
		
		NukeComponents.nukeTimeLimit = team2Tickets
		updateTicketLoss()
		setTicketLimits()

def getVectorDistance(pos1, pos2):
	diffVec = [0.0, 0.0, 0.0]
	diffVec[0] = math.fabs(pos1[0] - pos2[0])
	diffVec[1] = math.fabs(pos1[1] - pos2[1])
	diffVec[2] = math.fabs(pos1[2] - pos2[2])
	 
	return math.sqrt(diffVec[0] * diffVec[0] + diffVec[1] * diffVec[1] + diffVec[2] * diffVec[2])
		
