--[[
  @package    eg4-crank-ui
  @file       test/settings.lua
  @brief      Adds the required items for sbsettings when the library fails to load.
              This is used for testing purposes on the desktop.
]]

require "utils/table-utils"

local sbsettings = {}

sbsettings.test_mode = true

local settings = {}


settings.beeps_on_usb_update = 1
function sbsettings.get_beeps_on_usb_update()
  return settings.beeps_on_usb_update
end
function sbsettings.set_beeps_on_usb_update(value)
  settings.beeps_on_usb_update = value
end

settings.audio_list = {}
function sbsettings.read_audio_list()
  return settings.audio_list
end

settings.home_screen_icon_bar_qty = 4
function sbsettings.get_home_screen_icon_bar_qty()
  return settings.home_screen_icon_bar_qty
end
function sbsettings.set_home_screen_icon_bar_qty(value)
  settings.home_screen_icon_bar_qty = value
end

settings.show_profiles_list_by_default = 0
function sbsettings.get_show_profiles_list_by_default()
  return settings.show_profiles_list_by_default
end
function sbsettings.set_show_profiles_list_by_default(value)
  settings.show_profiles_list_by_default = value
end

-- Comma separated list of destination codes
settings.browser_destination_hide_list = {}
function sbsettings.get_browser_destination_hide_list()
  return settings.browser_destination_hide_list
end

-- Comma separated list of information codes
settings.browser_information_hide_list = {}
function sbsettings.get_browser_information_hide_list()
  return settings.browser_information_hide_list
end

settings.touch_screen_click = 1
function sbsettings.get_touch_screen_click()
  return settings.touch_screen_click
end
function sbsettings.set_touch_screen_click(value)
  settings.touch_screen_click = value
end

settings.profiles = {
  [0] = 'CIT405',
  [1] = 'CIT327',
  [2] = 'GX127',
  [3] = 'CYTIOS',
  [4] = '84X7',
  [5] = '',
  [7] = '190DOT',
}

function sbsettings.read_profile_id_list()
  return settings.profiles
end

function sbsettings.get_database_id()
  return settings.profiles[settings.settings_profile]
end

settings.preset_hcp_address_list ={
  [0] = {},
  [3] = {},
  [5] = {},
  [6] = {},
}
function sbsettings.read_preset_hcp_address_list()
  local list = {}
  for index,_ in pairs(settings.preset_hcp_address_list) do
    table.insert(list, index)
  end
  table.sort(list)
  return list
end

settings.configured_devices = 	{
  ['*'] = {
    [0] = {
      ['type'] = 1,
      ['sign_page_lookup'] = '0',
      ['route_number_alignment'] = 1,
      ['height'] = 0,
      ['location'] = 0,
      ['width'] = 0
    },
    [3] = {
      ['type'] = 1,
      ['sign_page_lookup'] = '3',
      ['route_number_alignment'] = 2,
      ['height'] = 0,
      ['location'] = 0,
      ['width'] = 0
    },
    [5] = {
      ['type'] = 1,
      ['sign_page_lookup'] = '5',
      ['route_number_alignment'] = 2,
      ['height'] = 0,
      ['location'] = 0,
      ['width'] = 0
    },
    [6] = {
      ['type'] = 1,
      ['sign_page_lookup'] = '3',
      ['route_number_alignment'] = 2,
      ['height'] = 0,
      ['location'] = 0,
      ['width'] = 0
    },
    [7] = {
      ['type'] = 1,
      ['sign_page_lookup'] = '5',
      ['route_number_alignment'] = 2,
      ['height'] = 0,
      ['location'] = 0,
      ['width'] = 0
    },
    [8] = {
      ['type'] = 1,
      ['sign_page_lookup'] = '6',
      ['route_number_alignment'] = 2,
      ['height'] = 0,
      ['location'] = 0,
      ['width'] = 0
    }
  }
}

function sbsettings.read_configured_devices()
  return settings.configured_devices
end

function sbsettings.read_sign_address_list()
  local list = {}
  for index,item in pairs(settings.configured_devices['*']) do
    if item.type == 1 then
      table.insert(list, index)
    end
  end
  table.sort(list)
  return list
end

function sbsettings.read_device_list()
  local list = {}
  for index,item in pairs(settings.configured_devices['*']) do
    table.insert(list, index)
  end
  table.sort(list)
  return list
end

local device_types = {
  "DESTINATION_SIGN",
  "ADVERT_SIGN",
  "HANVOX",
  "ONBOARD_COMPUTER",
  "ROUTE_NUMBER_SIGN",
  "DASH_SIGN",
  "NOT_USED",
  "BUS_STOPPING_SIGN",
  "NEXT_STOP_SIGN"
}

function sbsettings.get_sign_default_device_type(port_id)
  local item = settings.configured_devices['*'][tonumber(port_id)]
  local type = (item and item.type) and item.type or 0
  return device_types[type + 1]
end
function sbsettings.set_sign_default_device_type(value, port_id)
  for i,device_type in ipairs(device_types) do
    if device_type == value then
      settings.configured_devices['*'][tonumber(port_id)].type = i - 1
      break
    end
  end
end

function sbsettings.get_sign_default_device_type_list()
  return {
    "NOT_USED",
    "DESTINATION_SIGN",
    "ADVERT_SIGN",
    "HANVOX",
    "ONBOARD_COMPUTER",
    "ROUTE_NUMBER_SIGN",
    "DASH_SIGN",
    "BUS_STOPPING_SIGN",
    "NEXT_STOP_SIGN"
  }
end

function sbsettings.get_sign_default_database_index(port_id)
  local item = settings.configured_devices['*'][tonumber(port_id)]
  return item and item.sign_page_lookup or ""
end
function sbsettings.set_sign_default_database_index(value, port_id)
  settings.configured_devices['*'][tonumber(port_id)].sign_page_lookup = value
end

function sbsettings.get_sign_default_configured_height(port_id)
  local item = settings.configured_devices['*'][tonumber(port_id)]
  return item and item.height or 0
end
function sbsettings.set_sign_default_configured_height(value, port_id)
  settings.configured_devices['*'][tonumber(port_id)].height = value
end

function sbsettings.get_sign_default_configured_width(port_id)
  local item = settings.configured_devices['*'][tonumber(port_id)]
  return item and item.width or 0
end
function sbsettings.set_sign_default_configured_width(value, port_id)
  settings.configured_devices['*'][tonumber(port_id)].width = value
end

function sbsettings.get_sign_default_config_string(port_id)
  local item = settings.configured_devices['*'][tonumber(port_id)]
  return item and item.config_string or ""
end
function sbsettings.set_sign_default_config_string(value, port_id)
  settings.configured_devices['*'][tonumber(port_id)].config_string = value
end

settings.use_home_code_browse_controls = 1
function sbsettings.get_use_home_code_browse_controls()
  return settings.use_home_code_browse_controls
end
function sbsettings.set_use_home_code_browse_controls(value)
  settings.use_home_code_browse_controls = value
end

settings.show_toggle_theme_icon = 0
function sbsettings.get_show_toggle_theme_icon()
  return settings.show_toggle_theme_icon
end
function sbsettings.set_show_toggle_theme_icon(value)
  settings.show_toggle_theme_icon = value
end

settings.use_code_entry_keypad = 0
function sbsettings.get_use_code_entry_keypad()
  return settings.use_code_entry_keypad
end
function sbsettings.set_use_code_entry_keypad(value)
  settings.use_code_entry_keypad = value
end

settings.wake_on_remote_action = 1
function sbsettings.get_wake_on_remote_action()
  return settings.wake_on_remote_action
end
function sbsettings.set_wake_on_remote_action(value)
  settings.wake_on_remote_action = value
end

settings.icons_menu_favourites_list = {
}
function sbsettings.get_icons_menu_favourites_list()
  local list = {}
  for index,_ in pairs(settings.icons_menu_favourites_list) do
    table.insert(list, index)
  end
  return list
end
function sbsettings.get_icons_menu_favourite(index)
  return settings.icons_menu_favourites_list[index]
end

settings.home_screen_icons_list = {
  ['1'] = 'destination',
  ['2'] = 'route_number',
  ['3'] = 'round_trip',
  ['4'] = 'information'
}
function sbsettings.get_home_screen_icons_list()
  local list = {}
  for index,_ in pairs(settings.home_screen_icons_list) do
    table.insert(list, index)
  end
  return list
end
function sbsettings.get_home_screen_icon(index)
  return settings.home_screen_icons_list[index]
end

settings.htc_hot_key_select_list = {
  "Hot key action 1",
  "Hot key action 2",
  "Hot key action 3",
}
function sbsettings.get_htc_hot_key_select_list()
  local list = {}
  for key,_ in ipairs(settings.htc_hot_key_select_list) do
    table.insert(list, tostring(key))
  end
  return list
end
function sbsettings.get_htc_hot_key_select_list_item(index)
  return settings.htc_hot_key_select_list[tonumber(index)]
end

settings.destination_clear_control_mode = 1
function sbsettings.get_destination_clear_control_mode()
  return settings.destination_clear_control_mode
end
function sbsettings.set_destination_clear_control_mode(value)
  settings.destination_clear_control_mode = value
end

