---
--- @package   eg4-crank-ui
--- @file      ethernet-port-settings.lua
--- @brief     functionality related ethernet port settings screen
---
--- @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.
---

-- Flag to indicate that ethernet settings data has changed.
local apply_ethernet_data

---
--- @brief  Fetch long description of network port.
---
function GetNetworkItemDescription(port_id)
  local prefix = port_id:sub(1,2)
  if prefix == "et" or prefix == "en" then
    port_id = gre.get_value("mo_network_status.ethernet_port").." ("..port_id..")"
  elseif prefix == "wl" then
    port_id = gre.get_value("mo_network_status.wifi_port").." ("..port_id..")"
  end
  return port_id
end

---
--- @brief  Initialises the ethernet port list layer with available ethernet ports.
--- @param  gre#context mapargs
---
function CBInitEthernetPortList(mapargs)
  if mapargs.context_event_data.text == nil then
    return
  end

  local list = assert(loadstring(mapargs.context_event_data.text))()
  if list == nil then
    return
  end

  local layer = mapargs.context_layer
  local group = layer..".group"
  local control = group..".items"
  local search_id
  for _,port_id in pairs(list) do
    if port_id:sub(1,2) ~= "ww" and port_id:sub(1,2) ~= "pp" then
        -- Determine if item is already present.
      local index = 0
      repeat
        index = index + 1
        search_id = gre.get_value(string.format(control..".port_id.%d.1", index))
      until search_id == nil or search_id == "" or port_id == search_id

      if search_id ~= port_id then
        -- Add the new item to the menu list.
        gre.set_value(string.format(control..".port_id.%d.1", index), port_id)
        gre.set_value(string.format(control..".text.%d.1", index), GetNetworkItemDescription(port_id))

        gre.set_table_attrs(control, {height = index * 81})
        gre.set_table_attrs(control, {rows = index})

        SBCInitialiseScrollbar(mapargs)
      end
    end
  end
end

---
--- @brief  Initialises the ethernet port setting screen
--- @param  gre#context mapargs
---
function CBInitEthernetPortSettingScreen(mapargs)
  local port_id = gre.get_value("mo_network_ethernet_port.port_id")

  local prefix = port_id:sub(1, 2)
  gre.set_layer_attrs_global("ethernet_port_layer", { hidden = (prefix == "et" or prefix == "en") and 0 or 1 })
  gre.set_layer_attrs_global("wifi_port_layer", { hidden = (prefix == "wl") and 0 or 1 })

  local data = {}

  local mode = sbsettings.get_ethernet_port_mode(port_id)
  
  -- Show lease when mode is SERVER
  local hide_lease = (mode == 3) and 0 or 1
  data["ethernet_port_layer.menu_items.ethernet_port_dhcp_lease_start.grd_hidden"] = hide_lease
  data["ethernet_port_layer.menu_items.ethernet_port_dhcp_lease_end.grd_hidden"] = hide_lease

  -- Hide static items when mode is DHCP
  local hide_static = (mode == 0) and 1 or 0
  data["ethernet_port_layer.menu_items.ethernet_port_address.grd_hidden"] = hide_static
  data["ethernet_port_layer.menu_items.ethernet_port_netmask.grd_hidden"] = hide_static
  data["ethernet_port_layer.menu_items.ethernet_port_gateway.grd_hidden"] = hide_static
  data["wifi_port_layer.menu_items.ethernet_port_address.grd_hidden"] = hide_static
  data["wifi_port_layer.menu_items.ethernet_port_netmask.grd_hidden"] = hide_static
  data["wifi_port_layer.menu_items.ethernet_port_gateway.grd_hidden"] = hide_static
  
  gre.set_data(data)

  CBMenuUpdate(mapargs)
end

---
--- @brief  Initialises the wifi settings screen as a result of a change in the wifi port mode.
--- @param  gre#context mapargs
function CBUpdateWifiInterfaceMode(mapargs)
  CBSetApplyEthernetData(mapargs)
  CBInitEthernetPortSettingScreen(mapargs)
end

