--------------------------------------------------------------------------------
function init()
  self.sensors = sensors.create()

  capturepod.onInit()

  self.state = stateMachine.create({
    "attackState",
    "moveState",
    "captiveState"
  })
  self.state.leavingState = function(stateName)
    entity.setAnimationState("movement", "idle")
  end

  self.jumpHoldTime = 0;

  entity.setAggressive(true)
  entity.setDamageOnTouch(true)
  entity.setDeathParticleBurst("deathPoof")
  entity.setAnimationState("movement", "idle")
end

--------------------------------------------------------------------------------
function update(dt)
  util.trackTarget(entity.configParameter("noticeDistance"))
  if self.targetId ~= nil and not attacking() then
    self.state.pickState(self.targetId)
  end

  self.state.update(dt)
  self.sensors.clear()

  -- Update animation
  if mcontroller.onGround() then
    entity.setAnimationState("movement", "idle")
  else
    local velocity = mcontroller.velocity()

    if velocity[2] < 0 then
      entity.setAnimationState("movement", "fall")
    else
      entity.setAnimationState("movement", "jump")
    end
  end
end

--------------------------------------------------------------------------------
function damage(args)
  capturepod.onDamage(args)
end

--------------------------------------------------------------------------------
function setSpawnDirection(direction)
  local spawnVelocity = entity.configParameter("spawnVelocity")
  mcontroller.setVelocity({ spawnVelocity[1] * direction, spawnVelocity[2] })
end

--------------------------------------------------------------------------------
function poSize()
  return entity.configParameter("poSize")
end

--------------------------------------------------------------------------------
function attacking()
  return self.state.stateDesc() == "attackState"
end

--------------------------------------------------------------------------------
function move(delta, run)
  if not mcontroller.onGround() and self.jumpHoldTime > 0 then
    mcontroller.controlHoldJump()
    self.jumpHoldTime = math.max(0, self.jumpHoldTime - dt)
  end

  if mcontroller.onGround() then
    if delta[2] > entity.configParameter("largeJumpYThreshold") then
      self.jumpHoldTime = entity.configParameter("largeHumpHoldTime")
    else
      self.jumpHoldTime = 0
    end
  end

  mcontroller.controlMove(delta[1], true)
end

--------------------------------------------------------------------------------
moveState = {}

function moveState.enter()
  if capturepod.isCaptive() then return nil end

  return {
    timer = entity.randomizeParameterRange("moveTimeRange"),
    direction = util.toDirection(math.random(100) - 50)
  }
end

function moveState.update(dt, stateData)
  if self.sensors.blockedSensors.collision.any(true) then
    stateData.direction = -stateData.direction
  end

  move({ stateData.direction, 0 }, false)

  stateData.timer = stateData.timer - dt
  if stateData.timer <= 0 then
    return true, entity.randomizeParameterRange("moveCooldownTimeRange")
  end

  return false
end

--------------------------------------------------------------------------------
attackState = {}

function attackState.enterWith(targetId)
  return { timer = 0 }
end

function attackState.update(dt, stateData)
  if self.targetPosition == nil then return true end

  if self.targetId == nil then
    stateData.timer = stateData.timer + dt
    if stateData.timer > entity.configParameter("attackSearchTime") then
      return true
    end
  end

  local toTarget = world.distance(self.targetPosition, mcontroller.position())
  move(toTarget, true)

  return false
end

--------------------------------------------------------------------------------
captiveState = {
  closeDistance = 4,
  runDistance = 12,
}

function captiveState.enter()
  if not capturepod.isCaptive() or self.targetId ~= nil then return nil end

  return { running = false }
end

function captiveState.update(dt, stateData)
  -- Translate owner uuid to entity id
  if self.ownerEntityId ~= nil then
    if not world.entityExists(self.ownerEntityId) then
      self.ownerEntityId = nil
    end
  end

  if self.ownerEntityId == nil then
    local playerIds = world.entityQuery(mcontroller.position(), 50, {includedTypes = {"player"}})
    for _, playerId in pairs(playerIds) do
      if world.entityUniqueId(playerId) == storage.ownerUuid then
        self.ownerEntityId = playerId
        break
      end
    end
  end

  -- Owner is nowhere around
  if self.ownerEntityId == nil then
    return false
  end

  local ownerPosition = world.entityPosition(self.ownerEntityId)
  local toOwner = world.distance(ownerPosition, mcontroller.position())
  local distance = math.abs(toOwner[1])

  local movement
  if distance < captiveState.closeDistance then
    stateData.running = false
    movement = 0
  elseif toOwner[1] < 0 then
    movement = -1
  elseif toOwner[1] > 0 then
    movement = 1
  end

  if distance > captiveState.runDistance then
    stateData.running = true
  end

  move({ movement, toOwner[2] }, stateData.running)

  return false
end
