#
# MAD Nuke Mod
# CvMADNukeEventManager
# 

from CvPythonExtensions import *

import CvUtil
import CvEventManager
import sys
import PyHelpers
import CvConfigParser
import math
import time
import CvMADNukesManager
import CvScreenEnums 

# rucivfan_profile_help rph[
#import profile_help
# ]rucivfan_profile_help rph

gc = CyGlobalContext()	


PyPlayer = PyHelpers.PyPlayer
PyGame = PyHelpers.PyGame()

localText = CyTranslator()

# Change this value to false if nuke strikes should not completely destroy 
# cities.
# Default value is true
g_bNukesDestroyCities = false

# Change this value to false if nuke strikes should not change the plot that is
# hit into an ocean plot. When this value is set to true then the 
# g_bNukesDestroyUnits, g_bNukeStrikesLeaveRuins and g_bNukesDestroyCities are 
# automatically assumed to be true.
# Default value is true
g_bNukesAlterLandscape = false

# Change this value to change what type terrain the nuked plot will turn to if
# g_bNukesAlterLandscape is set to true. The current possible values for this
# option are:
# - TERRAIN_GRASS
# - TERRAIN_PLAINS
# - TERRAIN_DESERT
# - TERRAIN_TUNDRA
# - TERRAIN_SNOW
# - TERRAIN_COAST
# - TERRAIN_OCEAN
# - TERRAIN_PEAK
# - TERRAIN_HILL # Default value is "TERRAIN_COAST"
g_strAlterLandscapeTerrain = "TERRAIN_COAST"

# Change this value to false if the vanilla unit nuke strike survival code
# should be used instead of completely wiping them out when a nuke strikes a
# city.
# Default value is true
g_bNukesDestroyUnits = false

# Change this value to false if nuke strikes should leave city ruins.
# Default value is true
g_bNukeStrikesDestroyRuins = false

# Change this value to alter how other players are drawn into a war when a 
# mad-enabled nuke is launched.  Current posible values are:
# - 0:  Every player in the target plot when a nuke is launched automatically declares
#	   war on the agressor (as TheLopez's original M.A.D Nukes Mod)
# - 1:  If launching a nuke would draw any players other than the owner of the target
#	plot into a war at launch time the launch fails (as Vanilla)
# - 2:  Only the owner of the target plot declares war on the agressor.  No other players
#	   are drawn into the war regardless of unit positions. (new)
# Default value is 0
g_iMADSafeNukesMode = 0

g_plotTypeMapping = {
						"TERRAIN_GRASS" : PlotTypes.PLOT_LAND,
						"TERRAIN_PLAINS" : PlotTypes.PLOT_LAND, 
						"TERRAIN_DESERT" : PlotTypes.PLOT_LAND,
						"TERRAIN_TUNDRA" : PlotTypes.PLOT_LAND,
						"TERRAIN_SNOW" : PlotTypes.PLOT_LAND,
						"TERRAIN_COAST" : PlotTypes.PLOT_OCEAN,
						"TERRAIN_OCEAN" : PlotTypes.PLOT_OCEAN,
						"TERRAIN_PEAK" : PlotTypes.PLOT_PEAK,
						"TERRAIN_HILL" : PlotTypes.PLOT_HILLS
					}

def loadConfigurationValues():
	global g_bNukesDestroyCities
	global g_bNukesAlterLandscape
	global g_strAlterLandscapeTerrain
	global g_bNukesDestroyUnits
	global g_bNukeStrikesDestroyRuins
	global g_iNetMessage_launch
		
	gc.getGame().setMADEnabled(gc.getGame().isOption(GameOptionTypes.GAMEOPTION_COLD_WAR))
	gc.getGame().setMADSafeNukesEnabled(g_iMADSafeNukesMode)