---
--- @brief  Processes the ethernet port settings and populates the DNS Server list screen.
--- @param  gre#context mapargs
---
function CBReadEthernetPortDNSServerSetting(mapargs)
  local port_id = gre.get_value("mo_network_ethernet_port.port_id")

  local layer = "dns_servers_layer"
  local control = layer..".group.items"

  local data = {}

  -- Add the IP addresses.
  local index = 0
  local dns_servers = sbsettings.get_ethernet_port_dns_servers_table(port_id)
  if dns_servers ~= nil then
    for _,value in ipairs(dns_servers) do
      index = index + 1
      data[string.format(control..".text.%d.1", index)] = value
    end
  end

  gre.set_table_attrs(control, { height = index * 81, rows = index })

  gre.set_data(data)

  SBCInitialiseScrollbar(mapargs)
end

---
--- @brief  Add DNS server to ethernet port
--- @param  gre#context mapargs
---
function CBAddDNSServer(mapargs)
  local port_id = gre.get_value("mo_network_ethernet_port.port_id")
  local value = mapargs.value
  sbsettings.add_ethernet_port_dns_server(value, port_id)
  apply_ethernet_data = true
  TransitionBackScreen(mapargs)
end

---
--- @brief  Delete DNS server from ethernet port
--- @param  gre#context mapargs
---
function CBDeleteDNSServer(mapargs)
  local port_id = gre.get_value("mo_network_ethernet_port.port_id")
  local value = gre.get_value(mapargs.context_control .. ".text." .. mapargs.context_row .. ".1")
  sbsettings.del_ethernet_port_dns_server(value, port_id)
  apply_ethernet_data = true
  CBReadEthernetPortDNSServerSetting(mapargs)
end

---
--- @brief  Set flag to indicate that ethernet requires apply
---
function CBSetApplyEthernetData(mapargs)
  apply_ethernet_data = true
end

---
--- @brief  IP Address entry check and send IP Address to backend
--- @param  gre#context mapargs
---
function CBWriteIPAddress(mapargs)
  if mapargs.value == "" or GetIPType(mapargs.value) == "IPv4" then
    local port_id = gre.get_value("mo_network_ethernet_port.port_id")
    sbsettings.set_ethernet_port_address(mapargs.value, port_id)
    apply_ethernet_data = true
    TransitionBackScreen(mapargs)
  else
    gre.set_control_attrs("numeric_entry_layer.text_entry_group.error_icon", { hidden = 0 })
  end
end

local function ConvertIPv4ToInteger(ip_text)
  local o1,o2,o3,o4 = ip_text:match("(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)" )
  return 2^24*o1 + 2^16*o2 + 2^8*o3 + o4
end

---
--- @brief  Converts number 0-32 to netmask value
---
local function ConvertToNetmask(ip_text)
  if GetIPType(ip_text) ~= "IPv4" then
    return nil
  end
  local ip_address = ConvertIPv4ToInteger(ip_text)
  local netmask = 0
  for i=32,1,-1 do
    if ip_address % 2 == 1 then
      netmask = i
      for j=i,1,-1 do
        if ip_address % 2 ~= 1 then
          return nil
        end
        ip_address = math.floor(ip_address / 2)
      end
      break
    end
    ip_address = math.floor(ip_address / 2)
  end
  return netmask
end

---
--- @brief  Writes the netmask value to backend. Combines with the current IPv4 value.
--- @param  gre#context mapargs
---
function CBWriteNetmask(mapargs)
  local netmask = ConvertToNetmask(mapargs.value)
  local port_id = gre.get_value("mo_network_ethernet_port.port_id")
  local ip_address = sbsettings.get_ethernet_port_address(port_id)
  if netmask ~= nil and GetIPType(ip_address) == "IPv4"then
    sbsettings.set_ethernet_port_netmask(mapargs.value, port_id)
    apply_ethernet_data = true
    TransitionBackScreen(mapargs)
  else
    gre.set_control_attrs("numeric_entry_layer.text_entry_group.error_icon", { hidden = 0 })
  end
end

---
--- @brief Write the IPv4 gateway address to the backend
--- @param  gre#context mapargs
---
function CBWriteGatewayAddress(mapargs)
  if mapargs.value == "" or GetIPType(mapargs.value) == "IPv4" then
    local port_id = gre.get_value("mo_network_ethernet_port.port_id")
    sbsettings.set_ethernet_port_gateway(mapargs.value, port_id)
    apply_ethernet_data = true
    TransitionBackScreen(mapargs)
  else
    gre.set_control_attrs("numeric_entry_layer.text_entry_group.error_icon", { hidden = 0 })
  end
