---
--- @package   eg4-crank-ui
--- @file      digital-io-settings.lua
--- @brief     Functionality to support the editing of digital I/O settings. 
---
--- @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.                                              
---
---

---
--- @brief  Make the port id option text
--- @param control - control to get format from.
--- @param port_id - text for port ID
--- @param device_type - specfies input or output 
---
local function MakePortID(control, port_id, device_type)
  local fmt = gre.get_value(control.."."..device_type)
  return string.format(fmt, port_id)
end

---
--- @brief  Make the port id option text
--- @param layer - Layer to populate
--- @param port_id - text for port ID
--- @param device_type - specfies input or output 
---
local function AddDigitalIOSetting(layer, port_id, device_type)
  local group = layer..".group"
  local control = group..".items"

  -- Determine if item is already present. 
  local index = 0
  local item
  local type
  repeat
    index = index + 1
    item = gre.get_value(string.format(control..".port_id.%d.1", index))
    type = gre.get_value(string.format(control..".device_type.%d.1", index))
  until item == nil or item == "" or (port_id == item and device_type == type) 
  
  if item ~= port_id or device_type ~= type then
    -- Add the new item to the menu list.
    gre.set_value(string.format(control..".text.%d.1", index), MakePortID(control, port_id, device_type))
    gre.set_value(string.format(control..".port_id.%d.1", index), port_id)
    gre.set_value(string.format(control..".device_type.%d.1", index), device_type)    
    gre.set_table_attrs(control, {height = index * 81, rows = index})
  end
  
  local value = GetControlValueText(nil, device_type.."_use", port_id)
  gre.set_value(string.format(control..".value.%d.1", index), value)

  local fn = sbsettings["get_"..device_type.."_state"]
  if fn then
    local state = ""
    if fn(port_id) ~= 0 then
      state = gre.get_value(control..".active_text")
    else
      state = gre.get_value(control..".inactive_text")
    end
    gre.set_value(string.format(control..".state.%d.1", index), state)
  end
end

---
--- @brief  Initialises the digital I/O list layer with available I/O.
--- @param  gre#context mapargs
---
function CBInitDigitalIOSettingList(mapargs)
  local layer = "digital_io_list_layer"
  local list = sbsettings.read_digital_input_list()
  for _,port_id in ipairs(list) do
    AddDigitalIOSetting(layer, port_id, "digital_input")
  end

  local list = sbsettings.read_digital_output_list()
  for _,port_id in ipairs(list) do
    AddDigitalIOSetting(layer, port_id, "digital_output")
  end
  SBCInitialiseScrollbar(mapargs)
end

---
--- @brief  Initialises the digital I/O setting screen
--- @param  gre#context mapargs
---
function CBInitDigitalIOSettingScreen(mapargs)
  local device_type = gre.get_value(string.format(mapargs.context_control..".device_type.%d.1", mapargs.context_row))

  local port_id_heading = gre.get_value(string.format(mapargs.context_control..".text.%d.1", mapargs.context_row))
  gre.set_value("mo_"..device_type..".banner_heading", port_id_heading)

  local port_id = gre.get_value(string.format(mapargs.context_control..".port_id.%d.1", mapargs.context_row))
  gre.set_value(device_type.."_layer.menu_items.port_id", port_id)

  mapargs.screen = "mo_"..device_type
  mapargs.function_on_valid = "CBMaintenanceOperatorLoggedIn"

  CBMaintenanceSettingsStart(mapargs)
end

