--[[
  @package    eg4-crank-ui-2
  @file       time-zone-list.lua
  @brief      Handles the time zone browser list
]]

local BROWSE_LAYER = "time_zone_browse_layer"
local BROWSE_LIST_CONTROL = BROWSE_LAYER..".list"
local NAVIGATION_GROUP = BROWSE_LAYER..".navigate"
local TIME_ZONE_FOLDER = "/usr/share/zoneinfo/"
local LOCALTIME_SYMLINK = "/mnt/hos-state/root-overlay/etc/localtime"

local info = {}

---
--- @brief  Read time zone setting
--- @return time zone setting or nil if not set
local function read_time_zone_setting()
  local value = fileinfo:execute('readlink -f /etc/localtime')
  if #value == 0 then return end
  value = value[1]:split(TIME_ZONE_FOLDER, 1)
  if #value ~= 2 then return end
  return value[2]
end

---
--- @brief  get time zone setting
--- @return time zone setting or default UTC text
function get_time_zone()
  return read_time_zone_setting() or gre.get_value("date_time_settings_menu_layer.menu.time_zone.utc_text")
end

---
--- @brief  Time zone has changed externally, force update.
function cb_time_zone_changed()
  sbmodule.setTimeZone(read_time_zone_setting())
end

local function read_country_codes()
  local filename = TIME_ZONE_FOLDER.."iso3166.tab"
  local file = io.open(filename, "r")
  if not file then
    return nil, "Could not open file: " .. filename
  end

  local codes = {}
  for line in file:lines() do
    line = line:split("#")[1]  -- Remove comments
    line = line:split("\t")
    if #line == 2 then
      codes[line[1]] = line[2]
    end
  end

  file:close()
  return codes
end

