--[[
  @package eg4-crank-ui-2
  @file    system-information.lua
  @brief   Handles the system information screen.
]]

-- Option as to whether the tiles should align horizontally or be allowed to stagger
local ALIGN_TILES = 1

---
--- @brief  System informatio icons show always be available
function show_system_information()
  return true
end

---
--- @brief  Data source for the model name
--- @param  control  The control to update
--- @return updated control data
function ds_model_name(control)
  return { value = sbsettings.get_sysinfo_product_id() }
end

---
--- @brief  Data source for the product release
--- @param  control  The control to update
--- @return updated control data
function ds_product_release(control)
  return { value = sbsettings.get_sysinfo_product_release() }
end

---
--- @brief  Data source for the serial number
--- @param  control  The control to update
--- @return updated control data
function ds_serial_number(control)
  return { value = sbsettings.get_sysinfo_serial_number() }
end

---
--- @brief  Data source for the database status
--- @param  control  The control to update
--- @return updated control data
function ds_database_status(control)
  local load_status = sbsettings.get_database_load_status()
  local id = sbsettings.get_database_id()
  local data = {
    alpha_ok = load_status == 0 and 255 or 0,
    alpha_error = load_status == 0 and 0 or 255,
  }

  if load_status == 0 and id ~= "" then
    data.text = sbsettings.get_database_id()
  else
    data.text = gre.get_value(control..".status."..(load_status + 1)..".1")
  end
  return data
end

---
--- @brief  Data source for the settings profile
--- @param  control  The control to update
--- @return updated control data
function ds_settings_profile(control)
  return { value = sbsettings.get_settings_profile() }
end

---
--- @brief  Data source for the configured equipment
--- @param  control  The control to update
--- @return updated control data
function ds_configured_equipment(group)
  local counts = fetch_configured_equipment_counts()
  local fmt = gre.get_value(group..".value.format")
  return {
    ["value.text"] = string.format(fmt, counts.configured, counts.mapped),
  }
end

---
--- @brief  Update the connected equipment status data
--- @param  counts  The counts table for errors, warnings, etc.
local connected_equipment_status = {}
function update_connected_equipment_status_tile(counts)
  connected_equipment_status["ok.text"] = counts.ok
  connected_equipment_status["error.text"] = counts.error
  connected_equipment_status["warning.text"] = counts.warning
  connected_equipment_status["info.text"] = counts.info
end

---
--- @brief  Data source for the connected equipment
--- @param  control  The control to update
--- @return updated control data
function ds_connected_equipment(group)
  return connected_equipment_status
end

---
--- @brief  Update the positions of the tiles on the status screen
--- @param  mapargs  The map arguments from the callback
function cb_update_tile_positions(mapargs)
  local layer = mapargs.context_screen.."_layer"
  local data = {}
  data[mapargs.context_screen.."."..layer..".grd_scroll_enabled"] = 0
  local y1 = 16
  local y2 = 16
  local tiles = layer:get_children()
  for i = #tiles,1,-1 do
    local tile = tiles[i]
    if tile:get_value("grd_hidden") == 0 then
      local tile = tostring(tile)
      local background = tile..".background"
      local height = gre.get_value(background..".grd_height") + 16
      if gre.get_value(background..".grd_width") > 360 then
        if y1 < y2 then
          data[tile..".grd_x"] = 16
          data[tile..".grd_y"] = y2
          y2 = y2 + height
          y1 = y2
        else
          data[tile..".grd_x"] = 16
          data[tile..".grd_y"] = y1
          y1 = y1 + height
          y2 = y1
        end
      else
        if y1 <= y2 then
          data[tile..".grd_x"] = 16
          data[tile..".grd_y"] = y1
          y1 = y1 + height
        else
          data[tile..".grd_x"] = 391
          data[tile..".grd_y"] = y2
          y2 = y2 + height
          if ALIGN_TILES == 1 then
            if y1 < y2 then
              y1 = y2
            else
              y2 = y1
            end
          end
        end
      end
    end
  end
  gre.set_data(data)