math.randomseed(os.time())
local rx,tx=0,0
function sbsettings.get_netinfo_data_traffic()
  local value = math.floor(math.random(1, 4))
  if value == 1 then
    rx = rx + 1
    tx = tx + 1
  elseif value == 2 then
    rx = rx + 1
  elseif value == 3 then
    tx = tx + 1
  end
  return tx, rx
end

function sbsettings.get_netinfo_port_list()
  return fileinfo:scandir("/sys/class/net")
end

function sbsettings.get_wifi_status(port_id)
  local status = {}
  status.up = fileinfo:execute("cat /sys/class/net/"..port_id.."/operstate")[1] == "up" and 1 or 0
  status.running = fileinfo:execute("cat /sys/class/net/"..port_id.."/carrier 2>/dev/null")[1] == "1" and 1 or 0
  status.signal_dbm = tonumber(fileinfo:execute("cat /proc/net/wireless | grep "..port_id.." | awk '{print $4}'")[1])
  status.ssid = fileinfo:execute("iwgetid -r")[1]
  status.rx_packets = tonumber(fileinfo:execute("cat /sys/class/net/"..port_id.."/statistics/rx_packets")[1])
  status.tx_packets = tonumber(fileinfo:execute("cat /sys/class/net/"..port_id.."/statistics/tx_packets")[1])
  return status
end

settings.wifi_access_points = {
  ["SSID1"] = {
    psk="1234",
    scan_ssid=0,
    priority=0,

  },
}

function sbsettings.read_wifi_access_point_nodes()
  local nodes = {}
  for key,_ in pairs(settings.wifi_access_points) do
    table.insert(nodes, key)
  end
  return nodes
end
function sbsettings.del_wifi_access_point_node(value)
  settings.wifi_access_points[value] = nil
end

function sbsettings.get_wifi_psk(port_id)
  if settings.wifi_access_points[port_id] == nil then return "" end
  local value = settings.wifi_access_points[port_id].psk or ""
  return #value == 0 and "" or "********"
end
function sbsettings.set_wifi_psk(value, port_id)
  if settings.wifi_access_points[port_id] == nil then settings.wifi_access_points[port_id] = {} end
  settings.wifi_access_points[port_id].psk = value
end

function sbsettings.get_wifi_scan_ssid(port_id)
  if settings.wifi_access_points[port_id] == nil then return 0 end
  return settings.wifi_access_points[port_id].scan_ssid or 0
end
function sbsettings.set_wifi_scan_ssid(value, port_id)
  if settings.wifi_access_points[port_id] == nil then settings.wifi_access_points[port_id] = {} end
  settings.wifi_access_points[port_id].scan_ssid = value
end

function sbsettings.get_wifi_priority(port_id)
  if settings.wifi_access_points[port_id] == nil then return 0 end
  return settings.wifi_access_points[port_id].priority or 0
end
function sbsettings.set_wifi_priority(value, port_id)
  if settings.wifi_access_points[port_id] == nil then settings.wifi_access_points[port_id] = {} end
  settings.wifi_access_points[port_id].priority = value
end


settings.network_interfaces = {
  ["eth0"] = {
    auto_up = 0,
    enabled = 0,
    mode = 0,
    ip_address = "",
    netmask = "",
    gateway = "",
    dhcp_lease_start = "",
    dhcp_lease_end = "",
    dns_servers = {}
  },
  ["eth1"] = {
    auto_up = 0,
    enabled = 0,
    mode = 0,
    ip_address = "",
    netmask = "",
    gateway = "",
    dhcp_lease_start = "",
    dhcp_lease_end = "",
    dns_servers = {}
  },
  ["wlo0"] = {
    auto_up = 0,
    enabled = 0,
    wifi_access_points = {},
    mode = 0,
    ip_address = "",
    netmask = "",
    gateway = "",
    dns_servers = {}
  },
}

function sbsettings.get_ethernet_port_auto_up(port_id)
  return settings.network_interfaces[port_id].auto_up
end
function sbsettings.set_ethernet_port_auto_up(value, port_id)
  settings.network_interfaces[port_id].auto_up = value
end

function sbsettings.get_ethernet_port_enabled(port_id)
  return settings.network_interfaces[port_id].enabled
end
function sbsettings.set_ethernet_port_enabled(value, port_id)
  settings.network_interfaces[port_id].enabled = value
end
function sbsettings.get_ethernet_port_mode(port_id)
  return settings.network_interfaces[port_id].mode
end
function sbsettings.set_ethernet_port_mode(value, port_id)
  settings.network_interfaces[port_id].mode = value
end

function sbsettings.get_ethernet_port_address(port_id)
  return settings.network_interfaces[port_id].ip_address
end
function sbsettings.set_ethernet_port_address(value, port_id)
  settings.network_interfaces[port_id].ip_address = value
end

function sbsettings.get_ethernet_port_netmask(port_id)
  return settings.network_interfaces[port_id].netmask
end
function sbsettings.set_ethernet_port_netmask(value, port_id)
  settings.network_interfaces[port_id].netmask = value
end

function sbsettings.get_ethernet_port_gateway(port_id)
  return settings.network_interfaces[port_id].gateway
end
function sbsettings.set_ethernet_port_gateway(value, port_id)
  settings.network_interfaces[port_id].gateway = value
end

function sbsettings.get_ethernet_port_dhcp_lease_start(port_id)
  return settings.network_interfaces[port_id].dhcp_lease_start
end
function sbsettings.set_ethernet_port_dhcp_lease_start(value, port_id)
  settings.network_interfaces[port_id].dhcp_lease_start = value
end

function sbsettings.get_ethernet_port_dhcp_lease_end(port_id)
  return settings.network_interfaces[port_id].dhcp_lease_end
end
function sbsettings.set_ethernet_port_dhcp_lease_end(value, port_id)
  settings.network_interfaces[port_id].dhcp_lease_end = value
end

function sbsettings.get_ethernet_port_dns_servers(port_id)
  local dns_servers = ""
  local sep = ""
  for _,dns in ipairs(settings.network_interfaces[port_id].dns_servers) do
    dns_servers = dns_servers..sep..dns
    sep = ","
  end
  return dns_servers
end
function sbsettings.get_ethernet_port_dns_servers_table(port_id)
  return settings.network_interfaces[port_id].dns_servers
end
function sbsettings.add_ethernet_port_dns_server(value, port_id)
  table.insert(settings.network_interfaces[port_id].dns_servers, value)
end
function sbsettings.del_ethernet_port_dns_server(value, port_id)
  for index,dns in ipairs(settings.network_interfaces[port_id].dns_servers) do
    if dns == value then
      table.remove(settings.network_interfaces[port_id].dns_servers, index)
      break
    end
  end
end

settings.wdmc_vehicle_id = ""
function sbsettings.get_wdmc_vehicle_id()
  return settings.wdmc_vehicle_id
end
function sbsettings.set_wdmc_vehicle_id(value)
  settings.wdmc_vehicle_id = value
end

settings.wdmc_server_address = ""
function sbsettings.get_wdmc_server_address()
  return settings.wdmc_server_address
end
function sbsettings.set_wdmc_server_address(value)
  settings.wdmc_server_address = value
end

settings.wdmc_username = ""
function sbsettings.get_wdmc_username()
  return settings.wdmc_username
end
function sbsettings.set_wdmc_username(value)
  settings.wdmc_username = value
end

settings.wdmc_password = ""
function sbsettings.get_wdmc_password()
  return settings.wdmc_password == "" and "" or "********"
end
function sbsettings.set_wdmc_password(value)
  settings.wdmc_password = value
end

settings.wdmc_download_interval = 30
function sbsettings.get_wdmc_download_interval()
  return settings.wdmc_download_interval
end
function sbsettings.set_wdmc_download_interval(value)
  settings.wdmc_download_interval = value
end

settings.download_scheduler_ip_address_mandatory = 0
function sbsettings.get_download_scheduler_ip_address_mandatory()
  return settings.download_scheduler_ip_address_mandatory
end
function sbsettings.set_download_scheduler_ip_address_mandatory(value)
  settings.download_scheduler_ip_address_mandatory = value
end

settings.wdmc_controlled_destination_ids = {
}
function sbsettings.get_wdmc_controlled_destination_ids()
  local value = ""
  local sep = ""
  for _,id in ipairs(settings.wdmc_controlled_destination_ids) do
    value = value..sep..id
    sep = ","
  end
  return value
end
function sbsettings.add_wdmc_controlled_destination_id(value)
  table.insert(settings.wdmc_controlled_destination_ids, value)
end
function sbsettings.del_wdmc_controlled_destination_id(value)
  for index,id in ipairs(settings.wdmc_controlled_destination_ids) do
    if id == value then
      table.remove(settings.wdmc_controlled_destination_ids, index)
      break
    end
  end
end


settings.download_scheduler_time_of_day_enabled = 0
function sbsettings.get_download_scheduler_time_of_day_enabled()
  return settings.download_scheduler_time_of_day_enabled
end
function sbsettings.set_download_scheduler_time_of_day_enabled(value)
  settings.download_scheduler_time_of_day_enabled = value
end

settings.wdmc_time_of_day_start = 0
function sbsettings.get_wdmc_time_of_day_start()
  return settings.wdmc_time_of_day_start
end
function sbsettings.set_wdmc_time_of_day_start(value)
  settings.wdmc_time_of_day_start = value
