---
-- @package   eg4-crank-ui
-- @file      slider-control.lua
-- @brief     Functionality related to handling slider movement and updates.
--
-- @copyright Copyright 2019 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 = {}

local slider = nil
local timer_id = nil


---
--- @brief  Initialises the slider object. Collects all the data required to 
---         control the object while it is being used. This greatly increases 
---         the update efficiency.
--- @param  layer - context layer of control
--- @param  group - context group of control
--- @param  control - context control
---
function Slider:new(layer, group, control)
  local item = {}
  setmetatable(item, self)
  self.__index = self

  item.control = control

  local data = gre.get_data(
      control .. ".scale_width",
      control .. ".slider_pos",
      control .. ".slider_width",
      control .. ".min_value",
      control .. ".max_value",
      control .. ".callback",
      control .. ".step_size")

  item.scale_x = data[control .. ".slider_width"] / 2
  item.scale_width = gre.get_control_attrs(control, "width").width - data[control .. ".slider_width"]
  item.min_value = data[control .. ".min_value"]
  item.max_value = data[control .. ".max_value"]
  item.callback = data[control .. ".callback"]
  item.step_size = data[control .. ".step_size"] or 0
  if item.step_size ~= 0 then
    if item.max_value - item.min_value ~= 0 then
      item.step_size = item.step_size * item.scale_width / (item.max_value - item.min_value) 
    else
      item.step_size = 0
    end
  end

  local layer_x = gre.get_layer_attrs(layer, "x").x or 0
  local group_x = gre.get_control_attrs(group, "x").x or 0
  local control_x = gre.get_control_attrs(control, "x").x or 0
  item.control_offset = layer_x + group_x + control_x
  item.slider_offset = data[control .. ".slider_width"] / 2
  
  return item
end

---
--- @brief  Updates the control when the position changes and does the call 
---         back with the update value
---
function Slider:update()
  local pos = self.x - (self.slider_offset + self.control_offset)

  if pos < self.scale_x - self.slider_offset then
    pos = self.scale_x - self.slider_offset
  elseif pos > self.scale_x + self.scale_width - self.slider_offset then
    pos = self.scale_x + self.scale_width - self.slider_offset
  end

  if self.step_size ~= 0 then
    pos = math.floor((pos - self.scale_x + self.slider_offset) / self.step_size + 0.5) * 
    self.step_size + self.scale_x - self.slider_offset
  end

  gre.set_value(self.control .. ".slider_pos", pos)
  
  local value = math.floor(
    (pos + self.slider_offset - self.scale_x) / self.scale_width * 
    (self.max_value - self.min_value) + self.min_value + 0.5)

  if self.value ~= value or timer_id == nil then
    self.value = value
    local callback = _G[self.callback]
    if type(callback) == "function" then
      callback(value, timer_id ~= nil)
    end
  end
end

---
--- @brief  Updates the current position of the slider
--- @param  x - new position
---
function Slider:motion(x)
  if timer_id ~= nil then
    self.x = x
  end
end

---
--- @brief  Updates the slider position value for the required control.
--- @param  control - control to update
--- @param  value - value for slider
---
function Slider:SetSliderPosition(control, value)
  local data = gre.get_data(
    control .. ".slider_width",
    control .. ".max_value",
    control .. ".min_value",
    control .. ".step_size")

  local min_value = data[control .. ".min_value"]
  local max_value = data[control .. ".max_value"]
  local step_size = data[control .. ".step_size"] or 0

  if value < min_value then
    value = min_value
  elseif value > max_value then
    value = max_value
  end 
  
  local width = gre.get_control_attrs(control, "width").width - data[control .. ".slider_width"]
  local pos = (width * (value - min_value)) / (max_value - min_value)
  
  gre.set_value(control .. ".slider_pos", pos)
end

---
--- @brief  Timer callback to update the position of a slider at regular intervals.
---
function UpdateSliderControlPosition()
  if timer_id ~= nil then
    slider:update()
  end