---
--- @brief  Processes a current digital input setting event. Creates a clone 
--          and populates the group.
--- @param gre#context mapargs 
---
function CBUpdateDigitalInputSettingMenu(mapargs)
  local layer = "digital_input_layer"
  local group = layer..".menu_items"
  port_id = gre.get_value(group..".port_id")
  local data = {}

  local use = sbsettings.get_digital_input_use(port_id)

  -- Check if use is set to message code
  if use == 12 then
    data[group .. ".custom_announcement_info_code.grd_hidden"] = 0
  else
    data[group .. ".custom_announcement_info_code.grd_hidden"] = 1
  end

  -- Check if use is set to priority message code
  if use == 4 or use == 8 then
    data[group .. ".digital_input_priority_message.grd_hidden"] = 0
  else
    data[group .. ".digital_input_priority_message.grd_hidden"] = 1
  end

  -- Check if use is set to battery guard
  if use == 6 then
    data[group .. ".sign_blanking_level.grd_hidden"] = 0
    data[group .. ".sign_blanking_timeout.grd_hidden"] = 0
    data[group .. ".sign_blanking_timeout_spin.grd_hidden"] = 0
  else
    data[group .. ".sign_blanking_level.grd_hidden"] = 1
    data[group .. ".sign_blanking_timeout.grd_hidden"] = 1
    data[group .. ".sign_blanking_timeout_spin.grd_hidden"] = 1
  end

  -- Check if use is set to power save
  if use == 7 then
    data[group .. ".power_save_low_power_delay.grd_hidden"] = 0
    data[group .. ".power_save_low_power_delay_spin.grd_hidden"] = 0
  else
    data[group .. ".power_save_low_power_delay.grd_hidden"] = 1
    data[group .. ".power_save_low_power_delay_spin.grd_hidden"] = 1
  end

  -- Check if use is set to audio announcement
  data[group .. ".audio_settings.grd_hidden"] = 
    ((use == 9 or use == 10 or use == 11) and 
        gre.get_value("system_configuration_layer.menu_items.audio_settings.grd_hidden") or 1) 

  -- Check if use is set to emergency
  data[group .. ".digital_input_reset_type.grd_hidden"] = (use == 3) and 0 or 1

  gre.set_data(data)

  CBMenuUpdate(mapargs)
end

---
--- @brief  Get digital input priority message setting
--- @param priority - priority
--- @return encoded string
function getPriorityMessageSetting(priority)
  local value = sbsettings.get_priority_message_code(tostring(priority))
  if value == "" then
    local route_code = sbsettings.get_priority_route_code(priority)
    local destination_code = sbsettings.get_priority_destination_code(priority)
    if route_code ~= "" or destination_code ~= "" then
      value = string.format("%s %s", route_code, destination_code):trim()
      value = gre.get_value("mo_digital_input.dest").." "..value
    end
  else
    value = gre.get_value("mo_digital_input.info").." "..value
  end
  return value
end

---
--- @brief  Get digital input priority message setting
--- @return encoded string
function get_digital_input_priority_message()
  local port_id = gre.get_value("digital_input_layer.menu_items.port_id")
  local priority = sbsettings.get_digital_input_message_priority(port_id)
  return string.format("%d [%s]", priority, getPriorityMessageSetting(priority))
end

---
--- @brief  Get digital input use setting
--- @param port_id - port id
--- @return use setting
function get_digital_input_use(port_id)
  local value = sbsettings.get_digital_input_use(port_id)
  if value == 8 then
    value = 4
  elseif value > 8 then
    value = value - 1
  end
  return value
end

---
--- @brief  Set digital input use setting
--- @param value - use setting
--- @param port_id - port id
function set_digital_input_use(value, port_id)
  if value >= 8 then
    value = value + 1
  end
  sbsettings.set_digital_input_use(value, port_id)
end

---
--- @brief  Populate settings for digital input message priorities
function CBUpdateMessagePrioritySettings(mapargs)
  local data = {}
  for i=0,5 do
    data["digital_input_message_priority_layer.group.items.setting."..(i + 1)..".1"] = getPriorityMessageSetting(i)
  end
  gre.set_data(data)
end

function CBStartPriorityItemEdit(mapargs)
  gre.set_value("mo_digital_input_priority_type.port_id", mapargs.context_row - 1)
  ScreenTransition("mo_digital_input_priority_type")
end