end

settings.wdmc_time_of_day_end = 0
function sbsettings.get_wdmc_time_of_day_end()
  return settings.wdmc_time_of_day_end
end
function sbsettings.set_wdmc_time_of_day_end(value)
  settings.wdmc_time_of_day_end = value
end

settings.destination_freeze_enabled = 0
function sbsettings.get_destination_freeze_enabled()
  return settings.destination_freeze_enabled
end
function sbsettings.set_destination_freeze_enabled(value)
  settings.destination_freeze_enabled = value
end

settings.allow_driver_reboot = 0
function sbsettings.get_allow_driver_reboot()
  return settings.allow_driver_reboot
end
function sbsettings.set_allow_driver_reboot(value)
  settings.allow_driver_reboot = value
end

settings.can_protocols = {
  "CAN-DIGITAL-INPUT", "STATE-OF-CHARGE",
}
function sbsettings.read_available_can_protocols()
  return settings.can_protocols
end

settings.can_port_protocol = {
  {
    ["1"] = "STATE-OF-CHARGE",
  }
}

function sbsettings.get_can_linked_protocol(port_id)
  local value = ""
  local sep = ""
  for _,connection in ipairs(settings.can_port_protocol) do
    if connection.port_id == port_id then
      value = value..sep..connection.protocol_handler
      sep = ","
    end
  end
  return value
end
function sbsettings.set_can_port_protocol(state, protocol, port_id)
  if state == 1 then
    table.insert(settings.can_port_protocol, { port_id = port_id, protocol_handler = protocol })
  else
    for index,connection in ipairs(settings.can_port_protocol) do
      if connection.port_id == port_id and connection.protocol_handler == protocol then
        table.remove(settings.can_port_protocol, index)
        break
      end
    end
  end
end

settings.can_ports = {
  ["1"] = {
    kbit_rate = 250,
    control_mode_loopback = 0,
    control_mode_listen_only = 1,
  },
}

function sbsettings.get_can_kbit_rate(port_id)
  return settings.can_ports[port_id].kbit_rate
end
function sbsettings.set_can_kbit_rate(value, port_id)
  settings.can_ports[port_id].kbit_rate = value
end

function sbsettings.get_can_control_mode_loopback(port_id)
  return settings.can_ports[port_id].control_mode_loopback
end
function sbsettings.set_can_control_mode_loopback(value, port_id)
  settings.can_ports[port_id].control_mode_loopback = value
end

function sbsettings.get_can_control_mode_listen_only(port_id)
  return settings.can_ports[port_id].control_mode_listen_only
end
function sbsettings.set_can_control_mode_listen_only(value, port_id)
  settings.can_ports[port_id].control_mode_listen_only = value
end

settings.port_connections = {}

function sbsettings.read_g4_network_connections(port_id)
  local connections = {}
  for port_id,_ in pairs(settings.port_connections) do
    table.insert(connections, port_id)
  end
  return connections
end

function sbsettings.get_network_port_connection_linked_protocol(port_id)
  return settings.port_connections[port_id] or "-"
end

function sbsettings.set_network_port_connection_linked_protocol(value, port_id)
  local data = port_id:split("://")
  if #data ~=2 then return end
  if data[1] ~= "udp" and data[1] ~= "tcp" then return end
  data = data[2]:split(":")
  if tonumber(data[2]) == nil then return end
  if value == "-" then
    settings.port_connections[port_id] = nil
  else
    settings.port_connections[port_id] = value
  end
end

function sbsettings.set_serial_port_protocol(state, protocol, port_id)
  if state == 1 then
    table.insert(settings.port_connections, { port_id = port_id, protocol_handler = protocol })
  else
    for index,connection in ipairs(settings.port_connections) do
      if connection.port_id == port_id and connection.protocol_handler == protocol then
        table.remove(settings.port_connections, index)
        break
      end
    end
  end
end

settings.g4_protocols = {
  "-", "AESYS-A", "ALMEX", "BUS-STOPPING-SIGN", "CGA-A", "DASH", "DUBWAYF", "DUHAMEL", "ECHO",
  "EMT-MALAGA", "GTMH-1", "HANCIS", "HANO-1", "HANO-1F", "HTC", "HTC-IO-ERIC", "HTC-PRESENTATION-LED",
  "IBIS", "IBIS-4", "IBIS-MASTER", "IVNUPLOAD", "J1708-1", "J1708-2", "J1708-2A", "J1708-3", "J1708-4",
  "J1708-5", "NYCT", "OCTA", "ROUTE", "SIGN", "SLE", "SOCRIE-A", "SOCRIE-B", "TGX150-A", "TGX150-C", "TGX150-D",
}
function sbsettings.read_available_g4_protocols()
  return settings.g4_protocols
end

settings.serial_ports = {
  ["1"] = {
    baud_rate = 4800,
    word_length = 8,
    parity = 0,
    stop_bits = 1,
    flow_control = 0,
    break_signal = 1,
    wait_read_length = 0,
    read_timeout = 0,
    mapped_id = "-",
  },
  ["2"] = {
    baud_rate = 4800,
    word_length = 8,
    parity = 0,
    stop_bits = 1,
    flow_control = 0,
    break_signal = 1,
    wait_read_length = 0,
    read_timeout = 0,
    mapped_id = "-",
  },
  ["3"] = {
    baud_rate = 4800,
    word_length = 8,
    parity = 0,
    stop_bits = 1,
    flow_control = 0,
    break_signal = 1,
    wait_read_length = 0,
    read_timeout = 0,
    mapped_id = "-",
  },
}

function sbsettings.get_serial_port_baud_rate(port_id)
  return settings.serial_ports[port_id].baud_rate
end
function sbsettings.set_serial_port_baud_rate(value, port_id)
  settings.serial_ports[port_id].baud_rate = value
end

function sbsettings.get_serial_port_word_length(port_id)
  return settings.serial_ports[port_id].word_length - 7
end
function sbsettings.set_serial_port_word_length(value, port_id)
  settings.serial_ports[port_id].word_length = value + 7
end

function sbsettings.get_serial_port_parity(port_id)
  return settings.serial_ports[port_id].parity
end
function sbsettings.set_serial_port_parity(value, port_id)
  settings.serial_ports[port_id].parity = value
end

function sbsettings.get_serial_port_stop_bits(port_id)
  return settings.serial_ports[port_id].stop_bits - 1
end
function sbsettings.set_serial_port_stop_bits(value, port_id)
  settings.serial_ports[port_id].stop_bits = value + 1
end

function sbsettings.get_serial_port_flow_control(port_id)
  return settings.serial_ports[port_id].flow_control
end
function sbsettings.set_serial_port_flow_control(value, port_id)
  settings.serial_ports[port_id].flow_control = value
end

function sbsettings.get_serial_port_break_signal(port_id)
  return settings.serial_ports[port_id].break_signal
end
function sbsettings.set_serial_port_break_signal(value, port_id)
  settings.serial_ports[port_id].break_signal = value
end

function sbsettings.get_serial_port_wait_read_length(port_id)
  return settings.serial_ports[port_id].wait_read_length
end
function sbsettings.set_serial_port_wait_read_length(value, port_id)
  settings.serial_ports[port_id].wait_read_length = value
end

function sbsettings.get_serial_port_read_timeout(port_id)
  return settings.serial_ports[port_id].read_timeout
end
function sbsettings.set_serial_port_read_timeout(value, port_id)
  settings.serial_ports[port_id].read_timeout = value
end

function sbsettings.get_serial_port_mapped_id(port_id)
  return settings.serial_ports[port_id].mapped_id or ""
end


settings.info_messages_without_destination = 0
function sbsettings.get_info_messages_without_destination()
  return settings.info_messages_without_destination
end
function sbsettings.set_info_messages_without_destination(value)
  settings.info_messages_without_destination = value
end

settings.clear_on_destination_change = 0
function sbsettings.get_clear_on_destination_change()
  return settings.clear_on_destination_change
end
function sbsettings.set_clear_on_destination_change(value)
  settings.clear_on_destination_change = value
end

settings.driver_htc_terminal_permission = 1
function sbsettings.get_driver_htc_terminal_permission()
  return settings.driver_htc_terminal_permission
end
function sbsettings.set_driver_htc_terminal_permission(value)
  settings.driver_htc_terminal_permission = value
end


settings.departure_time_category = 1
function sbsettings.get_departure_time_category()
  return settings.departure_time_category
end

settings.state_of_charge_driver_specifier = "#"
function sbsettings.get_state_of_charge_driver_specifier()
  return settings.state_of_charge_driver_specifier
end

settings.state_of_charge_driver_message = {
  ["01"] = "FULLY CHARGED",
  ["02"] = "CHARGING - #%"
}
function sbsettings.get_state_of_charge_driver_message(info_code)
  return settings.state_of_charge_driver_message[info_code]
end

settings.can_protocol_active = {
  ["STATE-OF-CHARGE"] = 1
}
function sbsettings.check_can_protocol_active(protocol)
  return settings.can_protocol_active[protocol] == 1
end

settings.emergency_driver_message = "EMERGENCY"
function sbsettings.get_emergency_driver_message()
  return settings.emergency_driver_message
end

