local LD = require("design.LevelDesignLibrary")
local socketed = false
local thisObj, thisLevel
local crystalIsLit = false
local player, interactZone, secondLight, temporalObject, startsSocketed
local unsocketedColor = engine.Vector.New(0, 0, 0)
local socketedUnlitColor = engine.Vector.New(0.4, 1, 1)
local socketedLitColor = engine.Vector.New(0.4, 1, 1)
local unsocketedMaterialParams, socketedCrystalLitMaterialParams, socketedMaterialParams, baseFX, crystalFirstTimeFired, onCrystalFullyLitEvents, onCrystalFullyUnlitEvents, onSocketedEvents, onSocketedCompleteEvents, onUnsocketedEvents, fullyLitCallbacks, useLOSTest, objectMesh
local bridgesOff = 0
local bridgeCount = 0
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  obj:AddMarker("CarryCrystalBase", obj:GetJointIndex("promptJoint"))
  player = game.Player.FindPlayer()
  baseFX = obj:FindSingleGOByName("FX")
  secondLight = obj:FindSingleGOByName("SecondLight")
  interactZone = game.InteractZone.New(obj, "syncJoint90Pos")
  interactZone:SetPromptJoint("promptJoint")
  interactZone:SetHintAngle(360)
  interactZone:SetHintXZRange(5)
  interactZone:SetAngle(360)
  interactZone:SetInteractCarryAllowed()
  interactZone:SetDebugOffset(0, 2, 0)
  interactZone:SetXZRange(2.5)
  interactZone:SetXZRangeWeight(1)
  interactZone:SetPlayerFrontAngle(360)
  interactZone:SetInteractFrontAngle(360)
  interactZone:SetPlayerFrontAngleWeight(150)
  interactZone:SetInteractFrontAngleWeight(200)
  interactZone:SetYRange(1.5)
  interactZone:SetOnScreenPercent(0.05)
  interactZone:Disable()
  objectMesh = obj.Parent:FindSingleGOByName("ObjectMesh")
  local useLOSTest = thisObj:GetLuaTableAttribute("UseLineOfSightTest")
  startsSocketed = obj:GetLuaTableAttribute("StartsSocketed")
  if obj:FindLuaTableAttribute("TemporalObject") ~= nil and obj:FindLuaTableAttribute("TemporalObject") ~= "" then
    temporalObject = level:GetGameObject(tostring(obj:FindLuaTableAttribute("TemporalObject")))
  end
  if temporalObject ~= nil then
    temporalObject:Hide()
    temporalObject:HideCollision()
  end
  onSocketedEvents = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("Event_OnSocketed"))
  onSocketedCompleteEvents = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("Event_OnSocketComplete"))
  onUnsocketedEvents = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("Event_OnUnsocketed"))
  if startsSocketed == true then
    socketed = true
  end
  unsocketedMaterialParams = {EmissiveColor = unsocketedColor}
  socketedCrystalLitMaterialParams = {EmissiveColor = socketedLitColor}
  socketedMaterialParams = {EmissiveColor = socketedUnlitColor}
  UpdateVisuals()
  SoundInit()
end
function OnStart()
  SetSocketed(socketed)
  if crystalIsLit then
    SetCrystalLit()
  else
    SetCrystalUnlit()
  end
end
function PlayerPickedUpNearbyCrystal()
  if player:InsideZone(thisObj) and socketed == false then
    Enable()
  end
end
function PlayerPutDownNearbyCrystal()
  if player:InsideZone(thisObj) and socketed == false then
    Disable()
  end
end
function SetBaseLineOfSightTrue()
  if interactZone ~= nil then
    interactZone:SetEnableLineOfSightTest(true)
  end
end
function PlayerEnteredBaseZone(markerGameObject, markerId)
  local isCarrying = false
  if not player:HasMarker("NoDropZone") then
    player:AddMarker("NoDropZone")
  end
  local playerBB = player:GetPrivateBlackboard()
  if playerBB:Exists("CrystalCarry") then
    isCarrying = playerBB:GetBoolean("CrystalCarry")
  end
  if not socketed and isCarrying then
    Enable()
  end
end
function PlayerExitedBaseZone(markerGameObject, markerId)
  Disable()
  if player:HasMarker("NoDropZone") and not player:IsInsideEntityZone("Zone_BlockCarryableDrop") then
    player:RemoveMarker("NoDropZone")
  end
end
function LuaHook_SetCrystalSocketed()
  SetSocketed(true)
  UpdateVisuals()
  LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, onSocketedCompleteEvents, "SocketedCompleteEvents")
end
function OnUseWorld(level, obj)
  if interactZone:PlayerCanInteract() and player:HasMarker("Carrying") then
    player:RequestInteract(thisObj, interactZone)
  end
end
function CarryNewlyUnsocketed()
  SetSocketed(false)
  SetCrystalUnlit()
  Enable()
  UpdateVisuals()
end
function DisableTemporalObject()
  if temporalObject ~= nil and temporalObject ~= "" then
    if temporalObject.LuaObjectScript and temporalObject.LuaObjectScript.IsTemporalObject then
      temporalObject.LuaObjectScript.TurnTemporalObjectOff()
    else
      for _, childObj in pairs(temporalObject.Descendants) do
        if childObj.LuaObjectScript and childObj.LuaObjectScript.IsTemporalObject then
          childObj.LuaObjectScript.TurnTemporalObjectOff(TOTurnedOff)
          bridgeCount = bridgeCount + 1
        else
          childObj:HideCollision()
          LD.CallFunctionAfterDelay(function()
            childObj:Hide()
          end, 0.75)
        end
      end
    end
    if bridgeCount <= 0 then
      temporalObject:HideCollision()
      LD.CallFunctionAfterDelay(function()
        temporalObject:Hide()
      end, 0.75)
    end
  end