---
--- @brief  Processes a current digital output setting event. Creates a clone 
--          and populates the group.
--- @param gre#context mapargs 
---
function CBReadDigitalOutputSetting(mapargs)
  if mapargs.context_event_data.text == nil then
    return
  end
  local settings = assert(loadstring(mapargs.context_event_data.text))()

  if settings ~= nil then
    local layer = mapargs.context_layer
    local group = layer..".menu_items"
    if settings.port_id == gre.get_value(group..".port_id") then
      local data = {}
      if settings.use ~= nil then
        data[group .. ".use.index"] = settings.use + 1
    else
        data[group .. ".use.index"] = -1
        data[group .. ".use.value"] = ""
      end
      gre.set_data(data)
    end
  end
end

---
--- @brief  Fetch table for digital output use settings
--- @param  port_id - port id 
--- @return table of use settings
function get_digital_output_use(port_id)
  local value = sbsettings.get_digital_output_use(port_id)
  local uses = {}
  for i=0,31 do
    table.insert(uses, value % 2)
    value = math.floor(value / 2)
  end
  return uses
end

---
--- @brief  Set digital output use setting
--- @param  value - use setting
--- @param  index - index of use setting
--- @param  port_id - port id 
function set_digital_output_use(value, index, port_id)
  local setting = get_digital_output_use(port_id)
  setting[index] = value
  local value = 0
  for i=1,#setting do
    value = value + setting[i] * 2^(i - 1)
  end
  sbsettings.set_digital_output_use(value, port_id)
end

---
--- @brief  Submits the new digital input custom annoucement message code  
--- @param gre#context mapargs 
---
function CBSubmitCustomAnnouncementInfoCode(mapargs)
  local value = mapargs.context_row and SelectedInfoMessage(mapargs) or mapargs.value
  sbsettings.set_custom_announcement_info_code(value)
  TransitionBackScreen(mapargs)
end

---
--- @brief  Submits the new digital input message code  
--- @param gre#context mapargs 
---
function CBSubmitDigitalInputMessageCode(mapargs)
  local value = mapargs.context_row and SelectedInfoMessage(mapargs) or mapargs.value
  local priority = tonumber(gre.get_value("mo_digital_input_priority_type.port_id"))
  sbsettings.set_priority_message_code(value, tostring(priority))
  sbsettings.set_priority_route_code("", priority)
  sbsettings.set_priority_destination_code("", priority)
  TransitionBackScreen(mapargs)
end

---
--- @brief Start digital input destination ID entry
--- @param gre#context mapargs
function CBStartDigitalInputDestinationID(mapargs)
  gre.set_value("mo_digital_input_destination_code.selected_route_code", nil)
  if get_route_code_len() > 0 then
    ScreenTransition("mo_digital_input_route_code")
  else
    ScreenTransition("mo_digital_input_destination_code")
  end
end

---
--- @brief Digital input route code entered
--- @param gre#context mapargs
function CBDigitalInputRouteCode(mapargs)
  gre.set_value("mo_digital_input_destination_code.selected_route_code", mapargs.value)
  ScreenTransition("mo_digital_input_destination_code")
end

---
--- @brief Digital input destination code entered
--- @param gre#context mapargs
function CBSetDigitalInputDestinationID(mapargs)
  local priority = tonumber(gre.get_value("mo_digital_input_priority_type.port_id"))
  local route_code = gre.get_value("mo_digital_input_destination_code.selected_route_code") or ""
  if route_code ~= "" then
    route_code = string.rep("0", get_route_code_len() - #route_code)..route_code
  end
  local destination_code = mapargs.value
  if destination_code ~= "" then
    destination_code = string.rep("0", get_destination_code_len() - #destination_code)..destination_code
  end
  sbsettings.set_priority_message_code("", tostring(priority))
  sbsettings.set_priority_route_code(route_code, priority)
  sbsettings.set_priority_destination_code(destination_code, priority)
  TransitionBackScreen(mapargs)
end

---
--- @brief Call back to clear the Digital input destination code
--- @param gre#context mapargs
function CBClearDigitalInputDestinationID(mapargs)
  local port_id = gre.get_value("digital_input_layer.menu_items.port_id")
  sbsettings.set_digital_input_destination_id("", "", port_id)
end

-- EOF --