settings.bus_stopping_driver_message = "BUS STOPPING"
function sbsettings.get_bus_stopping_driver_message()
  return settings.bus_stopping_driver_message
end

settings.digital_input = {
  ["0"] = {
    state = 1,
    use = 0,
    destination_code = "",
    route_code = "",
    message_code = "",
    activation_type = 0,
    active_sense = 0,
    reset_type = 0,
  },
  ["1"] = {
    state = 0,
    use = 0,
    destination_code = "",
    route_code = "",
    message_code = "",
    activation_type = 0,
    active_sense = 0,
    reset_type = 0,
  },
  ["2"] = {
    state = 0,
    use = 0,
    destination_code = "",
    route_code = "",
    message_code = "",
    activation_type = 0,
    active_sense = 0,
    reset_type = 0,
  },
}

function sbsettings.read_digital_input_list()
  local nodes = {}
  for key,_ in pairs(settings.digital_input) do
    table.insert(nodes, key)
  end
  table.sort(nodes)
  return nodes
end

function sbsettings.get_digital_input_use(index)
  return settings.digital_input[index].use
end
function sbsettings.set_digital_input_use(value, index)
  settings.digital_input[index].use = value
end

function sbsettings.get_digital_input_state(index)
  return settings.digital_input[index].state
end

function sbsettings.get_digital_input_destination_id(index)
  local value = settings.digital_input[index].route_code
  if value ~= "" then
    value = value.." "
  end
  return value..settings.digital_input[index].destination_code
end
function sbsettings.set_digital_input_destination_id(route_code, dest_code, index)
  settings.digital_input[index].route_code = route_code
  settings.digital_input[index].destination_code = dest_code
end

settings.custom_announcement_info_code = ""
function sbsettings.get_custom_announcement_info_code()
  return settings.custom_announcement_info_code
end
function sbsettings.set_custom_announcement_info_code(value)
  settings.custom_announcement_info_code = value
end

function sbsettings.get_digital_input_message_code(index)
  return settings.digital_input[index].message_code
end
function sbsettings.set_digital_input_message_code(value, index)
  settings.digital_input[index].message_code = value
end

function sbsettings.get_digital_input_message_code_priority(index)
  local code = sbsettings.get_digital_input_message_code(index)
  if not settings.message_codes[code] then return 0 end
  return settings.message_codes[code].priority or 0
end
function sbsettings.set_digital_input_message_code_priority(value, index)
  local code = sbsettings.get_digital_input_message_code(index)
  if not settings.message_codes[code] then return end
  settings.message_codes[code].priority = value
end

function sbsettings.get_digital_input_activation_type(index)
  return settings.digital_input[index].activation_type
end
function sbsettings.set_digital_input_activation_type(value, index)
  settings.digital_input[index].activation_type = value
end

function sbsettings.get_digital_input_active_sense(index)
  return settings.digital_input[index].active_sense
end
function sbsettings.set_digital_input_active_sense(value, index)
  settings.digital_input[index].active_sense = value
end

function sbsettings.get_digital_input_reset_type(index)
  return settings.digital_input[index].reset_type
end
function sbsettings.set_digital_input_reset_type(value, index)
  settings.digital_input[index].reset_type = value
end

function sbsettings.get_digital_input_message_priority(index)
  return settings.digital_input[index].priority or 5
end
function sbsettings.set_digital_input_message_priority(value, index)
  settings.digital_input[index].priority = value
end

settings.priority_message_code = {}
function sbsettings.set_priority_message_code(value, index)
  settings.priority_message_code[index] = value
end
function sbsettings.get_priority_message_code(index)
  return settings.priority_message_code[index] or ""
end

settings.priority_destination_code = {}
function sbsettings.set_priority_destination_code(value, index)
  settings.priority_destination_code[index] = value
end
function sbsettings.get_priority_destination_code(index)
  return settings.priority_destination_code[index] or ""
end

settings.priority_route_code = {}
function sbsettings.set_priority_route_code(value, index)
  settings.priority_route_code[index] = value
end
function sbsettings.get_priority_route_code(index)
  return settings.priority_route_code[index] or ""
end

settings.sign_blanking_level = 100
function sbsettings.get_sign_blanking_level()
  return settings.sign_blanking_level
end
function sbsettings.set_sign_blanking_level(value)
  settings.sign_blanking_level = value
end

settings.sign_blanking_timeout = 0
function sbsettings.get_sign_blanking_timeout()
  return settings.sign_blanking_timeout
end
function sbsettings.set_sign_blanking_timeout(value)
  settings.sign_blanking_timeout = value
end

settings.power_save_low_power_delay = 1800
function sbsettings.get_power_save_low_power_delay()
  return settings.power_save_low_power_delay
end
function sbsettings.set_power_save_low_power_delay(value)
  settings.power_save_low_power_delay = value
end

settings.digital_output = {
  ["0"] = {
    state = 1,
    use = 0,
    active_sense = 0,
  },
  ["1"] = {
    state = 0,
    use = 0,
    active_sense = 0,
  },
}

function sbsettings.read_digital_output_list()
  local nodes = {}
  for key,_ in pairs(settings.digital_output) do
    table.insert(nodes, key)
  end
  table.sort(nodes)
  return nodes
end

function sbsettings.get_digital_output_use(index)
  return settings.digital_output[index].use
end
function sbsettings.set_digital_output_use(value, index)
  settings.digital_output[index].use = value
end

function sbsettings.get_digital_output_state(index)
  return settings.digital_output[index].state
end

function sbsettings.get_digital_output_active_sense(index)
  return settings.digital_output[index].active_sense
end
function sbsettings.set_digital_output_active_sense(value, index)
  settings.digital_output[index].active_sense = value
end

settings.driver_message_text_encoding = "UTF-8"
function sbsettings.get_driver_message_text_encoding()
  return settings.driver_message_text_encoding
end
function sbsettings.set_driver_message_text_encoding(value)
  settings.driver_message_text_encoding = value
end

settings.service_id_required = 1
function sbsettings.get_service_id_required()
  return settings.service_id_required
end
function sbsettings.set_service_id_required(value)
  settings.service_id_required = value
end

settings.service_id_valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
function sbsettings.get_service_id_valid_chars()
  return settings.service_id_valid_chars
end

settings.usb_enable_duration = 120
function sbsettings.get_usb_enable_duration()
  return settings.usb_enable_duration
end
function sbsettings.set_usb_enable_duration(value)
  settings.usb_enable_duration = value
end

settings.usb_power_state = 1
function sbsettings.get_usb_power_state()
  return settings.usb_power_state
end
function sbsettings.set_usb_power_state(value)
  settings.usb_power_state = value
end

settings.htc_hot_key_icon = 0
function sbsettings.get_htc_hot_key_icon()
  return settings.htc_hot_key_icon
end
function sbsettings.set_htc_hot_key_icon(value)
  settings.htc_hot_key_icon = value
end


settings.htc_hot_key_select_enable = 0
function sbsettings.get_htc_hot_key_select_enable()
  return settings.htc_hot_key_select_enable
end
function sbsettings.set_htc_hot_key_select_enable(value)
  settings.htc_hot_key_select_enable = value
end

settings.allow_multiple_messages = 0
function sbsettings.get_allow_multiple_messages()
  return settings.allow_multiple_messages
end
function sbsettings.set_allow_multiple_messages(value)
  settings.allow_multiple_messages = value
end

settings.allow_info_messages_display = 1
function sbsettings.get_allow_info_messages_display()
  return settings.allow_info_messages_display
end
function sbsettings.set_allow_info_messages_display(value)
  settings.allow_info_messages_display = value
end

settings.default_route_code = ""
function sbsettings.get_default_route_code()
  return settings.default_route_code
end
function sbsettings.set_default_route_code(value)
  settings.default_route_code = value
end

settings.default_destination_code = ""
function sbsettings.get_default_destination_code()
  return settings.default_destination_code
end
function sbsettings.set_default_destination_code(value)
  settings.default_destination_code = value
end

settings.destination_completion_timeout = 5000
function sbsettings.get_destination_completion_timeout()
  return settings.destination_completion_timeout
end
function sbsettings.set_destination_completion_timeout(value)
  settings.destination_completion_timeout = value
end

settings.destination_operation_on_timeout = 0
function sbsettings.get_destination_operation_on_timeout()
  return settings.destination_operation_on_timeout
end
function sbsettings.set_destination_operation_on_timeout(value)
  settings.destination_operation_on_timeout = value
end

settings.destination_selection_mode = 0
function sbsettings.get_destination_selection_mode()
  return settings.destination_selection_mode
end
function sbsettings.set_destination_selection_mode(value)
  settings.destination_selection_mode = value
end

settings.information_selection_mode = 0
function sbsettings.get_information_selection_mode()
  return settings.information_selection_mode
end
function sbsettings.set_information_selection_mode(value)
  settings.information_selection_mode = value
end

settings.allow_control_of_destination = 1
function sbsettings.get_allow_control_of_destination()
  return settings.allow_control_of_destination
end
function sbsettings.set_allow_control_of_destination(value)
  settings.allow_control_of_destination = value
end

settings.page_startup_action = 0
function sbsettings.get_page_startup_action()
  return settings.page_startup_action
end
function sbsettings.set_page_startup_action(value)
  settings.page_startup_action = value
