local LD = require("design.LevelDesignLibrary")
local mpicon = require("ui.mpicon")
local thisObj, son, player, heroPuppeteer, sonPuppeteer, includeSon, enabled, synchObj, startEnabled, interactZone, heroBranch, sonBranch, objectAnimName, synchJointName, promptJointName, useLinkJoint, weaponMode, completionPercentage
local synchSlaves = {}
local interactionOnStart, interactionOnTNFStart, interactionOnTNFIdle, interactionOnTNFStruggleStart, interactionOnTNFStruggleComplete, interactionOnTNFFail, interactionOnTNFFailComplete, interactionOnTNFWin, interactionOnTNFWinComplete, interactionOnInterrupted, luaCustomEvents, useGoAsReference
function OnScriptLoaded(level, obj)
  thisObj = obj
  player = game.Player.FindPlayer()
  son = game.AI.FindSon()
  startEnabled = obj:GetLuaTableAttribute("startEnabled")
  includeSon = obj:GetLuaTableAttribute("includeSon")
  sonBranch = obj:GetLuaTableAttribute("sonBranch")
  heroBranch = obj:GetLuaTableAttribute("heroBranch")
  synchObj = obj:GetLuaTableAttribute("synchObj")
  objectAnimName = obj:GetLuaTableAttribute("objectAnimName")
  synchJointName = obj:GetLuaTableAttribute("synchJointName")
  promptJointName = obj:GetLuaTableAttribute("promptJointName")
  useLinkJoint = obj:GetLuaTableAttribute("useLinkJoint")
  weaponMode = obj:FindLuaTableAttribute("weaponMode") or "Bare"
  completionPercentage = obj:FindLuaTableAttribute("completionPercentage") or 0
  interactionOnStart = obj:FindLuaTableAttribute("interactionOnStart")
  interactionOnTNFStart = obj:FindLuaTableAttribute("interactionOnTNFStart")
  interactionOnTNFIdle = obj:FindLuaTableAttribute("interactionOnTNFIdle")
  interactionOnTNFStruggleStart = obj:FindLuaTableAttribute("interactionOnTNFStruggleStart")
  interactionOnTNFStruggleComplete = obj:FindLuaTableAttribute("interactionOnTNFStruggleComplete")
  interactionOnTNFWin = obj:FindLuaTableAttribute("interactionOnTNFWin")
  interactionOnTNFWinComplete = obj:FindLuaTableAttribute("interactionOnTNFWinComplete")
  interactionOnTNFFail = obj:FindLuaTableAttribute("interactionOnTNFFail")
  interactionOnTNFFailComplete = obj:FindLuaTableAttribute("interactionOnTNFFailComplete")
  interactionOnInterrupted = obj:FindLuaTableAttribute("interactionOnInterrupted")
  if synchObj ~= nil and synchObj ~= "" then
    synchObj = level:GetGameObject(synchObj)
  else
    synchObj = obj
  end
  if objectAnimName ~= nil and objectAnimName ~= "" then
    if string.find(objectAnimName, "BRA") ~= nil then
      AddSlavesToSynch({Slave = synchObj, Branch = objectAnimName})
      useGoAsReference = false
    else
      AddSlavesToSynch({Slave = synchObj, Anim = objectAnimName})
      useGoAsReference = true
    end
  else
    useGoAsReference = true
  end
  if interactionOnStart ~= nil then
    interactionOnStart = LD.ExtractCallbacksForEvent(level, obj, interactionOnStart)
  end
  if interactionOnTNFStart ~= nil then
    interactionOnTNFStart = LD.ExtractCallbacksForEvent(level, obj, interactionOnTNFStart)
  end
  if interactionOnTNFIdle ~= nil then
    interactionOnTNFIdle = LD.ExtractCallbacksForEvent(level, obj, interactionOnTNFIdle)
  end
  if interactionOnTNFStruggleStart ~= nil then
    interactionOnTNFStruggleStart = LD.ExtractCallbacksForEvent(level, obj, interactionOnTNFStruggleStart)
  end
  if interactionOnTNFStruggleComplete ~= nil then
    interactionOnTNFStruggleComplete = LD.ExtractCallbacksForEvent(level, obj, interactionOnTNFStruggleComplete)
  end
  if interactionOnTNFWin ~= nil then
    interactionOnTNFWin = LD.ExtractCallbacksForEvent(level, obj, interactionOnTNFWin)
  end
  if interactionOnTNFFail ~= nil then
    interactionOnTNFFail = LD.ExtractCallbacksForEvent(level, obj, interactionOnTNFFail)
  end
  if interactionOnTNFWinComplete ~= nil then
    interactionOnTNFWinComplete = LD.ExtractCallbacksForEvent(level, obj, interactionOnTNFWinComplete)
  end
  if interactionOnTNFFailComplete ~= nil then
    interactionOnTNFFailComplete = LD.ExtractCallbacksForEvent(level, obj, interactionOnTNFFailComplete)
  end
  if interactionOnInterrupted ~= nil then
    interactionOnInterrupted = LD.ExtractCallbacksForEvent(level, obj, interactionOnInterrupted)
  end
  if promptJointName ~= nil and promptJointName ~= "" then
    interactZone = LD.CreateInteractZone_Standard_180(obj, promptJointName)
    interactZone:SetTags("NotWhileSonInteracting")
  else
    interactZone = LD.CreateInteractZone_Standard_180(obj, "promptJoint")
    interactZone:SetTags("NotWhileSonInteracting")
  end
  if startEnabled then
    enabled = true
  else
    enabled = false
  end
  SoundInit()
  game.SubObject.Sleep(obj)
