---
-- @package   eg4-crank-ui
-- @file      sbc-scroll-bar.lua
-- @brief     Functionality supporting vertical scroll bar.
--
-- @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 scroll_bar_active = false
local scroll_bar_layer = nil
local scroll_bar_area = nil
local scroll_bar_scale = nil

---
--- @brief Sets the scroll bar position
--- @param value - position to set (0.0 to 1.0)
---
function SBCSetScrollBar(value)
  if scroll_bar_layer == nil then return end

  if value < 0 then 
    value = 0
  elseif value > 1 then 
    value = 1
  end

  local group = scroll_bar_layer..".scroll_bar"
  local background = gre.get_control_attrs(group..".background", "y", "height")
  local slider = gre.get_control_attrs(group..".slider", "height")
  
  local y = math.floor(value * (background.height - slider.height - 10) + 0.5)
  
  gre.set_control_attrs(group..".slider", { y = y + background.y + 5 })
end

---
--- @brief Updates the scroll bar position.
--- @param gre#context mapargs
local function UpdateScrollBar(mapargs)
  local layer = gre.get_layer_attrs(mapargs.context_layer, "y")
  local slider = gre.get_control_attrs(mapargs.context_group..".slider", "height")
  local background = gre.get_control_attrs(mapargs.context_group..".background", "y", "height")

  local y = mapargs.context_event_data.y - (layer.y + background.y + slider.height / 2)
  if y < background.y + 5 then 
    y = background.y + 5
  elseif y > background.y + background.height - slider.height - 5 then 
    y = background.y + background.height - slider.height - 5
  end
  gre.set_control_attrs(mapargs.context_group..".slider", { y = y })

  local callback = _G[gre.get_value(mapargs.context_group..".callback")]
  if type(callback) == "function" then
    local value = (y - (background.y + 5)) / ((background.y + background.height + 5 - slider.height - 10) - (background.y + 5))
    if value < 0 then value = 0 elseif value > 1 then value = 1 end
    mapargs.position = value
    callback(mapargs)
  end
end

---
--- @brief Process press on scrollbar.
--- @param gre#context mapargs
function SBCScrollBarPress(mapargs) 

  local callback = gre.get_value(mapargs.context_screen..".sbc_scroll_bar_callback")
  if callback ~= nil then
    gre.set_value(mapargs.context_group..".callback", callback)
  end

  scroll_bar_active = true
  UpdateScrollBar(mapargs)
end

---
--- @brief Process touch motion on scrollbar.
--- @param gre#context mapargs
function SBCScrollBarMotion(mapargs) 
  if scroll_bar_active then
    UpdateScrollBar(mapargs)
  end
end

---
--- @brief Process release on scrollbar.
--- @param gre#context mapargs
function SBCScrollBarRelease(mapargs) 
  if scroll_bar_active then
    scroll_bar_active = false
    UpdateScrollBar(mapargs)
  end
end

---
--- @brief Process release on scrollbar.
---
local function SetScrollBarFromYOffset()
  if scroll_bar_layer == nil or scroll_bar_area == nil then return end
  local yoffset
  if gredom.get_object(scroll_bar_area):get_type() == gredom.LAYER then
    yoffset = gre.get_layer_attrs(scroll_bar_area, "yoffset").yoffset
  else
    yoffset = gre.get_value(scroll_bar_area..".grd_yoffset")
  end
  SBCSetScrollBar(-yoffset / scroll_bar_scale)
end

---
--- @brief Sets the position of the scroll bar slider or position of scrollable area.
--- @param gre#context mapargs
function SBCSetScrollBarControl(mapargs)
  if scroll_bar_layer == nil or scroll_bar_area == nil then return end
  -- Check if setting from specific position value. 
  if mapargs.position == nil then
    SetScrollBarFromYOffset()
  else
    if gredom.get_object(scroll_bar_area):get_type() == gredom.LAYER then
      gre.set_layer_attrs(scroll_bar_area, { yoffset = -scroll_bar_scale * mapargs.position})
    else
      gre.set_value(scroll_bar_area..".grd_yoffset", -scroll_bar_scale * mapargs.position)
    end
  end
end

---
--- @brief  Updates the visibility of the scroll bar.
---
function SBCShowScrollBar(layer, show)
  local group = layer..".scroll_bar"
  gre.set_value(group..".callback", "SBCSetScrollBarControl")
  gre.set_control_attrs(group..".slider", { y = 15 })
  if show == true or show == 1 then
    scroll_bar_layer = layer
    gre.set_value(group..".grd_hidden", 0)
  else
    scroll_bar_layer = nil
    gre.set_value(group..".grd_hidden", 1)
  end
end

---
--- @brief  Initialise of the scroll bar depending on the hieght of content in the layer being scrolled.
---
local function InitialiseScrollbar(layer)
  if layer == nil then return end
  scroll_bar_scale = GetGroupHeight(scroll_bar_area) - gre.get_layer_attrs(scroll_bar_area, "height").height
  SBCShowScrollBar(layer, scroll_bar_scale > 0)  
  SetScrollBarFromYOffset()
end

---
--- @brief  Locates the layer containing the scroll bar by matching "scroll_bar_*_layer"
---
local function LocateScrollBar(screen)
  local zindex = nil
  local list = GetChildObjectList(screen)
  for i = 1, #list do
    layer = tostring(list[i]):split(".")[2]
    if layer == scroll_bar_area then
      zindex = i
    end 
    if layer:sub(1, 11) == "scroll_bar_" and layer:sub(-6) == "_layer" then
      return layer, zindex
    end
  end
  return nil, zindex
end

---
--- @brief  Initialise the scroll bar for the screen
--- @param gre#context mapargs
function SBCInitialiseScrollbar(mapargs)
  scroll_bar_area = mapargs.context_layer or scroll_bar_area
  if gre.get_layer_attrs(scroll_bar_area, "hidden").hidden == 1 then return end
  local layer, zindex = LocateScrollBar(mapargs.context_screen)
  if layer == nil then
    layer = "scroll_bar_narrow_layer"
    gre.screen_attach_layer(layer, { mapargs.context_screen }, zindex)
    gre.set_layer_attrs_global(layer, { x = 854 - gre.get_layer_attrs(layer, "width").width, y = 94 })
  end
  InitialiseScrollbar(layer)
end

-- EOF --
