--[[
  @package    eg4-crank-ui-2
  @file       indeterminate-progress.lua
  @brief      Handles indeterminate progress bars.
]]

local INDETERMINATE_PROGRESS_BAR_FPS = 20
local INDETERMINATE_PROGRESS_BAR_DURATION = 2.0
local INDETERMINATE_PROGRESS_BAR_POINT_1_START = 0
local INDETERMINATE_PROGRESS_BAR_POINT_1_END = 1.0
local INDETERMINATE_PROGRESS_BAR_POINT_2_START = 0
local INDETERMINATE_PROGRESS_BAR_POINT_2_END = 1.0
local INDETERMINATE_PROGRESS_BAR_EASE_OUT_FACTOR = 3
local INDETERMINATE_PROGRESS_BAR_EASE_IN_FACTOR = 1.5

local info = {}

---
--- @brief     Easing equation function using a power (t^n) easing out.
--- @param t   Current time (in frames or seconds).
--- @param b   Starting value.
--- @param c   Change needed in value.
--- @param d   Expected easing duration (in frames or seconds).
--- @return    value
local function ease_out(t, b, c, d)
  t = t / d - 1
  return c * (math.pow(t, INDETERMINATE_PROGRESS_BAR_EASE_OUT_FACTOR) + 1) + b
end

---
--- @brief     Easing equation function using a cubic (t^n) easing in.
--- @param t   Current time (in frames or seconds).
--- @param b   Starting value.
--- @param c   Change needed in value.
--- @param d   Expected easing duration (in frames or seconds).
--- @return    value
local function ease_in_cubic(t, b, c, d)
  t = t / d
  return c * math.pow(t, INDETERMINATE_PROGRESS_BAR_EASE_IN_FACTOR) + b
end

---
--- @brief  Calculate the indeterminate progress bar positions
--- @param  elapsed - Elapsed time
--- @return p1 - Position 1
--- @return p2 - Position 2
--- @return elapsed - Updated elapsed time
local function calc_indeterminate_progress(elapsed)
  local p1 = ease_in_cubic(elapsed - INDETERMINATE_PROGRESS_BAR_POINT_1_START, 0, 1, INDETERMINATE_PROGRESS_BAR_POINT_1_END - INDETERMINATE_PROGRESS_BAR_POINT_1_START)
  if p1 < 0 then p1 = 0 elseif p1 > 1 then p1 = 1 end
  local p2 = ease_out(elapsed - INDETERMINATE_PROGRESS_BAR_POINT_2_START, 0, 1, INDETERMINATE_PROGRESS_BAR_POINT_2_END - INDETERMINATE_PROGRESS_BAR_POINT_2_START)
  if p2 < 0 then p2 = 0 elseif p2 > 1 then p2 = 1 end
  if elapsed >= 1 then elapsed = 0 else elapsed = elapsed + 1 / (INDETERMINATE_PROGRESS_BAR_FPS * INDETERMINATE_PROGRESS_BAR_DURATION) end
  return p1, p2, elapsed
end

---
--- @brief  Set the update in progress bar width and x position
--- @param  width - The width of the progress bar
--- @param  x - The x position of the progress bar (optional, default 0)
local function update_progress()
  for control, item in pairs(info.list) do
    local p1, p2, elapsed = calc_indeterminate_progress(item.elapsed)
    local x = p1 * item.width
    local width = (p2 - p1) * item.width
    gre.set_value(control..".x", x, control..".width", width)
    item.elapsed = elapsed
  end
end

---
--- @brief  Timer callback to update the rolling progress bar
--- @param  control - The control name
function start_indeterminate_progress(control)

  stop_indeterminate_progress(control)

  if not info.list then  info.list = {} end
  info.list[control] = {
    width = gre.get_value(control..".grd_width"),
    elapsed = 0
  }

  info.timer = gre.timer_set_interval(update_progress, 1000 / INDETERMINATE_PROGRESS_BAR_FPS)
end

---
--- @brief  Stop the rolling progress bar update timer
--- @param  control - The control name
function stop_indeterminate_progress(control)
  if not info.list then return end
  info.list[control] = nil
  if next(info.list) then return end
  if not info.timer then return end
  gre.timer_clear_interval(info.timer)
  info = {}
end