end

---
--- @brief  Process the page navigation operation
--- @param  dir  The direction to navigate in
--- @param  context_screen  The context screen [optional]
local navigation_index = 1
local function system_info_page_navigate(dir, context_screen)
  navigation_index = navigation_index + dir
  local layer = context_screen.."_layer"
  local page_height = gre.get_value(context_screen.."."..layer..".grd_height")
  local index = 1
  local y = 16
  local h = 0
  local at_end = false
  if navigation_index > 1 then
    local tiles = layer:get_children()
    at_end = true
    for i = #tiles,1,-1 do
      local tile = tiles[i]
      if tile:get_value("grd_hidden") == 0 then
        local tile = tostring(tile)
        local next = gre.get_value(tile..".grd_y")
        local height = gre.get_value(tile..".background.grd_height")

        if next == y and h < height then
          h = height
        elseif next > y then
          if index >= navigation_index then
            at_end = false
            break
          end
          y = next
          h = height
          index = index + 1
        end
      end
    end
  end

  if at_end then
    y = y - (page_height - h - 32)
  end

  local nav_layer = context_screen.."_page_navigation_layer"
  navigation_index = index
  local data = {
    [nav_layer..".up.alpha_disabled"] = (index == 1) and gre.get_value(nav_layer..".control_inactive_alpha") or 255,
    [nav_layer..".up.grd_active"] = index == 1 and 0 or 1,
    [nav_layer..".down.alpha_disabled"] = at_end and gre.get_value(nav_layer..".control_inactive_alpha") or 255,
    [nav_layer..".down.grd_active"] = at_end and 0 or 1,
  }
  gre.set_data(data)

  animate_scroll(context_screen.."."..layer, 16 - y)

end

---
--- @brief  Callback to process the page navigation operation
--- @param  mapargs  The map arguments from the callback
function cb_system_info_page_navigate(mapargs)
  system_info_page_navigate(mapargs.dir, mapargs.context_screen)
end

---
--- @brief  Update the system information screen tiles
function cb_update_system_information(mapargs)
  cb_update_tile_positions(mapargs)
  system_info_page_navigate(0, mapargs.context_screen)
  cb_stop_data_source_timers()
  start_data_sources(mapargs.context_screen)
end

---
--- @brief  Initialise the system information screen
function cb_init_system_information(mapargs)
  if gre.get_value(mapargs.context_screen..".home_navigation") == 1 then
    gre.set_value(
      mapargs.context_screen..".home_navigation", 0,
      mapargs.context_screen.."."..mapargs.context_screen.."_layer.grd_yoffset", 0
    )
    navigation_index = 1
  end
  cb_update_system_information(mapargs)
end

---
--- @brief Indicates that the driver reboot icon should be visible
--- @return true - show driver reboot control
function show_driver_reboot(mapargs)
  return sbsettings.get_allow_driver_reboot() ~= 0
end

---
--- @brief Start the reboot process.
function cb_reboot_device(mapargs)
  cb_close_notification(mapargs)
  notify("rebooting_device")
  os.execute('reboot')
end

---
--- @brief Start the start USB disaster recovery.
local function start_usb_disaster_recovery()
  notify("rebooting_device")
  os.execute('start-disaster-recovery')
end

---
--- @brief Confirm USB disaster recovery.
local usb_disaster_recovery_notification
function cb_start_disaster_recovery()
  if fileinfo:usb_mounted() then
    -- Start the USB disaster recovery process immediately
    start_usb_disaster_recovery()
    return
  end
  usb_disaster_recovery_notification = notify("start_usb_disaster_recovery")
  send_event("suspend_usb_device_mount_processing", tostring(10000))
end

---
--- @brief Start the start USB disaster recovery.
function cb_start_usb_disaster_recovery()
  if usb_disaster_recovery_notification == nil or not gre.get_value(usb_disaster_recovery_notification..".grd_hidden") then return end
  start_usb_disaster_recovery()
end