end

---
--- @brief  Sends event to backend that applies the ethernet settings.
--- @param  gre#context mapargs
---
function CBApplyEthernetSettings(mapargs)
  if apply_ethernet_data then
    apply_ethernet_data = nil
    SendEvent("settings_updated")
  end
end

---
--- @brief  Get auto up which is combined with enabled
--- @param  gre#context mapargs
---
function get_ethernet_port_enabled(port_id)
  return (sbsettings.get_ethernet_port_auto_up(port_id) == 0 or 
          sbsettings.get_ethernet_port_enabled(port_id) == 0) and 0 or 1
end

---
--- @brief  Set auto up and also enable network.
--- @param  gre#context mapargs
---
function set_ethernet_port_enabled(value, port_id)
  if value ~= 0 then
    sbsettings.set_ethernet_port_enabled(1, port_id)
  end
  sbsettings.set_ethernet_port_auto_up(value, port_id)
end

---
--- @brief  Disables network options if no networking is available
--- @param  gre#context mapargs
---
function CBDisableNetworkOptions(mapargs)
  gre.set_control_attrs("system_configuration_layer.menu_items.network", { hidden = 1 })
end

---
--- @brief  Enables network options if networking is available
--- @param  gre#context mapargs
---
function CBEnableNetworkOptions(mapargs)
  gre.set_control_attrs("system_configuration_layer.menu_items.network", { hidden = 0 })
end

---
--- @brief  Populate wifi access points with current ap items.
---
--- @param gre#context mapargs
function CBPopulateWifiAccessPoints(mapargs)
  local ap_list = sbsettings.read_wifi_access_point_nodes()

  -- Add the new item to the menu list.
  local group = "wifi_access_points_layer.group"
  local control = group..".items"
  local data = {}
  local index = 0
  for _, ap in pairs(ap_list) do
    index = index + 1
    data[string.format(control..".text.%d.1", index)] = ap
    data[string.format(control..".value.%d.1", index)] = ""
  end

  data[group .. ".add_new_wifi_access_point.grd_y"] = index * 81
  gre.set_data(data)

  gre.set_table_attrs(control, { height = index * 81, rows = index })

  SBCInitialiseScrollbar(mapargs)

end

---
--- @brief  Write wifi psk for current ssid.
---
--- @param gre#context mapargs
function CBWriteWifiPsk(mapargs)
  if mapargs.value:len() < 8 then
    gre.set_value(mapargs.context_group .. ".text_entry_info_control.grd_hidden", 0)
    return
  end
  sbsettings.set_wifi_psk(mapargs.value, gre.get_value("wifi_access_point_layer.menu_items.port_id"))
  CBSetApplyEthernetData(mapargs)
  TransitionBackScreen(mapargs)
end

---
--- @brief  Start the creation of a new wifi access point.
---
--- @param gre#context mapargs
function CBWifiNewAccessPoint(mapargs)
  if mapargs.value:len() < 2 then
    gre.set_value(mapargs.context_group .. ".text_entry_info_control.grd_hidden", 0)
    return
  end
  gre.set_value("mo_network_wifi_access_point.banner_heading", mapargs.value,
                "wifi_access_point_layer.menu_items.port_id", mapargs.value)
  CBSetApplyEthernetData(mapargs)
  ScreenTransition("mo_network_wifi_access_point")
end

--- @param gre#context mapargs
function CBWifiForgetSSID(mapargs)
  local port_id = gre.get_value(mapargs.context_group..".port_id")
  sbsettings.del_wifi_access_point_node(port_id)
  CBSetApplyEthernetData(mapargs)
  TransitionBackScreen(mapargs)
end

---
--- @brief  Check if wifi hardware is present using htc.
---
--- @param gre#context mapargs
local wifi_present
local function WifiPresent()
  if not wifi_not_present then
    local result = fileinfo:execute("htcdev -l | grep \"platform/network/wl\"")
    wifi_not_present = (result == "") and 0 or 1
  end
  return wifi_not_present
end

---
--- @brief  Initialise the network menu options.
---
--- @param gre#context mapargs
function CBInitNetworkMenu(mapargs)
  gre.set_value("network_layer.menu_items.wifi_access_points.grd_hidden", 1 - WifiPresent());
  CBMenuUpdate(mapargs)