end

settings.destination_startup_action = 0
function sbsettings.get_destination_startup_action()
  return settings.destination_startup_action
end
function sbsettings.set_destination_startup_action(value)
  settings.destination_startup_action = value
end

settings.info_startup_action = 0
function sbsettings.get_info_startup_action()
  return settings.info_startup_action
end
function sbsettings.set_info_startup_action(value)
  settings.info_startup_action = value
end

settings.route_number_startup_action = 0
function sbsettings.get_route_number_startup_action()
  return settings.route_number_startup_action
end
function sbsettings.set_route_number_startup_action(value)
  settings.route_number_startup_action = value
end

settings.service_id_startup_action = 0
function sbsettings.get_service_id_startup_action()
  return settings.service_id_startup_action
end
function sbsettings.set_service_id_startup_action(value)
  settings.service_id_startup_action = value
end

settings.change_priority = 0
settings.inhibit_remote_setting_idle = 0

local DAL_PMS_CHANGE_PRIORITY_LOCAL_CONTROL_ONLY = 0
local DAL_PMS_CHANGE_PRIORITY_LOCAL_PRIORITY = 1
local DAL_PMS_CHANGE_PRIORITY_REMOTE_PRIORITY = 2
local DAL_PMS_CHANGE_PRIORITY_REMOTE_PRIORITY_WITH_TEMPORARY_LOCAL_OVERRIDE = 3

function sbsettings.set_page_change_priority(value)
  if value == 1 then -- local_priority
    settings.change_priority = DAL_PMS_CHANGE_PRIORITY_LOCAL_PRIORITY
    settings.inhibit_remote_setting_idle = 0
  elseif value == 2 then -- remote_priority
    settings.change_priority = DAL_PMS_CHANGE_PRIORITY_REMOTE_PRIORITY
    settings.inhibit_remote_setting_idle = 0
  elseif value == 3 then -- remote_priority_with_temporary_local_override
    settings.change_priority = DAL_PMS_CHANGE_PRIORITY_REMOTE_PRIORITY_WITH_TEMPORARY_LOCAL_OVERRIDE
    settings.inhibit_remote_setting_idle = 0
  elseif value == 4 then -- remote_priority_inhibit_idle
    settings.change_priority = DAL_PMS_CHANGE_PRIORITY_REMOTE_PRIORITY
    settings.inhibit_remote_setting_idle = 1
  elseif value == 5 then -- remote_priority_inhibit_idle_with_temp
    settings.change_priority = DAL_PMS_CHANGE_PRIORITY_REMOTE_PRIORITY_WITH_TEMPORARY_LOCAL_OVERRIDE
    settings.inhibit_remote_setting_idle = 1
  else -- local_control_only
      settings.change_priority = DAL_PMS_CHANGE_PRIORITY_LOCAL_CONTROL_ONLY
      settings.inhibit_remote_setting_idle = 0
  end
end
function sbsettings.get_page_change_priority()
  if settings.change_priority == DAL_PMS_CHANGE_PRIORITY_LOCAL_PRIORITY then
    return 1 -- local_priority
  elseif settings.change_priority == DAL_PMS_CHANGE_PRIORITY_REMOTE_PRIORITY then
    if settings.inhibit_remote_setting_idle == 0 then
      return 2 -- remote_priority
    end
    return 4 -- remote_priority_inhibit_idle;
  elseif settings.change_priority == DAL_PMS_CHANGE_PRIORITY_REMOTE_PRIORITY_WITH_TEMPORARY_LOCAL_OVERRIDE then
    if settings.inhibit_remote_setting_idle == 0 then
        return 3 -- remote_priority_with_temporary_local_override;
    end
    return 5 -- remote_priority_inhibit_idle_with_temp;
  end
  return 0 -- local_control_only
end

settings.round_trip_enable = 0
function sbsettings.get_round_trip_enable()
  return settings.round_trip_enable
end
function sbsettings.set_round_trip_enable(value)
  settings.round_trip_enable = value
end

settings.route_number_len = 2
function sbsettings.get_route_number_len()
 return settings.route_number_len
end

settings.use_route_number = 1
function sbsettings.get_use_route_number()
 return settings.use_route_number
end

settings.ibis_manual_mode = 0
function sbsettings.get_ibis_manual_mode()
  return settings.ibis_manual_mode
end

settings.use_ibis_manual_line = 0
function sbsettings.get_use_ibis_manual_line()
  return settings.use_ibis_manual_line
end

settings.use_ibis_manual_line_szn = 0
function sbsettings.get_use_ibis_manual_line_szn()
  return settings.use_ibis_manual_line_szn
end

settings.use_destination_code_as_sign_data = 0
function sbsettings.get_use_destination_code_as_sign_data()
  return settings.use_destination_code_as_sign_data
end

settings.sysinfo_product_release = "1.0.0"
function sbsettings.get_sysinfo_product_release()
  return settings.sysinfo_product_release
end

settings.sysinfo_product_id = "EG4"
function sbsettings.get_sysinfo_product_id()
  return settings.sysinfo_product_id
end

settings.sysinfo_serial_number = "0123456789"
function sbsettings.get_sysinfo_serial_number()
  return settings.sysinfo_serial_number
end

local function read_cpu_status()
  local file = io.open("/proc/stat", "r")
  local data = {}
  for line in file:lines() do
    if not line:starts_with("cpu") then break end
    local cpu, user, nice, system, idle = line:match("(%w+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)")
    table.insert(data, { user, nice, system, idle })
  end
  file:close()
  return data
end

local cpu_status_last_result = {};
local cpu_usage_calcs = { 0.0 }
function sbsettings.get_sysinfo_cpu_usage()
  local cpu_status = read_cpu_status()
  if #cpu_status > 0 and #cpu_status == #cpu_status_last_result then
    for index = 1,#cpu_status do
      local a = cpu_status_last_result[index];
      local b = cpu_status[index] or 0.0;
      if #b == 4 and #a == 4 then
        cpu_usage_calcs[index] = ((b[1]+b[2]+b[3]) - (a[1]+a[2]+a[3])) / ((b[1]+b[2]+b[3]+b[4]) - (a[1]+a[2]+a[3]+a[4]));
      end
    end
  end

  -- Copy cpu_status table of tables to cpu_status_last_result
  cpu_status_last_result = table.copy(cpu_status);

  return cpu_usage_calcs;
end

function sbsettings.get_sysinfo_memory()
  local data = fileinfo:execute("free | grep Mem")[1]
  if not data then return -1 end
  local total, used = data:match("Mem:%s+(%d+)%s+(%d+)")
  return total, total - used
end

local disk = '/dev/root'
local ALTERNATE_DISK = '/dev/sda2'
function sbsettings.get_sysinfo_storage()
  local data = fileinfo:execute("df | grep "..disk)
  if #data == 0 and disk ~= ALTERNATE_DISK then
    disk = ALTERNATE_DISK
    data = fileinfo:execute("df | grep "..disk)
  end
  if #data == 0 then return 0, 0 end
  local total, used = data[1]:match(disk.."%s+(%d+)%s+(%d+)")
  return total or 0, used or 0
end

function sbsettings.get_sysinfo_uptime()
  local data = fileinfo:execute("cat /proc/uptime")[1]
  if not data then return -1 end
  local uptime = data:match("(%d+)")
  return math.floor(uptime)
end

settings.get_database_load_status = 1
function sbsettings.get_database_load_status()
  return settings.get_database_load_status
end


settings.use_htc_time_source = 0
function sbsettings.get_use_htc_time_source()
  return settings.use_htc_time_source
end
function sbsettings.set_use_htc_time_source(value)
  settings.use_htc_time_source = value
end

settings.use_gps_time = 0
function sbsettings.get_use_gps_time()
  return settings.use_gps_time
end
function sbsettings.set_use_gps_time(value)
  settings.use_gps_time = value
end

settings.automatic_date_and_time = 0
function sbsettings.get_automatic_date_and_time()
  return settings.automatic_date_and_time
end
function sbsettings.set_automatic_date_and_time(value)
  settings.automatic_date_and_time = value
end

settings.show_custom_splash = 0
function sbsettings.get_show_custom_splash()
  return settings.show_custom_splash
end
function sbsettings.set_show_custom_splash(value)
  settings.show_custom_splash = value
end

settings.backlight_mode = 1
function sbsettings.get_backlight_mode()
  return settings.backlight_mode
end
function sbsettings.set_backlight_mode(value)
  settings.backlight_mode = value
end

settings.display_off_timeout = 30
function sbsettings.get_display_off_timeout()
  return settings.display_off_timeout
end
function sbsettings.set_display_off_timeout(value)
  settings.display_off_timeout = value
end

settings.inactivity_brightness_adjust = 0
function sbsettings.get_inactivity_brightness_adjust()
  return settings.inactivity_brightness_adjust
end
function sbsettings.set_inactivity_brightness_adjust(value)
  settings.inactivity_brightness_adjust = value
end

settings.display_brightness = 100
function sbsettings.get_display_brightness()
  return settings.display_brightness
end
function sbsettings.set_display_brightness(value)
  settings.display_brightness = value
end

settings.manual_brightness = 100
function sbsettings.get_manual_brightness()
  return settings.manual_brightness