end

---
--- @brief  Callback to process slider control touches.
--- @param  gre#context mapargs
---
function CBSliderPress(mapargs)
  slider = Slider:new(mapargs.context_layer, mapargs.context_group, mapargs.context_control)
  timer_id = gre.timer_set_interval(UpdateSliderControlPosition, 1000 / UPDATE_FREQUENCY)
  slider:motion(mapargs.context_event_data.x)
  slider:update()
end

---
--- @brief  Callback to process slider control release
--- @param  gre#context mapargs
---
function CBSliderRelease(mapargs) 
  if timer_id ~= nil then
    slider:motion(mapargs.context_event_data.x)
    gre.timer_clear_timeout(timer_id)
    timer_id = nil;
    slider:update()
  end
end

---
--- @brief  Callback to process slider control touch motion
--- @param  gre#context mapargs
---
function CBSliderMotion(mapargs) 
  if slider ~= nil then
    slider:motion(mapargs.context_event_data.x)
  end
end

---
--- @brief  Callback to reset the position of a test slider
--- @param  gre#context mapargs
---
function SetSliderPosition(control, value)
  Slider:SetSliderPosition(control, value)
end

-- TODO: Remove test function
function CBUpdateTestValue(value, active)
  if active then
    gre.set_value("test_slider_control_layer.value.text", "* "..tostring(value).." *")
  else 
    gre.set_value("test_slider_control_layer.value.text", tostring(value))
  end 
end

---
--- @brief  Set an associate sound level control icon.
--- @param  value - value for slider
--- @param  active - true the slider is being moved.
---
function UpdateSoundLevelControl(control, value)
  local data = {}
  if value == 0 then
    data[control .. ".level0_alpha"] = 255
    data[control .. ".level1_alpha"] = 0
    data[control .. ".level2_alpha"] = 0
    data[control .. ".level3_alpha"] = 0
  elseif value <= 33 then
    data[control .. ".level0_alpha"] = 0
    data[control .. ".level1_alpha"] = 255
    data[control .. ".level2_alpha"] = 0
    data[control .. ".level3_alpha"] = 0
  elseif value <= 66 then
    data[control .. ".level0_alpha"] = 0
    data[control .. ".level1_alpha"] = 0
    data[control .. ".level2_alpha"] = 255
    data[control .. ".level3_alpha"] = 0
  else
    data[control .. ".level0_alpha"] = 0
    data[control .. ".level1_alpha"] = 0
    data[control .. ".level2_alpha"] = 0
    data[control .. ".level3_alpha"] = 255
  end
  gre.set_data(data)  
end

---
--- @brief  Callback for audio channel test volume
--- @param  value - value for slider
--- @param  active - true the slider is being moved.
---
function CBUpdateAudioChannelTestLevel(value, active)
  UpdateSoundLevelControl("audio_channel_test_layer.auto_test_status_group.sound_level_control", value)

  -- TODO: set test volume
end

---
--- @brief  Callback for the ambient noise test boost level
--- @param  value - value for slider
--- @param  active - true the slider is being moved.
---
function CBUpdateAmbientNoiseTestBoostLevel(value, active)
  UpdateSoundLevelControl("ambient_noise_test_layer.boost_level_group.sound_level_control", value)
  
  -- TODO: set test volume
end

---
--- @brief  Callback for the audio channel 1 level
--- @param  value - value for slider
--- @param  active - true the slider is being moved.
---
function CBUpdateAudioChannel1Level(value, active)

  -- TODO: Not action at this time

  -- TODO: set test volume
end

---
--- @brief  Callback for the audio channel 2 level
--- @param  value - value for slider
--- @param  active - true the slider is being moved.
---
function CBUpdateAudioChannel2Level(value, active)
  -- TODO: Not action at this time

  -- TODO: set test volume
end

-- EOF --
