--------------------------------------------------------------

-- L_NT_FACTION_SPY_SERVER.lua

-- Catch and process Spy Mission events, including formatting
-- the dialogue script data.
-- Created abeechler ... 4/8/11

--------------------------------------------------------------

------------------------------------------------------
-- L_NT_FACTION_SPY_SERVER script configData SpyCinematic requirements:
--
-- Cinematics are to be chained in Happy Flower using a naming convention
-- where transtions can equal dialogue displays.  Structure cinematics with
--
-- [[ 'cinema_core_name' AND/OR '_' +  'number' ]]
-- 'cinema_core_name' = A shared name leading each chained cinematic tag, for spy dialogue to play
--                      this MUST be the same across all chained cinematics initiated by the spyObject 
--                      SpyCinematic config data.
-- '_' +  'number'    = If the core name is followed by a number in this structure, then when the cinematic 
--                      begins a chat bubble will be displayed with dialogue corresponding to the dialogue 
--                      table location defined by the spy object.  
--
-- NOTE: Cinemas can be played throughout the chain by not providing '_' +  'number' data
------------------------------------------------------

----------------------------------------------------------------
-- Define empty tables that will be set from the 
-- spy object specific script
----------------------------------------------------------------
local SpyDialogueTable = {}
local SpyDialogueObjTable = {}
local SpyDataTable = {}
local SpyDialogueTableLength = 0

----------------------------------------------------------------
-- Variables passed from the object specific script that are used throughout the base script
----------------------------------------------------------------
function setGameVariables(self, passedSpyDialogueTable, passedSpyDialogueObjTable, passedSpyDataTable, passedSpyProxRadius)

	SpyDialogueTable = passedSpyDialogueTable
	SpyDialogueObjTable = passedSpyDialogueObjTable
	SpyDataTable = passedSpyDataTable
	
	SpyDialogueTableLength = table.maxn(SpyDialogueTable)
	
	-- If we have a valid SpyProxRadius, set a proximity radius sense trigger
	if(passedSpyProxRadius) then
	    -- Establish a sense radius for spy missions
        self:SetProximityRadius{radius = passedSpyProxRadius, name = "SpyDistance", collisionGroup = 1} 
	end
	
end

----------------------------------------------
-- Check to see if we have a valid spy
-- returns: true or false depending on spy validity
----------------------------------------------
function spyCheck(checkObject)

	-- Grab the necessary precondition objects from the passed in check table
	-- if we are missing any relevant data, return a false check result
    local spyFlagID = SpyDataTable.spyFlagID
    local spyItemID = SpyDataTable.spyItemID
    local spyMissionID = SpyDataTable.spyMissionID
	if((not spyFlagID) or (not spyItemID) or (not spyMissionID)) then return false end
	
	-- Is the checkObject on the right mission?
    -- Is the checkObject wearing the correct spy gear?
    -- Has the checkObject's flag been set?
    if((checkObject:GetMissionState{missionID = spyMissionID}.missionState ~= 2) or
       (not checkObject:GetIsItemEquippedByLOT{ItemID = spyItemID}.bEquipped) or
       (checkObject:GetFlag{iFlagID = flag}.bFlag)) then
        -- Immediately return false on 'true' to any of the queries
        return false
	else
		-- We have found a spy!
		return true
    end
	
end 

----------------------------------------------
-- Initiate the spy cinematic, and process dialog events in time
-- with the camera transitions
----------------------------------------------
function  playSpyDialogue(self, spyDialogueCinematic, spyObjID, senseObjID)
    
    local senseObj = GAMEOBJ:GetObjectByID(senseObjID)
	
    -- Stun lock the spy
	spyObjID:SetStunned{StateChangeType = "PUSH",
							bCantMove = true,
						    bCantTurn = true,
					      bCantAttack = true,
					     bCantUseItem = true,
						   bCantEquip = true,
					    bCantInteract = true}
						
	-- Initiate the dialogue cinematic
	spyObjID:PlayCinematic{pathName = spyDialogueCinematic, lockPlayer = false, bSendServerNotify = true}
	
	local cineName = split(spyDialogueCinematic, "_")
	local cineRoot = cineName[1] or false
	self:SetVar("CinematicRootName", cineRoot)
	
    -- Request for notification upon cinematic conclusion for dialogue event processing
	self:SendLuaNotificationRequest{requestTarget = spyObjID, messageName = "CinematicUpdate"}
	
end

----------------------------------------------
-- Catch and process proximity awareness events for spies
----------------------------------------------
function onProximityUpdate(self, msg)
    baseOnProximityUpdate(self, msg)
end