end
function sbsettings.set_manual_brightness(value)
  settings.manual_brightness = value
end

settings.display_inactivity_timeout = 15
function sbsettings.get_display_inactivity_timeout()
  return settings.display_inactivity_timeout
end
function sbsettings.set_display_inactivity_timeout(value)
  settings.display_inactivity_timeout = value
end

settings.sign_brightness = 128
function sbsettings.get_sign_brightness()
  return settings.sign_brightness
end
function sbsettings.set_sign_brightness(value)
  settings.sign_brightness = value
end

settings.sign_blanking_level = 128
function sbsettings.get_sign_blanking_level()
  return settings.sign_blanking_level
end
function sbsettings.set_sign_blanking_level(value)
  settings.sign_blanking_level = value
end

settings.sign_blanking_timeout = 150
function sbsettings.get_sign_blanking_timeout()
  return settings.sign_blanking_timeout
end
function sbsettings.set_sign_blanking_timeout(value)
  settings.sign_blanking_timeout = value
end

settings.continue_updates_on_inactive = 0
function sbsettings.get_continue_updates_on_inactive()
  return settings.continue_updates_on_inactive
end
function sbsettings.set_continue_updates_on_inactive(value)
  settings.continue_updates_on_inactive = value
end

settings.enable_virtual_htc_terminal = 0
function sbsettings.get_enable_virtual_htc_terminal()
  return settings.enable_virtual_htc_terminal
end
function sbsettings.set_enable_virtual_htc_terminal(value)
  settings.enable_virtual_htc_terminal = value
end

settings.virtual_htc_terminal_port = 1234
function sbsettings.get_virtual_htc_terminal_port()
  return settings.virtual_htc_terminal_port
end
function sbsettings.set_virtual_htc_terminal_port(value)
  settings.virtual_htc_terminal_port = value
end

settings.sign_firmware_update_baud_rate = 115200
function sbsettings.get_sign_firmware_update_baud_rate()
  return settings.sign_firmware_update_baud_rate
end
function sbsettings.set_sign_firmware_update_baud_rate(value)
  settings.sign_firmware_update_baud_rate = value
end

settings.auto_theme = 1
function sbsettings.get_auto_theme()
  return settings.auto_theme
end
function sbsettings.set_auto_theme(value)
  settings.auto_theme = value
end

settings.brightness_limits_low = 5
settings.brightness_limits_high = 80
function sbsettings.get_brightness_limits()
  return settings.brightness_limits_low, settings.brightness_limits_high
end
function sbsettings.set_brightness_limits(low, high)
  settings.brightness_limits_low = low
  settings.brightness_limits_high = high
end

settings.auto_theme_threshold_low = 0
settings.auto_theme_threshold_high = 0
function sbsettings.get_auto_theme_thresholds()
  return settings.auto_theme_threshold_low, settings.auto_theme_threshold_high
end
function sbsettings.set_auto_theme_thresholds(low, high)
  settings.auto_theme_threshold_low = low
  settings.auto_theme_threshold_high = high
end

-- DA: Dansk
-- DE: Deutsche
-- EN: English
-- ES: Español
-- FI: Suomi
-- FR: Français
-- ID: Bahasa Indonesia
-- IT: Italiano
-- NL: Nederlands
-- NO: Norsk
-- PT: Português
-- RO: Română
-- SV: Svenska
-- TR: Türk
-- ZH: 繁體中文
-- CN: 简体中文
settings.language = "EN"
function sbsettings.get_language()
  return settings.language
end

function sbsettings.set_language(value)
  settings.language = value
end

settings.settings_profile = 0
function sbsettings.get_settings_profile()
  return settings.settings_profile
end
function sbsettings.set_settings_profile(value)
  settings.settings_profile = value
end

settings.theme = nil
function sbsettings.get_theme()
  if not settings.theme then
    local value = fileinfo:execute('readlink -f '..gre.APP_ROOT..'/images')
    if #value == 0 then
      settings.theme = "dark"
    else
      settings.theme = value[1]:ends_with("light") and 0 or 1
    end
  end
  return settings.theme
end

function sbsettings.set_theme(value)
  settings.theme = value
end

settings.time_format = 1
function sbsettings.get_time_format()
  return settings.time_format
end
function sbsettings.set_time_format(value)
  settings.time_format = value
end

settings.date_format = 1
function sbsettings.get_date_format()
  return settings.date_format
end
function sbsettings.set_date_format(value)
  settings.date_format = value
end

settings.departure_time_specifier = '#'
function sbsettings.get_departure_time_specifier()
  return settings.departure_time_specifier
end

settings.round_trip = ""
function sbsettings.set_destination_round_trip(value)
  settings.round_trip = value
end
function sbsettings.get_destination_round_trip()
  return settings.round_trip
end

settings.destination_local_last = ""
function sbsettings.set_destination_local_last(value)
  settings.destination_local_last = value
end
function sbsettings.get_destination_local_last()
  return settings.destination_local_last
end

settings.network_time_servers = {
  ['pool.ntp.org'] = {},
  ['time.nist.gov'] = {},
  ['time-a.nist.gov'] = {}
}
function sbsettings.read_network_time_user_servers()
  local nodes = {}
  for key,_ in pairs(settings.network_time_servers) do
    table.insert(nodes, key)
  end
  return nodes
end

function sbsettings.add_network_time_user_server(value)
  settings.network_time_servers[value] = {}
end

function sbsettings.remove_network_time_user_server(value)
  settings.network_time_servers[value] = nil
end

function sbsettings.get_network_time_user_server_enabled(port_id)
  return settings.network_time_servers[port_id].enabled
end
function sbsettings.set_network_time_user_server_enabled(value, port_id)
  settings.network_time_servers[port_id].enabled = value
end
function sbsettings.get_network_time_user_server_trusted(port_id)
  return settings.network_time_servers[port_id].trusted
end
function sbsettings.set_network_time_user_server_trusted(value, port_id)
  settings.network_time_servers[port_id].trusted = value
end

function sbsettings.get_network_time_user_server_prefer(port_id)
  return settings.network_time_servers[port_id].prefer
end
function sbsettings.set_network_time_user_server_prefer(value, port_id)
  settings.network_time_servers[port_id].prefer = value
end

settings.home_screen_icon_order = ""
function sbsettings.get_home_screen_icon_order()
  return settings.home_screen_icon_order
end
function sbsettings.set_home_screen_icon_order(value)
  settings.home_screen_icon_order = value
end

settings.maintenance_timeout = 0
function sbsettings.get_maintenance_timeout()
  return settings.maintenance_timeout
end
function sbsettings.set_maintenance_timeout(value)
  settings.maintenance_timeout = value
end

settings.driver_passcode_required = 0
function sbsettings.get_driver_passcode_required()
  return settings.driver_passcode_required
end
function sbsettings.set_driver_passcode_required(value)
  settings.driver_passcode_required = value
end

settings.passcodes = {
  driver = {
    "1234",
    "1111"
  },
  maintenance = {
    "0101"
  },
  usb = {
    "9999"
  }
}