end
function OnStart(level, obj)
  if enabled then
    Enable()
  else
    Disable()
  end
end
function OnSaveCheckpoint(level, obj)
  return {enabled = enabled}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  enabled = savedInfo.enabled
end
function OnUseWorld(level, obj)
  if interactZone ~= nil and interactZone:PlayerCanInteract() then
    player:RequestInteract(obj, interactZone)
  end
end
local IconEnable = function()
  if useLinkJoint then
    mpicon.level.Create(player, "WORLD_INTERACT_MASHBUTTON", "linkJoint")
  else
    mpicon.level.Create(thisObj, "WORLD_INTERACT_MASHBUTTON")
  end
end
local IconDisable = function()
  if useLinkJoint then
    mpicon.level.Off(player, "WORLD_INTERACT_MASHBUTTON", "linkJoint")
  else
    mpicon.level.Off(thisObj, "WORLD_INTERACT_MASHBUTTON")
  end
end
function SetUseLinkJointForPrompt(bool)
  useLinkJoint = bool
end
function Enable()
  enabled = true
  interactZone:Enable()
end
function Disable()
  enabled = false
  interactZone:Disable()
end
function GetInteractZone()
  return interactZone
end
function OverrideInteractZone(overrideTable)
  LD.OverrideInteractZone(interactZone, overrideTable)
end
function OverrideHintXZRange(newRange)
  interactZone:SetHintXZRange(newRange)
end
function OnInteractStart(level, obj, creature)
  if interactionOnStart then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnStart, "Interaction Start Event")
  end
  heroPuppeteer = game.Puppeteer.NewForce(synchObj, "Interactive Object TNF", player)
  local synchJoint
  if synchJointName ~= nil and synchJointName ~= "" then
    synchJoint = synchJointName
  else
    synchJoint = "synchJoint"
  end
  heroPuppeteer:WeaponEquip({
    weaponMode = weaponMode,
    use_completion_percentage = true,
    completion_percentage = completionPercentage
  })
  local approachData = LD.GetGlobalApproachData()
  approachData.branch_name = heroBranch
  approachData.joint_name = synchJointName
  player:SetAccelerationOverride(approachData.acceleration_override)
  player:SetDecelerationOverride(approachData.deceleration_override)
  heroPuppeteer:Approach(approachData)
  heroPuppeteer:OnComplete(function()
    print("-- on complete --")
    player:ClearFocus()
    player:ClearDecelerationOverride()
    player:ClearAccelerationOverride()
    if includeSon then
      sonPuppeteer = game.Puppeteer.NewForce(synchObj, "Interactive Object TNF", son)
      sonPuppeteer:AcceptSync()
      table.insert(synchSlaves, {Slave = sonPuppeteer, Branch = sonBranch})
      local sonPuppeteerIndex = #synchSlaves
      if heroPuppeteer ~= nil then
        heroPuppeteer:Sync(heroBranch, useGoAsReference, synchSlaves, synchJoint)
        heroPuppeteer:OnComplete(function()
          if heroPuppeteer ~= nil then
            heroPuppeteer:Clear()
            heroPuppeteer = nil
          end
          if sonPuppeteer ~= nil then
            table.remove(synchSlaves, sonPuppeteerIndex)
            sonPuppeteer:Clear()
            sonPuppeteer = nil
          end
        end)
      end
      sonPuppeteer:OnComplete(function()
        if sonPuppeteer ~= nil then
          table.remove(synchSlaves, sonPuppeteerIndex)
          sonPuppeteer:Clear()
          sonPuppeteer = nil
        end
      end)
    elseif heroPuppeteer ~= nil then
      heroPuppeteer:Sync(heroBranch, useGoAsReference, synchSlaves, synchJoint)
      heroPuppeteer:OnComplete(function()
        if heroPuppeteer ~= nil then
          heroPuppeteer:Clear()
          heroPuppeteer = nil
        end
      end)
    end
  end)
