--[[
  @package    eg4-crank-ui-2
  @file       recent-destinations.lua
  @brief      Handles the recent destinations list
]]

local RECENT_LIST_LAYER = "recent_destinations_layer"
local RECENT_LIST_CONTROL = RECENT_LIST_LAYER..".list"
local MAX_RECENT_LIST_DEPTH = 15

local info = {}

---
--- @brief  Get the recent destinations filepath 
local function get_recent_filepath()
  return get_data_folder() .. "recent-list-destination-id"
end
---
--- @brief  Validates TAB delimited destination item
---
--- formatted as [<route-code><space>]<destination-code><space><space><driver message>
local function validate_recent_item(item)
  item = item:split("  ", 1)
  if #item < 1 then return false end
  return parse_destination_id(item[1]) ~= nil
end

---
--- @brief  Reads the recent destination list from the specified file.
local function read_recent_list()
  if info.filepath ~= nil then return end
  info.filepath = get_recent_filepath()
  info.lines = {}
  local file = io.open(info.filepath, "r")
  if not file then return end
  for line in file:lines() do
    if line ~= "" then
      table.insert(info.lines, line)
    end
  end
  file:close()
end

---
--- @brief  Write the recent list to file
---
local function write_recent_list()
  if info.filepath == nil then return end
  local file = io.open(info.filepath, "w")
  if file == nil then return end
  for _,line in ipairs(info.lines) do
    file:write(line.."\n")
  end
  file:close()
end

---
--- @brief  Clean the recent list
---
local function clean_recent_list()
  read_recent_list()

  local control = RECENT_LIST_CONTROL
  local lines = {}
  for _,line in ipairs(info.lines) do
    if validate_recent_item(line) then
      table.insert(lines, line)
    end
  end
  if #info.lines == #lines then return end
  info.lines = lines
  write_recent_list()
end

---
--- @brief  Update the text for the recent destinations tab
---
local function update_recent_tab_qty()
  local control = "browser_heading_layer.tabs.recent_destinations"
  local format = gre.get_value(control..".format")
  gre.set_value(control..".text", format:format(#info.lines))
end

---
--- @brief  Get the number of items per page
--- @param  context_screen  current screen
--- @return The number of items per page
local function get_items_per_page()
  local screen_height = get_screen_height()
  local top = gre.get_value(RECENT_LIST_CONTROL..".grd_y")
  local cell_height = gre.get_table_cell_attrs(RECENT_LIST_CONTROL, 1, 1, "height").height
  return math.floor((screen_height - top) / cell_height)
end

---
--- @brief  Set the navigation controls depending on the current index
--- @param  pages           The number of pages
--- @param  items_per_page  The number of items per page
--- @param  index           The current index
local function set_navigation_controls()

  local navigation_group = RECENT_LIST_LAYER..".navigate"

  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

function update_recent_list()
  set_navigation_controls()
  animate_variable(RECENT_LIST_CONTROL..".scrolling_bind", info.page * info.items_per_page)
end

function cb_update_recent_list(mapargs)
  local data = {}
  local rows = info.items_per_page
  local index = gre.get_value(mapargs.context_control..".scrolling_bind")
  for i=1,rows do
    if info.lines[index + i] == nil then rows = i - 1 break end
    data[mapargs.context_control..".text."..i..".1"] = info.lines[index + i]
    data[mapargs.context_control..".destination_id."..i..".1"] = info.lines[index + i]
  end
  gre.set_table_attrs(mapargs.context_control, {
    hidden = (rows == 0) and 1 or 0,
    rows = rows
  })
  gre.set_data(data)
end

---
--- @brief  Callback to read the recent destination list from the specified file.
--- @param  mapargs  callback context
function cb_initialise_recent_list(mapargs)
  clean_recent_list()
  update_recent_tab_qty()
  info.page = 0
  info.items_per_page = get_items_per_page()
  info.pages = math.ceil(#info.lines / info.items_per_page)

  gre.set_value(RECENT_LIST_CONTROL..".scrolling_bind", 0)
  update_recent_list()
  cb_update_recent_list({ context_control = RECENT_LIST_CONTROL })
end

---
--- @brief  Navigate the recent destination list up
--- @param  mapargs  callback context
function cb_recent_destination_navigate_up(mapargs)
  info.page = (info.page > 0) and (info.page - 1) or 0
  update_recent_list()
end

---
--- @brief  Navigate the recent destination list down
--- @param  mapargs  callback context
function cb_recent_destination_navigate_down(mapargs)
  info.page = (info.page < info.pages - 1) and (info.page + 1) or info.page
  update_recent_list()
end

---
--- @brief  Create a recent destination item from a destination ID and driver text. The first
---         line being the destination ID and the subsequent lines being the driver text.
--- @param  value  The destination ID and driver text to format.
--- @return string formatted as [<route-code><space>]<destination-code><space><space><driver message>
local function create_recent_item(value)
  local count = 0
  local sep = ""
  local item = ""
  for _,line in ipairs(value:lines()) do
    item = item..sep..line
    count = count + 1
    if count == 3 then break end
    sep = (sep == "") and "  " or  " "
  end
  return item
end

---
--- @brief  Add value to recent list. If the value is already in the list, it
---         is moved to the top of the list.
--- @param  value The destination ID and driver text to add to the recent list.
local function add_recent_list_item(value)
  for i,line in ipairs(info.lines) do
    if line == value then
      table.remove(info.lines, i)
      break
    end
  end
  table.insert(info.lines, 1, value)
  while #info.lines > MAX_RECENT_LIST_DEPTH do
    table.remove(info.lines, #info.lines)
  end
end

---
--- @brief  Adds value text to the recent lists file, removing the oldest item if required.
--- @param  value
---
function add_recent_list(value)
  if not value or value == "" then return end

  -- TODO Do not add this item to the recent list unless a new item has not been received for 5 seconds

  value = create_recent_item(value)

  -- Reject  invalid destination IDs
  if not validate_recent_item(value) then return end

  read_recent_list()

  add_recent_list_item(value)

  write_recent_list()
  update_recent_tab_qty()
end

---
--- @brief  Clear the recent destinations list
function clear_recent_destinations()
  os.remove(get_recent_filepath())
  info = {}
end