class CvMADNukesEventManager:

	madNukesManager = None
	iNetMessage_launch = 0
	
	def __init__(self, eventManager):
		self.EventKeyDown = 6
		self.eventManager = eventManager

		eventManager.addEventHandler("kbdEvent",self.onKbdEvent)
		eventManager.addEventHandler("ModNetMessage", self.onModNetMessage)
		eventManager.addEventHandler("MADNukeExplosion", self.onMADNukeExplosion)

		loadConfigurationValues()
		
		iNetMessage_launch = 0
		self.madNukesManager = CvMADNukesManager.CvMADNukesManager(CvScreenEnums.MAD_NUKES_MANAGER)		
		
		
	# Multiplayer compatability
	def onModNetMessage(self, argsList):
		'onModNetMessage - called whenever CyMessageControl.sendModNetMessage() is called'
		
		iData1, iData2, iData3, iData4, iData5 = argsList
		
		iMessage = iData1
		
		if (iMessage == self.iNetMessage_launch):
			#CyMessageControl.sendModNetMessage(self.iNetMessage_launch, objUnit.getID(), objUnit.getOwner(), 0, 0)
			iUnitID = iData2
			iPlayerID = iData3
			
			objPlayer = gc.getPlayer(iPlayerID)
			
			if (objPlayer == None or objPlayer.isNone() == True):
				return
			
			objUnit = objPlayer.getUnit(iUnitID)
			
			if (objUnit == None or objUnit.isNone() == True):
				return
			
			if (objUnit.launchMADNuke() == False):
				CyInterface().addImmediateMessage("MAD Debug: objUnit.launch() failed!","")
			
			# Update the outgoing warheads screen
			self.madNukesManager.updateOutgoingWarheads()
				
		
	# This method handles the key input and will bring up the nuclear arsenel 
	# manager screen if the player presses the 'ALT-N' key.
	# rucivfan_profile_help rph[
	#@profile_help.profile_func(output_dir="test",  mode="a", strip_dirs=True, sort_by="time") 
	# ]rucivfan_profile_help rph
	def onKbdEvent(self, argsList):
		'keypress handler - return 1 if the event was consumed'
		eventType,key,mx,my,px,py = argsList

		loadConfigurationValues();
		theKey=int(key)

		if ( eventType == self.EventKeyDown and theKey == int(InputTypes.KB_N) and self.eventManager.bAlt ):
			if ( gc.getGame().isMADEnabled() == True ):
				if (gc.getPlayer(gc.getGame().getActivePlayer()).isMADStrategicCommandEnabled() == True):
					#MAD_TODO: There HAS to be a better way to pass this variable than this...
					self.madNukesManager.interfaceScreen(self.iNetMessage_launch)
										#CvScreensInterface.showMADWatchInfoPane()
				

	def onMADNukeExplosion(self, argsList):
		'MADNukeExplosion'
		genericList = argsList[0][0]
		pPlot, pPlayer = genericList

		loadConfigurationValues()

		if ( gc.getGame().isMADEnabled() == True ):
			self.handleNukeDetonation(pPlot, pPlayer)

	# This method handles the actual detonation of a nuke. It will basically
	# go through a player's enemy list. For each enemy the method will go
	# through each one of their cities and see if the number of plots with 
	# fallout around a city cross a threshold and perform the appropriate
	# action.
	def handleNukeDetonation(self, objPlot, objPlayer):
		# Get the owner of the nuke.	
		iPlayer = objPlayer
		
		# Return immediately if MAD is not enabled
		if ( gc.getGame().isMADEnabled() == False ):
			return
		
		# Return immediately if no plot was hit
		if(objPlot == None or objPlot.isNone()):
			return
		
		# Call devistatePlot if this is not a city
		# and g_bNukesAlterLandscape is True
		if(not objPlot.isCity()):
			if(g_bNukesAlterLandscape):
				self.devistatePlot(objPlot, iPlayer)
				
		# Return immediately if the plot is not a city plot
		if(not objPlot.isCity()):
			return
					
		objCity = objPlot.getPlotCity()
		
		# If the number of plots with fallout exceed the fallout 
		# threshold then perform the appropriate action
		if(self.checkFalloutPastThreshold(objCity)):
			# Get the plot the city is on
			objCityPlot = objCity.plot()
			if(g_bNukesAlterLandscape):
				objCity.kill()
				self.devistatePlot(objCityPlot, iPlayer)

			else:
				# Destroy the route that was on the same plot as the city
				objCityPlot.setRouteType(gc.getInfoTypeForString("NO_ROUTE"))
							
				# Go through all of the units in the city and kill them off 
				# if the mod is configured to do so
				if(g_bNukesDestroyUnits):
					for i in range(objCityPlot.getNumUnits()):
						objCityPlot.getUnit(i).kill(true,iPlayer)
						
				# Kill the city	if mod is configured to do so
				if(g_bNukesDestroyCities):
					objCity.kill()
						
				# Destroy the improvement that was on the same plot as the 
				# city if mod is configured to do so
				if(g_bNukeStrikesDestroyRuins):
					objCityPlot.setImprovementType(gc.getInfoTypeForString("NO_IMPROVEMENTS"))

		# Refresh and redraw the interface
		CyInterface().setDirty(InterfaceDirtyBits.MinimapSection_DIRTY_BIT, true)
		CyInterface().setDirty(InterfaceDirtyBits.MiscButtons_DIRTY_BIT, true)
		
	# Returns true if the number of fallout plots in a city's radius
	# is greater than iTotalNonWaterPlots/2, otherwise returns false
	def checkFalloutPastThreshold(self, objCity):
		falloutPlotList = []
		iTotalNonWaterPlots = -1
		
		# Return immediately if MAD is not enabled
		if ( gc.getGame().isMADEnabled() == False ):
			return
				
		# Go through plots adjacent to the city
		for i in range(9):
				
			# Save the plot if it cannot be worked by the city and it 
			# contains fallout
			if(not objCity.canWork(objCity.getCityIndexPlot(i)) and objCity.getCityIndexPlot(i).getFeatureType() == gc.getInfoTypeForString("FEATURE_FALLOUT")):
				falloutPlotList.append(objCity.getCityIndexPlot(i))

			# Increment the iTotalNonWaterPlots counter if the plot is 
			# not water
			if(not objCity.getCityIndexPlot(i).isWater()):
				iTotalNonWaterPlots+=1
				
		if (len(falloutPlotList) >= iTotalNonWaterPlots/2):
			return True
			
		return False


	# Completely devistates the plot that the city is on and turns it into an
	# ocean plot.
	def devistatePlot(self, objPlot, iPlayer):		
		# Return immediately if the plot passed in is invalid
		if(objPlot == None or objPlot.isNone()):
			return
		
		# Return an empty list immediately if the player passed in is invalid
		if(iPlayer < 0):
			return 

		objPlayer = gc.getPlayer(iPlayer)

		# Return an empty list immediately if the player passed in is invalid
		if(objPlayer == None or objPlayer.isNone()):
			return 

		# If g_bNukesDestroyUnits is True then
		# go through all of the units in the city and kill them off
		if (g_bNukesDestroyUnits == True):
			for i in range(objPlot.getNumUnits()):
				objPlot.getUnit(i).kill(true,iPlayer)

		# Destroy the improvement that was on the same plot
		# TODO: The following returns an error.  This hack should work
		#	   for now, but this should be addressed.
		#objPlot.setImprovementType(gc.getInfoTypeForString("NO_IMPROVEMENT"))
		objPlot.setImprovementType(-1)

		# Destroy the route that was on the same plot
		# TODO: The following returns an error.  This hack should work
		#	   for now, but this should be addressed.
		#objPlot.setRouteType(gc.getInfoTypeForString("NO_ROUTE"))
		objPlot.setRouteType(-1)
		
		# Change the plot type of the plot where the city was
		objPlot.setPlotType(PlotTypes(g_plotTypeMapping[g_strAlterLandscapeTerrain]), True, True)
		
		# Change the terrain type of the plot where the city was
		objPlot.setTerrainType(gc.getInfoTypeForString(g_strAlterLandscapeTerrain), True, True)
			
		# Refresh and redraw the interface
		CyInterface().setDirty(InterfaceDirtyBits.MinimapSection_DIRTY_BIT, true)
		CyInterface().setDirty(InterfaceDirtyBits.MiscButtons_DIRTY_BIT, true)			
	
	
