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

-- L_CASTLE_DEFENSE_TST_SERVER.lua

-- Server side test script for Castle Defense prototyping.
-- created abeechler ... 8/5/11

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

-- 13925 Castle Corner Inner 2 9499 3 4210
-- 13926 Castle Gate 2 9503 3 4214
-- 13927 Castle Tower 2 9507 3 4218
-- 13928 Castle Wall Bridge 2 9508 3 4219
-- 13929 Castle Wall Straight 2 9514 3 4225
-- 13930 Castle Wall T 2 9517 3 4228
-- 13931 Castle Wall Tower With Top
-- 13932 Castle Wall Widening 2 9520 3 4231

-- 14202 Castle Wall Tower With Top QB
-- 14206 Castle Corner Inner QB
-- 14207 Castle Gate QB
-- 14208 Castle Tower QB
-- 14209 Castle Wall Bridge QB
-- 14210 Castle Wall Straight QB
-- 14211 Castle Wall T QB
-- 14213 Castle Wall Widening QB

-- 16131 Center Object

--if(child:GetLOT().objtemplate == gCenterLOT) then
--    if child:GetID() == gCenterIDs.center1:GetID() then
--        print("team 2 wins")
--        message_string = "TEAM 2 WINS!!"
--        bDisplayMessage = true
--    elseif child:GetID() == gCenterIDs.center2:GetID() then
--        print("team 1 wins")
--        message_string = "TEAM 1 WINS!!"
--        bDisplayMessage = true
--    end
--end

--------------------------------------------------------------
-- Includes
--------------------------------------------------------------
require('ai/ACT/L_ACT_GENERIC_ACTIVITY_MGR')


-- Center piece lot number
local gCenterLOT = 16131

local gCenterSpawned = 0

local gGameOver = false

-- Center piece object IDs
local gCenterIDs =
{
	center1 = 0,
	center2 = 0
}

local HealthCBLOT = 16206
local ShieldCBLOT = 16207
local DragonCBLOT = 16208
local AttackCBLOT = 16209

local ShieldCBCount = 0
local DragonCBCount = 0
local AttackCBCount = 0
local HealthCBCount = 0

local team1Score = 0
local team2Score = 0

--//////////////////////////////////////////////////////////////////////////////////
-- User Config local variables
local gConstants = 
{
    acceptedDelay = 2,          -- how long to wait after all the players have accepted before starting the game.
    startDelay = 2,             -- how long to wait after all the players have accepted before starting the game.
    returnZone = 89,          -- map number the player will return to on exit
    returnLoc = { x = 0, y = 0, z = 0 } -- {x,y,z} location that the player will be teleported to in the returnZone on exit
}


--============================================================
-- Script only local variables
local gGamestate =
{
    tPlayers = {},          -- players who have entered the game
    tWaitingPlayers = {},   -- players who haven't accepted yet
    iNumberOfPlayers = 0	-- number of players given from ZoneLoadedInfo
}


----------------------------------------------------------------
-- Received when the script is loaded
----------------------------------------------------------------
function onStartup(self)
    -- Initialize the pseudo random number generator and return 
    math.randomseed(os.time())
    self:SetVar('playersAccepted', 0)
    self:SetVar('playersReady', false)
    --self:MiniGameSetParameters{numTeams = 1, playersPerTeam = 4}
     
end

function onChildRemoved(self, msg)

	local child = msg.childID
	
	local message_string = "test"
	local bDisplayMessage = false
	
	if(gGameOver == true) then return end
	
	if(bDisplayMessage == true) then
		gGameOver = true
		for i = 1, table.maxn(gGamestate.tPlayers) do
			local playerID = GAMEOBJ:GetObjectByID(gGamestate.tPlayers[i]) 
			playerID:DisplayMessageBox{bShow = true, 
											imageID = 1, 
											text = message_string, 
											callbackClient = self, 
											identifier = "score thing"}
		end
	end
	
end