end
function EnableTemporalObject()
  if temporalObject ~= nil and temporalObject ~= "" then
    if temporalObject.LuaObjectScript and temporalObject.LuaObjectScript.IsTemporalObject then
      temporalObject.LuaObjectScript.TurnTemporalObjectOn()
    else
      for _, childObj in pairs(temporalObject.Descendants) do
        if childObj.LuaObjectScript and childObj.LuaObjectScript.IsTemporalObject then
          childObj.LuaObjectScript.TurnTemporalObjectOn()
        else
          childObj:ShowCollision()
          childObj:Show()
        end
      end
      temporalObject:ShowCollision()
      temporalObject:Show()
    end
  end
end
function TOTurnedOff()
  bridgesOff = bridgesOff + 1
  if bridgesOff >= bridgeCount then
    temporalObject:Hide()
    temporalObject:HideCollision()
  end
end
function RegisterFullyLitCallback(fn)
  if fullyLitCallbacks == nil then
    fullyLitCallbacks = {}
  end
  table.insert(fullyLitCallbacks, fn)
end
function FireFullyLitCallbacks()
  if fullyLitCallbacks ~= nil then
    for _, callback in pairs(fullyLitCallbacks) do
      callback()
    end
  end
end
function Enable()
  print("something is enabling me", socketed, thisObj, thisLevel)
  if socketed == false then
    interactZone:Enable()
    interactZone:SetPlayerFrontAngleWeight(150)
    interactZone:SetInteractFrontAngleWeight(200)
  end
end
function Disable()
  print("Something is disabling me")
  interactZone:Disable()
end
function Lock()
  interactZone:Lock()
end
function Unlock()
  interactZone:Unlock()
end
function SetSocketed(newState)
  socketed = newState
  if socketed then
    baseFX:Hide()
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, onSocketedEvents, "JustSocketedEvents")
    Disable()
    StopSoundPodIdleLoop()
  else
    crystalIsLit = false
    baseFX:Show()
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, onUnsocketedEvents, "JustUnsocketedEvents")
    PlaySoundPodIdleLoop()
  end
end
function SetCrystalLit()
  crystalIsLit = true
  LD.ExtractAndExecuteCallbacksForEvent(thisLevel, thisObj, thisObj:GetLuaTableAttribute("Event_OnCrystalFullyLit"), "Crystal light")
  FireFullyLitCallbacks()
  EnableTemporalObject()
end
function SetCrystalUnlit()
  LD.ExtractAndExecuteCallbacksForEvent(thisLevel, thisObj, thisObj:GetLuaTableAttribute("Event_OnCrystalFullyUnlit"), "Crystal unlit")
  DisableTemporalObject()
end
function FireFirstTimeLit()
  if not crystalFirstTimeFired and thisObj:GetLuaTableAttribute("OnCrystalLitFirstTime") ~= nil then
    LD.ExtractAndExecuteCallbacksForEvent(thisLevel, thisObj, thisObj:GetLuaTableAttribute("OnCrystalLitFirstTime"), "Crystal first light")
    crystalFirstTimeFired = true
  end
end
function UpdateVisuals()
  if socketed == false then
    LD.SetAnimatedMaterialAttributes(objectMesh, "LightHolders", "Layer2", unsocketedMaterialParams)
    secondLight:Hide()
  elseif socketed == true and crystalIsLit == true then
    LD.SetAnimatedMaterialAttributes(objectMesh, "LightHolders", "Layer2", socketedCrystalLitMaterialParams)
    secondLight:Show()
    secondLight:PlayAnimCycle()
  elseif socketed == true and crystalIsLit == false then
    LD.SetAnimatedMaterialAttributes(objectMesh, "LightHolders", "Layer2", socketedMaterialParams)
    secondLight:Hide()
    secondLight:SetAnimRate(0.5)
    secondLight:PlayAnimCycle()
  end
end
function OnInteractStart(level, obj, creature)
  local jointIndex = thisObj:GetJointIndex("syncJoint")
  LD.SetJointFacingTowardObject(thisObj, player, jointIndex, "Sync")
end
function Sync()
  LD.PlaySingleSynchMove_KratosObject(thisObj, "syncJoint", "Carry Place Socket Exit", "BRA_CarryPlaceSocketExit", "", interactZone, true, "Bare")
  player:CallScript("SetCurrentBaseObject", thisObj)
  local playerBB = player:GetPrivateBlackboard()
  playerBB:Set("CrystalCarry", false)
end
local podEmitter
local soundEvents = {
  podIdle = "SND_MAG_Temporal_Light_Crystal_Pod_LP"
}
function SoundInit()
  podEmitter = thisObj.SoundEmitters[1]
end
function PlaySoundPodIdleLoop()
  LD.PlayRestartableSoundLoop(podEmitter, soundEvents.podIdle)
end
function StopSoundPodIdleLoop()
  LD.StopRestartableSoundLoop(podEmitter, soundEvents.podIdle)
end
function OnSaveCheckpoint(level, obj)
  return {
    crystalIsLit = crystalIsLit,
    socketed = socketed,
    crystalFirstTimeFired = crystalFirstTimeFired
  }
end
function OnRestoreCheckpoint(level, obj, tab)
  crystalIsLit = tab.crystalIsLit
  crystalFirstTimeFired = tab.crystalFirstTimeFired
  socketed = tab.socketed
  UpdateVisuals()
end