function sbsettings.get_password_counts()
  return { driver = #settings.passcodes.driver, maintenance = #settings.passcodes.maintenance }
end

settings.usb_update_requires_password = 1
function sbsettings.get_usb_update_requires_password()
  return settings.usb_update_requires_password
end

function sbsettings.set_usb_update_requires_password(value)
  settings.usb_update_requires_password = value
end

function sbsettings.validate_bus_driver_password(value)
  for k, v in ipairs(settings.passcodes.driver) do
    if v == value then return true end
  end
end

function sbsettings.validate_maintenance_operator_password(value)
  for k, v in ipairs(settings.passcodes.maintenance) do
    if v == value then return true end
  end
end

function sbsettings.validate_usb_update_password(value)
  for k, v in ipairs(settings.passcodes.usb) do
    if v == value then return true end
  end
end

settings.driver_id_entry = 0
function sbsettings.get_driver_id_entry()
  return settings.driver_id_entry
end
function sbsettings.set_driver_id_entry(value)
  settings.driver_id_entry = value
end

function sbsettings.validate_driver_id(value)
  return value == "1111"
end

settings.destination_requires_passcode = 0
function sbsettings.get_destination_requires_passcode()
  return settings.destination_requires_passcode
end
function sbsettings.set_destination_requires_passcode(value)
  settings.destination_requires_passcode = value
end

settings.information_requires_passcode = 0
function sbsettings.get_information_requires_passcode()
  return settings.information_requires_passcode
end
function sbsettings.set_information_requires_passcode(value)
  settings.information_requires_passcode = value
end

settings.sign_test_maintenance_lock = 0
function sbsettings.get_sign_test_maintenance_lock()
  return settings.sign_test_maintenance_lock
end
function sbsettings.set_sign_test_maintenance_lock(value)
  settings.sign_test_maintenance_lock = value
end

settings.route_code_len = 3
function sbsettings.get_route_code_len()
  return settings.route_code_len
end

settings.destination_code_len = 5
function sbsettings.get_destination_code_len()
  return settings.destination_code_len
end

settings.destination_list =
{
  '000 00001 Not in Service',
  '000 00002 Staff Bus',
  '000 00003 NX West Midlands',
  '000 00004 National Express',
  '000 00005 Metro Replacem\'t',
  '000 00007 Excursion',
  '000 00010 Walsall Garage',
  '000 00011 Contract',
  '000 00012 Park & Ride',
  '000 00013 Private',
  '000 00014 College Bus',
  '000 00015 Funeral',
  '000 00016 Bus Driver of the Year',
  '000 00017 School Service',
  '000 00018 Rail Replacem\'t',
  '000 00019 CHANGE OVER BUS',
  '000 00022 St Thomas More 4 Anniversary Mass',
  '000 00034 Football Special',
  '000 00035 blank des',
  '000 40001 4 Blackheath',
  '000 40002 4 Walsall',
  '000 40003 4E Delves',
  '000 40004 4E Stone Cross',
  '000 40005 4E West Bromwich',
  '000 40006 4E Oldbury',
  '000 60001 6 Sutton Coldfie 6 Sutton Coldfie',
  '000 60002 6 Walsall 6 via Aldridge',
  '000 60003 6E Mere Green',
  '000 60004 6E Aldridge',
  '000 70001 7 Walton Road',
  '000 70002 7 Walsall 7 via Aldridge',
  '000 70003 7 Castlefort',
  '000 80001 8 Pelsall',
  '000 80002 8 Walsall',
  '000 V0001 V Fest logo',
  '000 V0002 Weston Park Weston Park',
  '000 V0003 Wolverhampton',
  '000 V0004 Stafford',
  '000 V0005 Telford',
  '000 V0006 White GATE Camp Site',
  '000 V0007 White PUMP Camp Site',
  '001 00001 10 Burntwood 10 via Brownhill',
  '001 00002 10 Ogley Hay',
  '001 00003 10 Walsall 10 via Brownhill',
  '001 00004 10 Streets Corne',
  '002 90001 29 Bloxwich 29 via Blakenall',
  '002 90002 29 Walsall (blak 29 via Blakenall',
  '002 90003 29 Blakenall',
  '002 90005 29 Walsall',
  '003 40001 34 Stowlawn',
  '003 40002 34 Walsall 34 via Bilston',
  '003 40003 34 Bilston',
  '003 40004 34E Darlaston',
  '003 80001 38 Wednesbury',
  '003 80002 38 Walsall',
  '003 80003 38E Darlaston',
  '003 90001 39 Wolverhampton',
  '003 90002 39 Walsall',
  '003 90003 39E Bilston',
  '003 90004 39E Darlaston',
  '004 00001 40 Wolverhampton',
  '004 00002 40 Walsall 40 Walsall',
  '004 00003 40E Lodge Farm',
  '004 00004 40E Willenhall',
  '004 10001 41 Willenhall',
  '004 10002 41 Walsall',
  '004 10003 41E New Invent',
  '004 50002 45 Walsall',
  '004 50003 45 Yew Tree',
  '004 50004 45 Stone Cross',
  '004 50005 45 Charlemont Fa',
  '004 H0001 4H Halesowen',
  '004 H0002 4H Walsall',
  '004 H0003 4H Stone Cross',
  '004 H0004 4H Delves',
  '004 H0005 4H West Bromwich',
  '004 H0006 4H Oldbury',
  '004 H0007 4H Blackheath',
  '004 M0001 4M Merry Hill',
  '004 M0002 4M Walsall',
  '004 M0003 4M West Bromwich',
  '004 M0004 4M Delves',
  '004 M0005 4M Stone Cross',
  '004 M0006 4M Oldbury',
  '004 M0007 4M Blackheath',
  '004 M0008 4M Cradley Heath',
  '004 M0009 4M R regis hos',
  '004 M0010 4M Old Hill',
  '004 M0011 4M Whiteheath',
  '005 10001 51 Birmingham 51 via Scott Arm',
  '005 10002 51 Walsall',
  '006 90001 69 Wolverhampton',
  '006 90002 69 Walsall 69 via New Inven',
  '006 90003 69E New Inventio',
  '006 90004 69E Wednesfield',
  '006 90005 69E Beechdale',
  '007 00001 70 Bloxwich',
  '007 00002 70 Walsall 70 via Beechdale',
  '007 00003 70 Beechdale',
  '007 00004 70E Dudley Field',
  '007 00006 70 Blox Snow',
  '007 70001 77 Sutton via Streetly',
  '007 70002 77 Walsall via Streetly',
  '007 70003 77 Beggars Bush',
  '007 70004 77 George Freder',
  '007 A0001 7A castlefort 7A then to',
  '007 A0002 7A Walsall 7A via Aldridge',
  '007 A0003 7A Aldridge',
  '007 A0007 7A  walton 7A then to',
  '007 S0001 7S Walsall',
  '007 S0002 7S Aldridge Scho',
  '008 90001 89 Wolverhampton',
  '008 90002 89 Walsall',
  '008 90003 89 Pelsall',
  '008 90004 89 Bloxwich',
  '008 90005 89 Wednesfield',
  '008 90006 89E New Cross H',
  '010 A0001 10A Brownhills W',
  '010 A0002 10A Walsall',
  '010 A0003 10A Brown smith',
  '015 90001 159 School Servi',
  '015 90002 159 St Francis S',
  '015 90003 159 Birchills G',
  '030 10001 301 Mossley',
  '030 10002 301 Walsall 301 via Bloxwich',
  '030 10003 301E Bloxwich',
  '030 10004 301E Bus Garage',
  '030 20001 302 Lower Farm E',
  '030 20002 302 Walsall',
  '030 20003 302E Bloxwich',
  '030 20004 302E Bus Garage',
  '031 10001 311 Dudley via W\'bry/Dudley',
  '031 10002 311 Walsall',
  '031 10003 311E Wednesbury 311 Via Pleck',
  '031 10004 311E Princes end',
  '031 30001 313 Dudley via Wednesbury',
  '031 30002 313 Walsall 313 Via Wednesbu',
  '031 30003 313E Wednesbury 313E via Pleck',
  '031 30004 313E Princes End',
  '033 50001 335 Alumwell Cir via Manor Hosp.',
  '033 50002 335 Alumwell 335 Alumwell',
  '033 50003 335 Walsall 335 walsall',
  '033 60001 336 Alumwell Cir via Manor Hosp.',
  '033 60002 336 Alumwell',
  '033 60003 336 Walsall',
  '051 E0001 51E Great Barr Scott Arms',
  '051 E0002 51E Perry Barr',
  '051 E0003 51E 6 Ways Aston',
  '051 E0004 51E Boundary ho',
  '052 90001 529 Wolverhampto',
  '052 90002 529 Walsall',
  '052 90003 529 Willenhall',
  '052 90004 529 Motorway Isl 529 Junction 10',
  '052 90005 529 Portabello I',
  '070 10001 701 ST Thomas Mo',
  '070 10002 701 Harden Road',
  '070 30001 703 ST Thomas Mo',
  '070 30002 703 New Inventio',
  '070 50001 705 School Bus',
  '070 A0001 70A Beechdale',
  '070 A0002 70A Walsall',
  '072 10001 721 School Bus',
  '072 70001 727 School Bus',
  '074 50001 745 School Bus',
  '075 80001 758 School Bus',
  '078 80001 788 Barr Beacon 788 Barr Beacon',
  '078 80002 788 Walsall',
  '079 10001 791 Walsall',
  '088 10001 881 Barr Beacon',
  '088 10002 881 Palfrey 881 Milton Stree',
  '088 60001 886 King Edwards 886 School Servi',
  '088 60002 886 Walsall',
  '093 40001 934 Birmingham',
  '093 40002 934 Walsall',
  '093 40003 934E Cat & Fiddl',
  '093 40004 934E Kingstandin',
  '093 40006 934E Pheasey Chu',
  '093 40007 934 Barr Beacon 934 Sutton barr',
  '093 40008 934E Lanc Circus',
  '093 40009 934E Perry Barr',
  '093 50001 935 Birmingham',
  '093 50002 935 Walsall',
  '093 50003 935E George Fred',
  '093 50004 935E Kingstandin',
  '093 50005 935E Streetly',
  '093 50006 935E Perry Barr',
  '093 60001 936 Birmingham',
  '093 60002 936 Brownhills',
  '093 60003 936E Perry Barr',
  '093 60004 936E Kingstandin',
  '093 60005 936E Pheasey Chu',
  '093 60006 936E Cat & Fiddl',
  '093 60007 936E Barr Beacon',
  '093 60008 936E Aldridge',
  '093 60009 936E Streets Cnr',
  '099 00001 990 Shuttle Serv',
  '099 70001 997 Birmingham',
  '099 70002 997 Walsall',
  '099 70003 997E Deers Leap',
  '099 70004 997E Pheas Churc',
  '099 70005 997E Aldridge',
  '099 70006 997E Rushall',
  '099 70007 997E Asda Quesle',
  '099 70008 997E Perry Barr',
  '099 70009 997E Streetly',
  '0X5 10001 X51 Birmingham',
  '0X5 10002 X51 Walsall',
  '0X5 10003 X51 Cannock',
  '0X5 10004 X51 Bloxwich',
  '0X5 10005 X51 Scott Arms X51 Only',
  '0X5 60001 X56 Birmingham',
  '0X5 60002 X56 Boney Hay',
  '0X5 60003 X56 Pheasey',
  '0X5 60004 X56 Foley Arms',
  '159 C0001 159C School Serv',
  '159 C0002 159C St Francis',
  '159 E0001 159E Streets Crn',
  '301 E0001 301E Garage',
  '301 E0002 301E Bloxwich 301E Only',
  '301 E0003 301E Walsall',
  '935 A0001 935A Brownhills',
  '935 A0002 935A Birmingham',
  '935 A0003 935A Streetly',
  '935 A0004 935A Kingstandin',
  '935 A0005 935A Walsall Woo',
  '935 A0006 935A Perry Barr',
  '935 A0007 935A George Fred',
  '935 A0008 935A Hundred Acr',
  '935 E0001 935E Streetly',
  '935 E0002 935E George Fred',
  '935 E0003 935E Kingstandin',
  '997 E0001 997E Deers Leap',
  '997 E0002 997E Pheasey Chu'
}

function sbsettings.get_destination_driver_message(route_code, destination_code)
  if route_code ~= "" then
    destination_code = route_code.." "..destination_code
  end
  destination_code = destination_code.." "

  for _,text in ipairs(settings.destination_list) do
    if text:sub(1,#destination_code) == destination_code then
      return text:sub(#destination_code+1)
    end
  end
  return nil
end

function sbsettings.get_route_code_keys()
  if settings.route_code_len == 0 then return "" end

  local keys = {}
  for _,text in ipairs(settings.destination_list) do
    local code = text:match("%w+")
    code = code:gsub("%d", "") -- Delete all numbers from code string
    table.insert(keys, code)
  end

  -- Remove duplicates from keys table and construct valid keys string
  table.sort(keys)
  local valid_keys = ""
  local last = nil
  for _,c in ipairs(keys) do
    if c ~= last then
      last = c
      valid_keys = valid_keys..c
    end
  end

  return valid_keys
end

function sbsettings.get_destination_code_keys()
  local fmt = (settings.route_code_len == 0) and "%w+" or "%w+%s(%w+)"

  local keys = {}
  for _,text in ipairs(settings.destination_list) do
    local code = text:match(fmt)
    code = code:gsub("%d", "") -- Delete all numbers from code string
    for i=1,#code do
      table.insert(keys, code:sub(i,i))
    end
  end

  -- Remove duplicates from keys table and construct valid keys string
  table.sort(keys)
  local valid_keys = ""
  local last = nil
  for _,c in ipairs(keys) do
    if c ~= last then
      last = c
      valid_keys = valid_keys..c
    end
  end

  return valid_keys
end

function sbsettings.get_destinations_present()
  return #settings.destination_list == 0 and 0 or 1
end

local filtered_destinations = {}
local function filter_destination(destination_id, selected_route_code, text_filter)
  if selected_route_code ~= "" and not destination_id:starts_with(selected_route_code) then return false end
  if text_filter == "" then return true end

  -- Check destination_id to see if there the letters and numbers in text_filter appear and are in the same order
  local text_filter_index = 1
  local id_index = 1
  while text_filter_index <= #text_filter and id_index <= #destination_id do
    if text_filter:sub(text_filter_index, text_filter_index) == destination_id:sub(id_index, id_index) then
      text_filter_index = text_filter_index + 1
    end
    id_index = id_index + 1
  end

  -- Check if all the letters and numbers in text_filter appear in v
  return text_filter_index > #text_filter
end

local function fixup_route_code(value)
  value = value or ""
  if value ~= "" then
    local len = sbsettings.get_route_code_len()
    if #value < len then
      value = string.rep("0", len - #value) .. value
    end
    value = value.." "
  end
  return value
end

local function fixup_destination_code(value)
  value = value or ""
  if value ~= "" then
    local len = sbsettings.get_destination_code_len()
    if #value < len then
      value = string.rep("0", len - #value) .. value
    end
    value = value.." "
  end
  return value
end

function sbsettings.update_destination_list(selected_route_code, text_filter, destination_id)
  selected_route_code = fixup_route_code(selected_route_code)

  text_filter = text_filter and text_filter:upper() or ""
  destination_id = destination_id or ""
  if destination_id ~= "" then
    destination_id = destination_id.." "
  end

  filtered_destinations = {}
  local index = 0
  for k, v in ipairs(settings.destination_list) do
    if filter_destination(v:upper(), selected_route_code, text_filter) then
      table.insert(filtered_destinations, v)
      if destination_id ~= "" and v:starts_with(destination_id) then
        index = k
      end
    end
  end
  return #filtered_destinations, index
end

function sbsettings.get_destination_list_item(index)
  return filtered_destinations[index]
end


function sbsettings.validate_route_code(value)
  if sbsettings.get_use_destination_code_as_sign_data() == 1 then return true end

  value = fixup_route_code(value)
  -- Check if destination_list item starts with value
  for k, v in ipairs(settings.destination_list) do
    if v:starts_with(value) then
      return true
    end
  end
end

function sbsettings.check_destination_id(selected_route_code, value)
  value = fixup_route_code(selected_route_code)..fixup_destination_code(value)
  -- Check if destination_list item starts with value
  for k, v in ipairs(settings.destination_list) do
    if v:starts_with(value) then
      return true
    end
  end
end

function sbsettings.validate_destination_id(selected_route_code, value)
  if sbsettings.get_use_destination_code_as_sign_data() == 1 then return true end
  return sbsettings.check_destination_id(selected_route_code, value)
end

settings.message_codes = {
  ["1"] = {
    driver_message = "Not in Service",
    priority = 0,
  },
  ["2"] = {
    driver_message = "Staff Bus",
    priority = 0,
  },
  ["3"] = {
    driver_message = "NX West Midlands",
    priority = 0,
  },
  ["4"] = {
    driver_message = "National Express",
    priority = 0,
  },
  ["5"] = {
    driver_message = "Metro Replacem't",
    priority = 0,
  },
  ["7"] = {
    driver_message = "Excursion",
    priority = 0,
  },
  ["10"] = {
    driver_message = "Walsall Garage",
    priority = 0,
  },
  ["11"] = {
    driver_message = "Contract",
    priority = 0,
  },
  ["12"] = {
    driver_message = "Park & Ride",
    priority = 0,
  },
  ["13"] = {
    driver_message = "Private",
    priority = 0,
  },
  ["14"] = {
    driver_message = "College Bus",
    priority = 0,
  },
  ["15"] = {
    driver_message = "Funeral",
    priority = 0,
  },
  ["16"] = {
    driver_message = "Bus Driver of the Year",
    priority = 0,
  },
  ["17"] = {
    driver_message = "School Service",
    priority = 0,
  },
}

function sbsettings.get_info_message_code_params()
  local params = {
    max_len = 1,
  }

  local keys = {}
  for code,_ in pairs(settings.message_codes) do
    if #code > params.max_len then params.max_len = #code end
    code = code:gsub("%d", "") -- Delete all numbers from code string
    table.insert(keys, code)
  end

  -- Remove duplicates from keys table and construct valid keys string
  table.sort(keys)
  params.valid_keys = ""
  local last = nil
  for _,c in ipairs(keys) do
    if c ~= last then
      last = c
      params.valid_keys = params.valid_keys..c
    end
  end

  return params
end

function sbsettings.get_info_messages_present()
  return next(settings.message_codes) and 1 or 0
end

local function search_case_insensitive(text, filter)
  if not filter or filter == "" then return true end
  local fi = 1
  for i=1,#text do
    if text:sub(i, i):upper() == filter:sub(fi, fi):upper() then
      if fi == #filter then return true end
      fi = fi + 1
    end
  end
end

local filtered_info_messages = {}
function sbsettings.update_info_message_list(filter)
  filtered_info_messages = {}
  local max_info_code_len = 1;
  for code,data in pairs(settings.message_codes) do
    if #code > max_info_code_len then max_info_code_len = #code end
    local text = code.." "..data.driver_message
    if search_case_insensitive(text, filter) then
      table.insert(filtered_info_messages, text)
    end
  end

  -- Sort the message list by the normalised info code
  table.sort(filtered_info_messages, function(a, b)
    a = a:match("%w+")
    b = b:match("%w+")
    a = string.rep("0", max_info_code_len - #a)..a
    b = string.rep("0", max_info_code_len - #b)..b
    return a < b
  end)
end

function sbsettings.get_info_message_list_size()
  return #filtered_info_messages
end

function sbsettings.get_info_message_list_item(index)
  return filtered_info_messages[index]
end

function sbsettings.validate_info_code(value)
  for code,_ in pairs(settings.message_codes) do
    if code == value then return true end
  end
  return false
end

settings.allow_multiple_messages = 0
function sbsettings.get_allow_multiple_messages()
  return settings.allow_multiple_messages
end

-- Write all the settings map to a file on the disk
function sbsettings.save()
  local file = io.open(gre.APP_ROOT.."/settings.txt", "w")
  file:write(map(settings))
  file:close()
end

-- Read all the settings map from a file on the disk
local function load_settings(filename)
  local file = io.open(filename, "r")
  if not file then return end
  local data = file:read("*all")
  local data = loadstring("return "..data)()
  settings = table.add_defaults(data, settings)
  file:close()
end

-- Load settings when module is loaded.
load_settings(os.getenv("SETTINGS_FILE") or (gre.APP_ROOT.."/settings.txt"))

-- Export the settings accessor function table
return sbsettings