---
-- @package   eg4-crank-ui
-- @file      sbc-range-slider-control.lua
-- @brief     Functionality related to handling range slider movement and updates.
--
-- @copyright Copyright 2020 Hanover Displays Limited.
-- @license   This program is the confidential and proprietary product of
--            Hanover Displays Limited. Any unauthorised use, reproduction or
--            transfer of this program is strictly prohibited. (Subject to
--            limited distribution and restricted disclosure only.) All
--            rights reserved.
--
--

local UPDATE_FREQUENCY = 30
local SLIDER_CENTER_OFFSET = 21

local context
local slider
local timer_id

---
--- @brief  Order and scale the values
---
local function ScaleValues(a, b)
  if a > b then a,b = b,a end
  return math.floor(a * context.scale + 0.5), math.floor(b * context.scale + 0.5)
end

---
--- @brief  Set the value(s) text.
---
local function SetValueControl(a, b)
  if gre.get_value(context.group..".slider_a.grd_hidden") == 0 and 
     gre.get_value(context.group..".slider_b.grd_hidden") == 0 then
    local format = gre.get_value(context.group..".format") or "%d%% - %d%%"
    gre.set_value(context.layer..".value.text", string.format(format, a, b))
  else
    local format = gre.get_value(context.group..".format") or "%d%%"
    gre.set_value(context.layer..".value.text", string.format(format, b))
  end
end

---
--- @brief  Initialise the range slider control
---
local function Initialise(mapargs)

  local group = context.group
  local layer = context.layer

  slider = {}
  slider.offset = gre.get_layer_attrs(layer, "x").x + gre.get_group_attrs(group, "x").x + SLIDER_CENTER_OFFSET

  slider.x = mapargs.context_event_data.x - slider.offset

  slider.range = gre.get_control_attrs(group..".gradient", "x", "width")
  slider.range.x = slider.range.x - SLIDER_CENTER_OFFSET
  slider.range.width = slider.range.width - 3

  local slider_a = gre.get_control_attrs(group..".slider_a", "x", "hidden")
  local slider_b = gre.get_control_attrs(group..".slider_b", "x", "hidden")
  if slider_a == nil or slider_a.hidden == 1 then
    if slider_b == nil or slider_b.hidden  == 1 then
      return
    else
      slider.control = group..".slider_b"
      slider.start = slider.range.x
    end
  else
    if slider_b == nil or slider_b.hidden  == 1 then
      slider.control = group..".slider_a"
      slider.start = slider.range.x
    else
      if math.abs(slider_a.x - slider.x) < math.abs(slider_b.x - slider.x) then
        slider.control = group..".slider_a"
        slider.start = slider_b.x
      else
        slider.control = group..".slider_b"
        slider.start = slider_a.x
      end
    end
  end

  slider.selected = group..".selected"
  slider.step_size = tonumber(gre.get_value(group..".step_size"))

end

---
--- @brief  Timer callback to update the position of a slider at regular intervals.
---
local function Update()

  if slider.x < slider.range.x then
    slider.x = slider.range.x
  elseif slider.x > slider.range.x + slider.range.width then
    slider.x = slider.range.x + slider.range.width
  end

  -- Adjust for step size
  if slider.step_size ~= nil and slider.step_size > 0  and slider.step_size <= 1 then
    slider.x = math.floor((slider.x - slider.range.x) / (slider.step_size * slider.range.width) + 0.5) * (slider.step_size * slider.range.width) + slider.range.x
  end

  gre.set_control_attrs(slider.control, { x = slider.x })
  
  if slider.start < slider.x then
    gre.set_control_attrs(slider.selected, { x = slider.start + SLIDER_CENTER_OFFSET + 2, width = slider.x - slider.start })
  else
    gre.set_control_attrs(slider.selected, { x = slider.x + SLIDER_CENTER_OFFSET + 2, width = slider.start - slider.x })
  end

  local a = (slider.start - slider.range.x) / slider.range.width
  local b = (slider.x - slider.range.x) / slider.range.width
  local active = timer_id ~= nil

  SetValueControl(ScaleValues(a, b))

  if timer_id == nil or gre.get_value(context.group..".running_mode") then
    -- 
    local value1, value2 = ScaleValues(a, b)  
    -- Check if a set function exists for this component.
    if context.set_value then
      if gre.get_value(context.group..".slider_a.grd_hidden") == 0 and 
         gre.get_value(context.group..".slider_b.grd_hidden") == 0 then
        context.set_value(value1, value2, context.port_id)
      else
        context.set_value(value2, context.port_id)
      end
    end
  
    -- If specified, run callback
    callback = _G[gre.get_value(context.group..".callback")]
    if type(callback) == "function" then
      callback({
        port_id = context.port_id,
        value1 = value1, 
        value2 = value2 
      })
    end
  
    -- If specified, send event to backend
    local event = gre.get_value(context.group .. ".event")
    if event then
      SendEvent(event)
    end
  end
end

---
--- @brief  Callback to process slider control press.
--- @param  gre#context mapargs
---
function SBCRangeSliderPress(mapargs)
  timer_id = gre.timer_set_interval(Update, 1000 / UPDATE_FREQUENCY)
  Initialise(mapargs)
  Update()
end

---
--- @brief  Callback to process slider control release
--- @param  gre#context mapargs
---
function SBCRangeSliderRelease(mapargs)
  if timer_id then
    gre.timer_clear_timeout(timer_id)
    timer_id = nil;
    slider.x = mapargs.context_event_data.x - slider.offset
    Update()
  end
end

---
--- @brief  Callback to process slider control touch motion
--- @param  gre#context mapargs
---
function SBCRangeSliderMotion(mapargs)
  if timer_id then
    slider.x = mapargs.context_event_data.x - slider.offset
  end
end

---
--- @brief  Set the initial slider positions
---
local function SetSlider(a, b)
  local range = gre.get_control_attrs(context.group..".gradient", "x", "width")
  range.x = range.x - SLIDER_CENTER_OFFSET
  range.width = range.width - 3

  a = a / context.scale * range.width + range.x
  b = b / context.scale * range.width + range.x

  gre.set_value(context.group..".slider_a.grd_x", a,
                context.group..".slider_b.grd_x", b )
  gre.set_control_attrs(context.group..".slider_b", { x = b })
  if a > b then a,b = b,a end
  gre.set_value(
    context.group..".selected.grd_x", a + SLIDER_CENTER_OFFSET + 2, 
    context.group..".selected.grd_width", b - a )
end

---
--- @brief  Initialise the slider screen
---
function CBInitialiseRangeControl(mapargs)
  local group = findObject(mapargs.context_screen, "range_slider")
  local layer = group:split(".")[1]

  context = {
    group = group,
    layer = group:split(".")[1]
  }
  context.port_id = gre.get_value(context.group..".port_id") or gre.get_value(mapargs.context_screen..".port_id")
  context.scale = tonumber(gre.get_value(context.group..".scale")) or 100
  context.set_value = sbsettings["set_"..layer] or sbsettings["set_"..layer:sub(1, -7)]

  local fn = sbsettings["get_"..layer] or sbsettings["get_"..layer:sub(1, -7)]
  if type(fn) == "function" then
    local a, b = fn(context.port_id)
    b = b or 0
    SetSlider(a, b)
    if a > b then a,b = b,a end
    SetValueControl(a, b)
  end
end

-- EOF --