local Vector3 = require "types.vector3"
local Signal = require "types.signal"

local TEXTURE = "images/duckie.png"

local States = {
  IDLE = 1,
  WANDER = 2,
  CHASE = 3,
}

local WANDER_TIME = 1.0
local IDLE_TIME = 1.0

local Duck = {}

Duck.__index = Duck

function Duck.new(position)
  -- local d = util.shallow_copy(Duck)
  local d = {
    position = position:copy(),
    velocity = Vector3(),
    direction = Vector3(0, 0, -1):rotated(Vector3.UP, util.random_float(-math.pi, math.pi)),
  
    target = nil,
    state = States.IDLE,
  
    wander_timer = 0,
  
    speed = 0.02,
    chase_speed = 0.04,
    accel = 0.5,
  
    STATES = States,
  
    AteFeed = Signal.new(),
    SeekFeed = Signal.new(),
    
    index = 1,
  }
  d.position.y = 0.4

  return setmetatable(d, Duck)
end

function Duck:tick(ctx)
  if self.state == States.IDLE then
    self:idle(ctx.frame_duration)
  elseif self.state == States.WANDER then
    self:wander(ctx.frame_duration)
  elseif self.state == States.CHASE then
    self:chase(ctx.frame_duration)
  end
  draw_billboard{position = self.position, size = {x = 0.4, y = 0.4}, texture = TEXTURE}
end

function Duck:idle(delta)
  self.wander_timer = self.wander_timer + delta
  if self.wander_timer >= IDLE_TIME then
    self.state = States.WANDER
    self.wander_timer = 0
    self.direction = Vector3(0, 0, -1):rotated(Vector3.UP, math.random() * (math.pi * 2.0))
    return
  end
end

function Duck:wander(delta)
  self.wander_timer = self.wander_timer + delta
  if self.wander_timer >= WANDER_TIME then
    self.state = States.IDLE
    self.wander_timer = 0
    self.SeekFeed:emit(self)
    return
  end

  local target_vel = self.direction * self.speed
  self.velocity = self.velocity:lerp(target_vel, self.accel)
  self.position = self.position + self.velocity
end

function Duck:chase(delta)
  local hpos = self.position:horizontal()
  local fhpos = self.target.position:horizontal()

  local dist = hpos:distance_to(fhpos)
  if dist <= 0.1 then
    self.AteFeed:emit(self.target)
    self.wander_timer = 0
    self.state = States.IDLE
    self.target = nil
  else
    local dir = self.position:horizontal():direction_to(self.target.position:horizontal())
    local target_vel = dir * self.chase_speed
    self.velocity = self.velocity:lerp(target_vel, self.accel)
    self.position = self.position + self.velocity
  end
end

function Duck:start_chase(feed)
  if self.state == States.CHASE then return end
  -- print("duck " .. self.index .. " starting chase")
  self.state = States.CHASE
  self.target = feed
  feed.occupied = true
end

return Duck