----------------------------------------------------------------
-- Player is fully loaded and has completed the load handshake process.
----------------------------------------------------------------
function onPlayerReady(self, msg)
     if not self:GetVar('SurvivalStartupComplete') then
        self:SetVar('SurvivalStartupComplete', true)
    end
end

----------------------------------------------------------------
-- Player has loaded into the map
----------------------------------------------------------------
function onPlayerLoaded(self, msg)	
	
   -- adding the players to the gGamestate tables
    table.insert(gGamestate.tPlayers, msg.playerID:GetID())    
    table.insert(gGamestate.tWaitingPlayers, msg.playerID:GetID())    
        
    -- adding player to mini game team
    --self:MiniGameAddPlayer{playerID = msg.playerID}    
    --self:MiniGameSetTeam{playerID = msg.playerID, teamID = 1}

    -- setting up player ui
    self:SetNetworkVar('Define_Player_To_UI', msg.playerID:GetID())
    
    -- freeze the player movement/controls
    if not self:GetNetworkVar('wavesStarted') then
        -- updating the scoreboard for the new players
        
        self:SetNetworkVar('Update_ScoreBoard_Players', gGamestate.tPlayers)
        
        self:SetNetworkVar('Show_ScoreBoard', true)
    end
        
    -- TODO set this up to work with teams
    -- move players to correct spawn locations
    SetPlayerSpawnPoints(self)
    
    msg.playerID:PlayerSetCameraCyclingMode{ cyclingMode = ALLOW_CYCLE_TEAMMATES, bAllowCyclingWhileDeadOnly = true }  
    
    if not self:GetNetworkVar('wavesStarted') then
        playerConfirmed(self) 
    else
        local playerID = msg.playerID
        
        if not playerID then return end
        table.insert(gGamestate.tWaitingPlayers, v)
        UpdatePlayer(self, playerID)        
        --playerID:SetUserCtrlCompPause{bPaused = false}
        GetLeaderboardData(self, playerID, self:GetActivityID().activityID, 50)
        --set player stats to max
        playerID:SetHealth{health = playerID:GetMaxHealth{}.health}
        --print('max health = ' .. playerID:GetMaxHealth{}.health)
        playerID:SetArmor{armor = playerID:GetMaxArmor{}.armor}
        playerID:SetImagination{imagination = playerID:GetMaxImagination{}.imagination}
    end         
end