end
function OnInteractAbort(level, obj, creature)
  if interactionOnInterrupted then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnInterrupted, "Interaction TNF Interrupt Event")
  end
  IconDisable()
  StopTNFActiveLoop()
  PlayTNFFailSound()
  PlayTNFInActiveLoop()
end
function SetSynchObject(obj)
  useGoAsReference = false
  synchObj = obj
end
function AddSlavesToSynch(args)
  if args.Branch ~= nil then
    table.insert(synchSlaves, {
      Slave = args.Slave,
      Branch = args.Branch
    })
  end
  if args.Anim ~= nil then
    table.insert(synchSlaves, {
      Slave = args.Slave,
      Anim = args.Anim
    })
  end
end
function RegisterCustomEvent(fn)
  if luaCustomEvents == nil then
    luaCustomEvents = {}
  end
  luaCustomEvents[#luaCustomEvents + 1] = fn
end
function FireCustomEvents()
  if luaCustomEvents ~= nil then
    for _, fn in pairs(luaCustomEvents) do
      fn()
    end
  end
end
function LuaEvent_Custom()
  FireCustomEvents()
end
function LuaEvent_TNF_Start(level, obj, creature)
  IconEnable()
  if interactionOnTNFStart then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnTNFStart, "Interaction TNF Start Event")
  end
  StopTNFInActiveLoop()
  PlayTNFActiveLoop()
end
function LuaEvent_TNF_Idle_Start(level, obj, creature)
  if interactionOnTNFIdle then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnTNFIdle, "Interaction TNF Idle Event")
  end
end
function LuaEvent_TNF_Struggle_Start(level, obj, creature)
  if interactionOnTNFStruggleStart then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnTNFStruggleStart, "Interaction TNF Struggle Start Event")
  end
end
function LuaEvent_TNF_Struggle_Complete(level, obj, creature)
  if interactionOnTNFStruggleComplete then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnTNFStruggleComplete, "Interaction TNF Struggle Complete Event")
  end
end
function LuaEvent_TNF_Win(level, obj, creature)
  if interactionOnTNFWin then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnTNFWin, "Interaction TNF Win Event")
  end
  Disable()
  IconDisable()
  StopTNFActiveLoop()
  PlayTNFWinSound()
end
function LuaEvent_TNF_Win_Complete(level, obj, creature)
  if interactionOnTNFWinComplete then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnTNFWinComplete, "Interaction TNF Win Complete Event")
  end
end
function LuaEvent_TNF_Fail(level, obj, creature)
  if interactionOnTNFFail then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnTNFFail, "Interaction TNF Fail Event")
  end
  IconDisable()
  StopTNFActiveLoop()
  PlayTNFFailSound()
  PlayTNFInActiveLoop()
end
function LuaEvent_TNF_Fail_Complete(level, obj, creature)
  if interactionOnTNFFailComplete then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnTNFFailComplete, "Interaction TNF Fail Complete Event")
  end