end

---
--- @brief  Update the wifi icon
---
--- @param gre#context mapargs
local function getWifiStatus()
  for _,port in ipairs(sbsettings.get_netinfo_port_list()) do
    if port:sub(1, 2) == "wl" then
      return sbsettings.get_wifi_status(port)
    end
  end
end

---
--- @brief  Update the wifi icon
---
--- @param gre#context mapargs
local wifi_status = {}
function CBUpdateWifiStatusIcon(mapargs)
  gre.thread_create(function()
    local status = getWifiStatus()

    local control = "home_status_layer.status.wifi"
    if not status or status.up == 0 then
      gre.set_value(control..".grd_hidden", 1)
      return 
    end
  
    --  if not status.ssid or status.ssid 
    local connected = status.running ~= 0
    gre.set_value(
      control..".grd_hidden", 0,
      control..".bar_4", (connected and status.signal_dbm > -50) and 255 or 0,
      control..".bar_3", (connected and status.signal_dbm > -70) and 255 or 0,
      control..".bar_2", (connected and status.signal_dbm > -90) and 255 or 0,
      control..".bar_1", (connected and status.signal_dbm <= -90) and 255 or 0,
      control..".down", (status.up == 0) and 255 or 0,
      control..".not_connected", (status.running == 0) and 255 or 0,
      control..".rx_active", (wifi_status.rx_packets ~= status.rx_packets) and 255 or 32,
      control..".tx_active", (wifi_status.tx_packets ~= status.tx_packets) and 255 or 32
    )
    wifi_status = status
  end)
end

---
--- @brief  Update the GSM icon [DEPRECATED]
-- TODO remove this function
function CBUpdateGsmStatusIcon()
end

---
--- @brief  Initialise the network status port screen
---
--- @param gre#context mapargs
function CBInitNetworkStatusPortScreen(mapargs)
  local port_id = gre.get_value("mo_network_status_port.port_id")
  gre.set_value("network_status_layer.menu_items.netinfo_ssid.grd_hidden", port_id:sub(1,2) == "wl" and 0 or 1)
  CBMenuUpdate(mapargs)
end

---
--- @brief  Get the Network name (SSID) for port
---
--- @param gre#context mapargs
function get_netinfo_ssid(port_id)
  local status = sbsettings.get_wifi_status(port_id)
  return status and status.ssid or ""
end

---
--- @brief  Initialise the network status port screen
---
--- @param gre#context mapargs
function CBInitNetworkStatusPortScreen(mapargs)
  local port_id = gre.get_value("mo_network_status_port.port_id")
  gre.set_value("network_status_layer.menu_items.netinfo_ssid.grd_hidden", port_id:sub(1,2) == "wl" and 0 or 1)
  CBMenuUpdate(mapargs)
end

---
--- @brief  Get the Network name (SSID) for port
---
--- @param gre#context mapargs
function get_netinfo_ssid(port_id)
    local status = sbsettings.get_wifi_status(port_id)
    return status and status.ssid or ""
end

---
--- @brief  IP Address entry check and send IP Address to backend
--- @param  gre#context mapargs
---
function CBWriteLeaseStartIPAddress(mapargs)
  if mapargs.value == "" or GetIPType(mapargs.value) == "IPv4" then
    local port_id = gre.get_value("mo_network_ethernet_port.port_id")
    sbsettings.set_ethernet_port_dhcp_lease_start(mapargs.value, port_id)
    apply_ethernet_data = true
    TransitionBackScreen(mapargs)
  else
    gre.set_control_attrs("numeric_entry_layer.text_entry_group.error_icon", { hidden = 0 })
  end
end

---
--- @brief  IP Address entry check and send IP Address to backend
--- @param  gre#context mapargs
---
function CBWriteLeaseEndIPAddress(mapargs)
  if mapargs.value == "" or GetIPType(mapargs.value) == "IPv4" then
    local port_id = gre.get_value("mo_network_ethernet_port.port_id")
    sbsettings.set_ethernet_port_dhcp_lease_end(mapargs.value, port_id)
    apply_ethernet_data = true
    TransitionBackScreen(mapargs)
  else
    gre.set_control_attrs("numeric_entry_layer.text_entry_group.error_icon", { hidden = 0 })
  end
end

-- EOF --