----------------------------------------------------------------
-- Player has exited the map
----------------------------------------------------------------
function onPlayerExit(self, msg)
   local playerNum = 0
    --print('player ' .. msg.playerID:GetName().name .. ' has exited')
        
    for i = 1, table.maxn(gGamestate.tPlayers) do
        if gGamestate.tPlayers[i] == msg.playerID:GetID() then
            playerNum = i
        end
    end
    
    if playerNum ~= 0 then
        table.remove(gGamestate.tPlayers, playerNum)
        -- set player to not auto-respawn
        msg.playerID:SetPlayerAllowedRespawn{dontPromptForRespawn=false}
    end    
                
    playerNum = 0
    
    for k,v in ipairs(gGamestate.tWaitingPlayers) do
        if msg.playerID:GetID() == v then
            playerNum = k
        end
    end
    
    if playerNum ~= 0 then    
        table.remove(gGamestate.tWaitingPlayers, playerNum)
    end
        
    if not self:GetNetworkVar('wavesStarted') then  
        playerConfirmed(self)
        --print('num of players left waiting: ' .. #gGamestate.tWaitingPlayers)
        
        if #gGamestate.tPlayers == 0 then return end
        
        if table.maxn(gGamestate.tWaitingPlayers) == 0 then           
            --print('All players have accepted')        
            ActivityTimerStopAllTimers(self)
            ActivityTimerStart(self, 'AllAcceptedDelay', 1, gConstants.startDelay) --(timerName, updateTime, stopTime)
        elseif #gGamestate.tPlayers > #gGamestate.tWaitingPlayers then
            if not self:GetVar('AcceptedDelayStarted') then
                self:SetVar('AcceptedDelayStarted', true)
                ActivityTimerStart(self, 'AcceptedDelay', 1, gConstants.acceptedDelay ) --(timerName, updateTime, stopTime)
            end
        end        
    else  
        UpdatePlayer(self, msg.playerID, true)
        
        if checkAllPlayersDead() then          
            GameOver(self, msg.playerID)
        end
    end
    
    SetActivityValue(self, msg.playerID, 1, 0)
    local numPlayers = self:GetNetworkVar('NumberOfPlayers')
    
    self:SetNetworkVar('NumberOfPlayers', numPlayers - 1)
end


function onZoneLoadedInfo(self, msg)
    self:SetNetworkVar('NumberOfPlayers', msg.maxPlayersSoft)
end


function playerConfirmed(self)
    local playersConfirmed = {}
    
    for k,v in ipairs(gGamestate.tPlayers) do
        local bPass = false
        for key,value in ipairs(gGamestate.tWaitingPlayers) do
            if value == v then
                bPass = true
            end
        end
        
        if not bPass then                        
            table.insert(playersConfirmed, v)
        end
    end
    
    self:SetNetworkVar('PlayerConfirm_ScoreBoard', playersConfirmed)
end


----------------------------------------------------------------
-- This is called when players hit the UI to exit or stop the game.
----------------------------------------------------------------
function baseMessageBoxRespond(self, msg, newMsg)
    if (msg.identifier == "RePlay" ) then 	
        PlayerAccepted(self, msg.sender)  
        playerConfirmed(self)
    elseif (msg.identifier == "Exit_Question" ) and msg.iButton == 1 then 		
        ResetStats(msg.sender)        
        self:SetNetworkVar('Exit_Waves', msg.sender:GetID())  
        -- send player to a specific location
        msg.sender:TransferToLastNonInstance{ playerID = msg.sender, bUseLastPosition = false, pos_x = 131.83, pos_y = 376, pos_z = -180.31, rot_x = 0, rot_y = -0.268720, rot_z = 0, rot_w = 0.963218}  
    end	
end


----------------------------------------------------------------
-- Custom function: Checks to see if all players have accepted,
-- if they have then the game is started.
----------------------------------------------------------------
function PlayerAccepted(self, playerID)
    local playerNum = 0
    
    print("PLAYER ACCEPTED")
    
    -- Figure out what player num this guy is.
    for k,v in ipairs(gGamestate.tWaitingPlayers) do
        if playerID:GetID() == v then
            playerNum = k
        end
    end
    
    
    if playerNum == 0 then return end
    
    -- TODO make sure this value is correct
    table.remove(gGamestate.tWaitingPlayers, playerNum)
    if table.maxn(gGamestate.tWaitingPlayers) == 0  and #gGamestate.tPlayers >= self:GetNetworkVar('NumberOfPlayers') then   
		print("accepted option 1")                
        ActivityTimerStopAllTimers(self)
        ActivityTimerStart(self, 'AllAcceptedDelay', 1, gConstants.startDelay) --(timerName, updateTime, stopTime)
    else
        if not self:GetVar('AcceptedDelayStarted') then
			print("accepted option 2")        
            self:SetVar('AcceptedDelayStarted', true)
            --ActivityTimerStart(self, 'AcceptedDelay', 1, gConstants.acceptedDelay) --(timerName, updateTime, stopTime)
            ActivityTimerStart(self, 'AcceptedDelay', 1, 2) --(timerName, updateTime, stopTime)
        end
    end
end


function ResetStats(playerID)
    -- set the player's imag, health and armor to full
    if playerID:Exists() then
        --set player stats to max
        playerID:SetHealth{health = playerID:GetMaxHealth{}.health}
        playerID:SetArmor{armor = playerID:GetMaxArmor{}.armor}
        playerID:SetImagination{imagination = playerID:GetMaxImagination{}.imagination}
    end
end


----------------------------------------------------------------
-- Custom function: Starts the game.
----------------------------------------------------------------
function StartWaves(self)    
    SetupActivity(self, 4)  
	self:ActivityStart()
    self:SetVar('playersReady', true)
    self:SetVar('AcceptedDelayStarted', false)
    gGamestate.tWaitingPlayers = {}
    
    for k,v in ipairs(gGamestate.tPlayers) do
        local playerID = GAMEOBJ:GetObjectByID(v)   
        
        if not playerID then return end
        
        table.insert(gGamestate.tWaitingPlayers, v)
        UpdatePlayer(self, playerID)        
        GetLeaderboardData(self, playerID, self:GetActivityID().activityID, 50)
        ResetStats(playerID)         
        
        if not self:GetVar('firstTimeDone') then
            --remove the activity cost from the player as they load into the map
            local takeCost = self:ChargeActivityCost{user = playerID}.bSucceeded
        end
    end
    
    self:SetVar('firstTimeDone', true)

    -- needed to get rewards -- taskType = DB name for series of achievments, target = activityID, value1 = what it will evaluate 
    local sTaskType = 'survival_time_team'
    
    if #gGamestate.tPlayers == 1 then
        sTaskType = 'survival_time_solo'
    end
    
    self:SetVar('missionType', sTaskType)
    
    --print('start smashables')
    self:SetNetworkVar('wavesStarted', true)
    self:SetNetworkVar('Start_Wave_Message', "Start!")  
    
    -- Start the score accumulation
    team1Score = 0
    team2Score = 0
    GAMEOBJ:GetTimer():AddTimerWithCancel(1, "printScore", self)

end

function onTimerDone(self,msg)

	if msg.name == "printScore" then
	    printScore(self)
	    GAMEOBJ:GetTimer():AddTimerWithCancel(1, "printScore", self)
	end
	
end

function printScore(self) 
    local team1Walls = self:GetVar("team1Walls") or {}
    local team2Walls = self:GetVar("team2Walls") or {}
    
    -- Get Team 1 Score
    team1Score = team1Score + calculateScoreTally(self, "Team1")
    -- Get Team 2 Score
    team2Score = team2Score + calculateScoreTally(self, "Team2")
    
    -- Print Out!
    print("------------------------------")
    print("TEAM                SCORE")
    print(string.format("%-20s %5d", "Team 1", team1Score))    
    print(string.format("%-20s %5d", "Team 2", team2Score))
    print("------------------------------")  
end

function calculateScoreTally(self, groupName)
    local scoreTally = 0
    local wallObjects = self:GetObjectsInGroup{group = groupName, ignoreSpawners = false}.objects
    
    for i, wall in ipairs(wallObjects) do
        if(wall:Exists() and wall:GetVar("built")) then
            scoreTally = scoreTally + 1
        end
    end
    
    return scoreTally
end

----------------------------------------------------------------
-- Custom function: Checks to see if all the players are dead,
-- then stops the game.
----------------------------------------------------------------
function checkAllPlayersDead()
    local deadPlayers = 0
    
    for k,v in ipairs(gGamestate.tPlayers) do
        local playerID = GAMEOBJ:GetObjectByID(v)
        
        if not playerID then return end
        
        if playerID:IsDead().bDead then
            deadPlayers = deadPlayers + 1
        end
    end
    
    if deadPlayers == table.maxn(gGamestate.tPlayers) then
        return true
    end
    
    return false
end

-- Create a SetPlayerSpawnPoints per team
function SetPlayerSpawnPoints(self)
    for k,v in ipairs(gGamestate.tPlayers) do           
        local playerID = GAMEOBJ:GetObjectByID(v)
        
        if not playerID then return end
        
        local spawnObj = self:GetObjectsInGroup{ group = 'P' .. k .. '_Spawn', ignoreSpawners = true }.objects[1]
        
        if spawnObj then
            local pos = spawnObj:GetPosition().pos
            local rot = spawnObj:GetRotation()
        
			print("SPAWN POINT FOUND")
            playerID:Teleport{pos = pos, x = rot.x, y = rot.y, z = rot.z, w = rot.w, bSetRotation = true}       
        end 
    end
end

----------------------------------------------------------------
-- Custom function: Happens when all players have died, this 
-- stops all running processes and resets gGamestate variables
----------------------------------------------------------------
function GameOver(self, player)  
    if not checkAllPlayersDead() then return end
    
    local finalTime = ActivityTimerGetCurrentTime(self, 'ClockTick')
    
    ActivityTimerStopAllTimers(self)
        
    for k,v in ipairs(gGamestate.tPlayers) do   
        local playerID = GAMEOBJ:GetObjectByID(v)
        
        if not playerID then return end
        
        local timeVar = GetActivityValue(self, playerID, 1)
        local scoreVar = GetActivityValue(self, playerID, 0)
        
        self:NotifyClientZoneObject{name = 'Update_ScoreBoard', paramObj = playerID, paramStr = tostring(scoreVar), param1 = timeVar}
        
        playerID:Resurrect()
          
        -- this is to have everyone get their own time at the end of the match
        StopActivity(self, playerID, scoreVar, timeVar)  
        
    end
        
    self:SetNetworkVar('wavesStarted', false)        
    
    SetPlayerSpawnPoints(self)
	
end

function basePlayerResurrected(self, msg, newMsg)
    self:SetNetworkVar('Show_ScoreBoard', true)
end

----------------------------------------------------------------
-- When activity is stopped this is needed to update the leaderboard.
----------------------------------------------------------------
function onDoCalculateActivityRating(self, msg)
    -- get the time for the player    
    --print('Score = ' .. msg.fValue1)
    --print('Time = ' .. msg.fValue2)
    
    msg.outActivityRating = msg.fValue2
    
    return msg
end

-- activity timers 
----------------------------------------------------------------
-- When ActivityTimerUpdate is sent, basically when a timer hits it updateInterval.
----------------------------------------------------------------
function onActivityTimerUpdate(self, msg)
    if msg.name == "AcceptedDelay" then
        --print('update delay timer to ' .. math.ceil(msg.timeRemaining))
        self:SetNetworkVar('Update_Default_Start_Timer', math.ceil(msg.timeRemaining))                      
    elseif msg.name == "ClockTick" then
        self:SetNetworkVar('Update_Timer', msg.timeElapsed)                            
    elseif msg.name == "SpawnTick" and not self:GetVar('isCoolDown') then
        --spawnMobs(self)                           
    end    
end

----------------------------------------------------------------
-- When ActivityTimerDone is sent, basically when the activity timer has reached it's duration.
----------------------------------------------------------------
function onActivityTimerDone(self, msg)
    if msg.name == "AcceptedDelay" then --or msg.name == "AllAcceptedDelay"       
        --print('update delay timer to 0')
        self:SetNetworkVar('Update_Default_Start_Timer', 0)                      
        ActivityTimerStart(self, 'AllAcceptedDelay', 1, 1)
    elseif msg.name == "AllAcceptedDelay" then --or msg.name == "AllAcceptedDelay"       
        --print('accepted delay *******************************')         
        self:SetNetworkVar('Clear_Scoreboard', true)                      
        ActivityTimerStart(self, 'StartDelay', 3, 3) --(timerName, updateTime, stopTime)  
        StartWaves(self)    
    elseif msg.name == "StartDelay" then        
        --print('adding in timers *******************************')        
        ActivityTimerStart(self, 'ClockTick', 1) --(timerName, updateTime, stopTime) 
       -- spawnMobs(self)
        ActivityTimerStart(self, 'PlaySpawnSound', 3, 3) --(timerName, updateTime, stopTime)          
    elseif msg.name == "CoolDownStart" then
        --print('cool down start timer *******************************')
        self:SetVar('isCoolDown', true)
        ActivityTimerStop(self, 'SpawnTick')  
        
        --print('stopping clock tick')          
    elseif msg.name == "CoolDownStop" then       
        --print('cool down stop timer *******************************')
        self:SetVar('isCoolDown', false)       

       -- spawnMobs(self)         
        ActivityTimerStart(self, 'PlaySpawnSound', 3, 3) --(timerName, updateTime, stopTime)          
    elseif msg.name == "PlaySpawnSound" then
        -- play war horn sound
        for k,v in ipairs(gGamestate.tPlayers) do      
            GAMEOBJ:GetObjectByID(v):PlayNDAudioEmitter{m_NDAudioEventGUID = '{ca36045d-89df-4e96-a317-1e152d226b69}'}             
        end      
    end
end


--============================================================

--============================================================
-- Game messages sent to the BASE_SURVIVAL_SERVER.lua file, these
-- must be in this script. Only change to add custom functionality, 
-- but leav e the base*message*(self, msg, newMsg) in the function.
--============================================================


----------------------------------------------------------------
-- Received a fire event messaged from the client
----------------------------------------------------------------
function onFireEventServerSide(self, msg)   
    baseFireEventServerSide(self, msg, newMsg)
end

----------------------------------------------------------------
-- A player has respawned
----------------------------------------------------------------
function onPlayerResurrected(self, msg)
    basePlayerResurrected(self, msg, newMsg)
end

----------------------------------------------------------------
-- Received a notify object message 
----------------------------------------------------------------
function onNotifyObject(self, msg)

	-- add to this CBs ref count. If it is 4 then do whatever
	if(msg.name == "cb_added") then
		if(msg.param1 == HealthCBLOT) then
			HealthCBCount = HealthCBCount + 1 
			if(HealthCBCount == 4) then
				print("ALL 4")
				-- do something
			end
		elseif(msg.param1 == DragonCBLOT) then
			DragonCBCount = DragonCBCount + 1
			if(DragonCBCount == 4) then
				print("ALL 4")
				-- do something
			end
		
		elseif(msg.param1 == AttackCBLOT) then
			AttackCBCount = AttackCBCount + 1
			if(AttackCBCount == 4) then
				print("ALL 4")
				-- do something
			end
		
		elseif(msg.param1 == ShieldCBLOT) then
			ShieldCBCount = ShieldCBCount + 1
			if(ShieldCBCount == 4) then
				print("ALL 4")
				-- do something
			end
		end
		
		print("CB ADDED: " .. msg.param1)
		return
	end
	
	-- remove from this CBs ref count. If it is 3 then undo whatever
	if(msg.name == "cb_removed") then
		print("CB REMOVED: " .. msg.param1)
		
		if(msg.param1 == HealthCBLOT) then
			HealthCBCount = HealthCBCount - 1 
			if(HealthCBCount == 3) then
				print("WAS 4")
				-- do something
			end
		elseif(msg.param1 == DragonCBLOT) then
			DragonCBCount = DragonCBCount - 1
			if(DragonCBCount == 3) then
				print("WAS 4")
				-- do something
			end
		
		elseif(msg.param1 == AttackCBLOT) then
			AttackCBCount = AttackCBCount - 1
			if(AttackCBCount == 3) then
				print("WAS 4")
				-- do something
			end
		
		elseif(msg.param1 == ShieldCBLOT) then
			ShieldCBCount = ShieldCBCount - 1
			if(ShieldCBCount == 3) then
				print("WAS 4")
				-- do something
			end
		end
		return
	end
	
	
    local player = msg.ObjIDSender
    
    -- check to make sure the player is in the activity
    if not IsPlayerInActivity(self, player) then return end
    
    -- update kill score
    UpdateActivityValue(self, player, 0, msg.param1)     
end

----------------------------------------------------------------
-- This is called when players hit the UI to exit or stop the game.
----------------------------------------------------------------
function onMessageBoxRespond(self,msg)
    baseMessageBoxRespond(self, msg, newMsg)
end

----------------------------------------------------------------
-- Notification that a ui element used.
----------------------------------------------------------------
function onActivityStateChangeRequest(self,msg)
    baseActivityStateChangeRequest(self, msg, newMsg)
end

----------------------------------------------------------------
-- Utility Script Functions
---------------------------------------------------------------
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 

function string.starts(String,Start)
    -- finds if a string starts with a giving string.
   return string.sub(String,1,string.len(Start))==Start
end 