end
local TNFisActive = false
local synchObjSoundEmitter = ""
local struggleToActiveIdleSoundShouldPlay = false
local soundOverrides = {
  synchObjSoundEmitter = nil,
  interactStart = "",
  loopSound = "",
  inactiveLoopSound = "",
  activeIdleLoopSound = "",
  struggleToActiveIdleSound = "",
  winSound = "",
  failSound = "",
  explicit_InteractStart = "",
  explicit_LoopSound = "",
  explicit_InactiveLoopSound = "",
  explicit_WinSound = "",
  explicit_FailSound = ""
}
function SoundInit()
  synchObjSoundEmitter = synchObj:FindSingleSoundEmitterByName("TNFEmitter")
end
function SoundSetup(newSoundOverrides)
  if newSoundOverrides ~= nil then
    if newSoundOverrides.synchObjSoundEmitter ~= nil then
      synchObjSoundEmitter = synchObj:FindSingleSoundEmitterByName(newSoundOverrides.synchObjSoundEmitter)
    end
    if newSoundOverrides.explicitSoundEmitter ~= nil then
      synchObjSoundEmitter = newSoundOverrides.explicitSoundEmitter
    end
    for key in pairs(soundOverrides) do
      for newKey, newValue in pairs(newSoundOverrides) do
        if newKey == key and newValue ~= nil and newValue ~= "" then
          soundOverrides[key] = newValue
        end
      end
      LD.SoundDebug(tostring(key) .. ": " .. tostring(soundOverrides[key]))
    end
  end
end
function PlayTNFInteractStart()
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.interactStart)
end
function PlayTNFActiveLoop()
  TNFisActive = true
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.loopSound)
end
function StopTNFActiveLoop()
  TNFisActive = false
  LD.StopSound(synchObjSoundEmitter, soundOverrides.loopSound)
end
function PlayTNFInActiveLoop()
  TNFisActive = true
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.inactiveLoopSound)
end
function StopTNFInActiveLoop()
  TNFisActive = false
  LD.StopSound(synchObjSoundEmitter, soundOverrides.inactiveLoopSound)
end
function StopSoundEnterStruggle()
  StopTNFActiveIdleLoop()
end
function PlayTNFActiveIdleLoop()
  if struggleToActiveIdleSoundShouldPlay then
    LD.PlaySound(synchObjSoundEmitter, soundOverrides.struggleToActiveIdleSound)
    struggleToActiveIdleSoundShouldPlay = false
  end
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.activeIdleLoopSound)
end
function StopTNFActiveIdleLoop()
  LD.StopSound(synchObjSoundEmitter, soundOverrides.activeIdleLoopSound)
  struggleToActiveIdleSoundShouldPlay = true
end
function PlayTNFWinSound()
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.winSound)
end
function PlayTNFFailSound()
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.failSound)
end
function LuaExplicitSoundEvent_InteractStart()
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.explicit_InteractStart)
end
function LuaExplicitSoundEvent_PlayLoopSound()
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.explicit_LoopSound)
end
function LuaExplicitSoundEvent_StopLoopSound()
  LD.StopSound(synchObjSoundEmitter, soundOverrides.explicit_LoopSound)
end
function LuaExplicitSoundEvent_PlayInactiveLoopSound()
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.explicit_InactiveLoopSound)
end
function LuaExplicitSoundEvent_StopInactiveLoopSound()
  LD.StopSound(synchObjSoundEmitter, soundOverrides.explicit_InactiveLoopSound)
end
function LuaExplicitSoundEvent_WinSound()
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.explicit_WinSound)
end
function LuaExplicitSoundEvent_FailSound()
  LD.PlaySound(synchObjSoundEmitter, soundOverrides.explicit_FailSound)
end
function SoundDebugTable()
  if engine.IsDebug == false then
    return
  end
  if TNFisActive then
    local dt = {
      Title = "TNF Info",
      X = 120,
      Y = 10,
      TitleColor = engine.Vector.New(255, 0, 128)
    }
    table.insert(dt, {
      "MOV Percentage: ",
      player:GetActiveMovePercent() * 100
    })
    table.insert(dt, {
      "Emitter: ",
      tostring(synchObjSoundEmitter)
    })
    table.insert(dt, {
      "LoopSound: ",
      soundOverrides.loopSound
    })
    table.insert(dt, {
      "WinSound: ",
      soundOverrides.winSound
    })
    table.insert(dt, {
      "FailSound: ",
      soundOverrides.failSound
    })
    engine.DrawDebugTable(dt)
  end
end