local function read_zone_tab()
  local country_codes = read_country_codes()
  local filename = TIME_ZONE_FOLDER.."zone.tab"
  local file = io.open(filename, "r")
  if not file then return end

  local zones = {}
  for line in file:lines() do
    line = line:split("#")[1]
    line = line:split("\t")
    if #line >= 3 then
      local location = line[3]:gsub("_", " ")
      local city = location:split("/")
      local city = city[#city]
      local country = country_codes[line[1]] or ""
      local location = city..", "..country
      table.insert(zones, {
        location = location,
        time_zone = line[3],
      })
    end
  end
  table.sort(zones, function(a, b)
    return a.location < b.location
  end)
  file:close()
  return zones
end

---
--- @brief  Read the time zone list and filter it based on the last filter value.
local function read_time_zone_info()
  if info.zones == nil then 
    info.zones = read_zone_tab()
  end

  if info.last_filter_value == "" then
    info.list = info.zones
    return
  end

  -- Convert filter to pattern match characters in the order they appear
  local filter = ""
  local sep = ""
  for word in info.last_filter_value:gmatch("%S+") do
    filter = filter..sep..word
    sep = ".*"
  end

  info.list = {}
  for _,item in ipairs(info.zones) do
    if item.location:remove_accents():upper():find(filter) then
      table.insert(info.list, item)
    end
  end
end

---
--- @brief  Get the height of the recent list layer
--- @return The layer height
local function get_table_height()
  return gre.get_value(BROWSE_LIST_CONTROL..".grd_height")
end

---
--- @brief  Get the number of items per page
--- @return The number of items per page
local function get_items_per_page()
  local control_height = gre.get_table_cell_attrs(BROWSE_LIST_CONTROL, 1, 1, "height").height
  return math.floor(get_table_height() / control_height + 0.5)
end

---
--- @brief  Set the navigation controls depending on the current index
local function set_navigation_controls()
  local scroll_marker_top = 0
  if info.pages > 1 then
    local height = gre.get_value(NAVIGATION_GROUP..".sep.grd_height")
    local sep = gre.get_value(NAVIGATION_GROUP..".sep.height")
    scroll_marker_top = math.floor((height - sep) * info.page / (info.pages - 1) + 0.5)
  end

  local data = {}
  data[NAVIGATION_GROUP..".sep.y"] = scroll_marker_top
  data[NAVIGATION_GROUP..".up.grd_active"] = info.page > 0
  data[NAVIGATION_GROUP..".up.alpha_disabled"] = info.page > 0 and 255 or 83
  data[NAVIGATION_GROUP..".down.grd_active"] = info.page < info.pages - 1
  data[NAVIGATION_GROUP..".down.alpha_disabled"] = (info.page < info.pages - 1) and 255 or 83
  data[NAVIGATION_GROUP..".paging.grd_hidden"] = info.pages < 2
  data[NAVIGATION_GROUP..".paging.rich_text"] = make_page_text(info.page + 1, info.pages)

  gre.set_data(data)
end

---
--- @brief  Update the time zone list table.
local function update_time_zone_list()
  set_navigation_controls()
  animate_variable(BROWSE_LIST_CONTROL..".scrolling_bind", info.page * info.items_per_page)
end

---
--- @brief  Update the time zone item display page
--- @param mapargs  The map arguments containing the context control
function cb_update_time_zone_list(mapargs)
  local color_pressed = gre.get_value("color_list_pressed_text_option")
  local color_normal = gre.get_value("color_list_text_option")
  local color_select = gre.get_value("color_list_text_title")

  local data = {}
  local rows = info.items_per_page
  local index = gre.get_value(mapargs.context_control..".scrolling_bind")
  for i=1,info.items_per_page do
    local item = info.list[index + i]
    if item == nil then rows = i - 1 break end
    local text = item.location
    data[mapargs.context_control..".info_id."..i..".1"] = text:match("^(%w+) ")
    data[mapargs.context_control..".text."..i..".1"] = highlight_filtered_text(text, info.last_filter_value, color_normal, color_select)
    data[mapargs.context_control..".released."..i..".1"] = data[mapargs.context_control..".text."..i..".1"]
    data[mapargs.context_control..".pressed."..i..".1"] = highlight_filtered_text(text, info.last_filter_value, color_pressed, color_pressed)
  end
  gre.set_data(data)
  gre.set_table_attrs(mapargs.context_control, { rows = rows, hidden = (rows == 0) })
end

---
--- @brief  Either starts the selection process or against the current search string
--- @param  filter  The filter to apply
function cb_initialise_time_zone_list(filter)
  if type(filter) == "table" then
    -- Clear the info when called on screen show
    info = {}
  end
  info.last_filter_value = type(filter) == "string" and filter or ""

  read_time_zone_info()

  -- Center on previously highlighted item if present
  info.index = 1
  info.size = #info.list
  info.items_per_page = get_items_per_page()
  info.pages = math.ceil(info.size / info.items_per_page)
  info.page = 0

  gre.set_value(BROWSE_LIST_CONTROL..".scrolling_bind", 0)
  update_time_zone_list()
  cb_update_time_zone_list({ context_control = BROWSE_LIST_CONTROL })
end

---
--- @brief  Callback to perform the navigation repeat
--- @param  dir  The direction to navigate (-1 for up, 1 for down)
--- @return true if the navigation was rejected
function cb_time_zone_list_navigate(mapargs)
  info.page = info.page + mapargs.dir
  if info.page < 0 then
    info.page = 0
    return true
  end
  if info.page >= info.pages then
    info.page = info.pages - 1
    return true
  end
  update_time_zone_list()
end


---
--- @brief  Process selected time zone
--- @param gre#context mapargs
function cb_time_zone_item_selected(mapargs)
  local time_zone = info.list[info.items_per_page * info.page + mapargs.context_row].time_zone
  -- Set the new time zone.
  if not sbsettings.test_mode then
    os.execute("ln -snf "..TIME_ZONE_FOLDER..time_zone.." "..LOCALTIME_SYMLINK)
  end
  -- Reload time zone data (strip leading forward slash)
  sbmodule.setTimeZone(time_zone)
  send_event("time_zone_changed")
  cb_back_screen(mapargs)
end