function baseOnProximityUpdate(self, msg)

    local msgName = msg.name
    local proxObjID = msg.objId
    local senseObjID = msg.ownerId

    -- Is this the Spy Distance?
    if((msgName == "SpyDistance")  and (msg.status == "ENTER"))then    
		
	    -- proxObjID is in spy distance - are they a valid spy object?
		local bIsSpyObj = spyCheck(proxObjID)
			
		if(bIsSpyObj)then
			-- We have a valid spy, check for a valid spy dialogue cinematic
			local spyDialogueCinematic = self:GetVar("SpyCinematic") or false
			if(spyDialogueCinematic) then
				-- We have a valid spy dialogue cinematic
				playSpyDialogue(self, spyDialogueCinematic, proxObjID, senseObjID)
			end
		end
    end
	
end

----------------------------------------------------------------
-- Server notification of cinematic events firing
----------------------------------------------------------------
function notifyCinematicUpdate(self, player, msg)
    baseNotifyCinematicUpdate(self, player, msg)
end

function baseNotifyCinematicUpdate(self, player, msg)

	local eventType = msg.event
	
	local cineRoot = self:GetVar("CinematicRootName")
	
	local tPathName = split(msg.pathName, "_")
	local tPathRoot = tPathName[1] or false
	local tPathExtension = tPathName[2] or 0
	local dialogueIndex = tonumber(tPathExtension)
	
	if((tPathRoot) and (tPathRoot == cineRoot)) then
		if(eventType == "STARTED") then
			-- We are initiating a camera transition to a dialogue display segment
			-- We have a valid indexed cinematic cam sequence starting
			if((dialogueIndex) and (dialogueIndex > 0) and (dialogueIndex <= SpyDialogueTableLength)) then
				-- Send the spy dialogue line for display
				self:NotifyClientObject{name ="displayDialogueLine", paramStr = SpyDialogueTable[dialogueIndex].dialogueToken, paramObj = SpyDialogueObjTable[SpyDialogueTable[dialogueIndex].convoID]}
			end
			
		elseif(eventType == "ENDED") then
		    if((dialogueIndex) and (dialogueIndex >= SpyDialogueTableLength)) then
			    -- We are ending a camera sequence that marks the end of a dialogue camera chain:
			    -- Unlock player
			    player:SetStunned{ StateChangeType = "POP",
									     bCantMove = true,
									     bCantTurn = true,
								       bCantAttack = true,
								      bCantUseItem = true,
								        bCantEquip = true,
								     bCantInteract = true}
								     
				-- Prepare for mission completion by marking the associated flag
				player:SetFlag{iFlagID = SpyDataTable.spyFlagID, bFlag = true}
				
				-- Unsubscribe from the scipt notification
		        self:SendLuaNotificationCancel{requestTarget = player, messageName = "CinematicUpdate"}
		    end
		end
	end
	
end

----------------------------------------------
-- Utility function usable by spy objects to construct their dialogue objects table
-- returns: an organized table of dialogue participants
----------------------------------------------
function buildSpyDialogueObjTable(spyObject, secondaryObjGroupTable)
	
	-- Declare the spy dialogue object table
	local spyDialogueObjectTable = {}
	
	-- spyObject is always the first element - it is the primary dialogue object
	table.insert(spyDialogueObjectTable, spyObject)
	
	-- Iterate through the secondaryObjGroupTable, if one exists,
	-- and retrieve the first object from each group for addition to
	-- the dialogue object table
	if(secondaryObjGroupTable) then
		-- We have received a table
		for k, groupNom in ipairs(secondaryObjGroupTable) do
			-- Retrieve the first object from each group and add it to the spyDialogueObjectTable
			local dialogObject = returnFirstGroupObject(spyObject, groupNom)
			table.insert(spyDialogueObjectTable, dialogObject)
		end
	end
	
	-- Return the compiled table of dialog objects
	return spyDialogueObjectTable
	
end

----------------------------------------------
-- Utility function to return a valid object from a group
----------------------------------------------
function returnFirstGroupObject(self, groupName)

    if(groupName) then
	    -- We have a target group to proces
	    local groupObjs = self:GetObjectsInGroup{group = groupName, ignoreSpawners = true}.objects
			
	    -- Iterate through the group and catch the first valid object
	    for k,obj in ipairs(groupObjs) do
		    if obj:Exists() then
			    return obj
		    end
	    end
	end
	
	-- Catch for missing objects or empty group
	return false
	
end

----------------------------------------------
-- Utility function to process tokenized strings
----------------------------------------------
function split(str, pat)
    local t = {}
    -- Creates a table of strings based on the passed in pattern   
    string.gsub(str .. pat, "(.-)" .. pat, function(result) table.insert(t, result) end)

    return t
end
