#!/usr/bin/lua

local unit_base = "/etc/hwinit/"
local systemd_enable_base = "/etc/systemd/system/multi-user.target.wants/"

--Function to execute terminal commands to run a separate program and returns value as string
local function exec(cmd)

  -- Starts program program in a separated process and returns a file handle 
  -- that you can use to read data from this program (if mode is "r", the default) 
  -- or to write data to this program (if mode is "w").
  local f = io.popen(cmd) 
  
  -- Reads the file, according to the given formats, which specify what to read. For each format, 
  -- the function returns a string (or a number) with the characters read, or nil if it cannot read 
  -- data with the specified format.
  -- "*n" reads a number; this is the only format that returns a number instead of a string.
  -- "*a" reads the whole file, starting at the current position. On end of file, it returns the empty string 
  -- "*|" reads the next line (skipping the end of line), returning nil on end of file. This is the default format.
  -- Number reads a string with up to this number of characters, returning nil on end of file. 
  -- If number is zero, it reads nothing and returns an empty string, or nil on end of file.
  local value = f:read("*a") or "none"
  
  -- Closes file. Note that files are automatically closed when their handles are garbage collected, but 
  -- that takes an unpredictable amount of time to happen.
  f:close()
  
  return value
end


function file_exists(name)

  -- This function opens a file, in the mode specified in the string mode. 
  -- It returns a new file handle, or, in case of errors, nil plus an error message. 
  -- The mode string can be any of the following: 
  -- "r":  read mode (the default);
  --  "w":  write mode;
  --  "a":  append mode;
  --  "r+": update mode, all previous data is preserved;
  --  "w+": update mode, all previous data is erased;
  --  "a+": append update mode, previous data is preserved, writing is only allowed at the end of file.
  -- The mode string can also have a ‘b‘ at the end, which is needed in some systems to open the file in binary mode. 
  -- This string is exactly what is used in the standard C function fopen.
   local f = io.open(name,"r")
   
   if f~=nil then 
   
   -- Equivalent to file:close(). Without a file, closes the default output file.
    io.close(f) 
    return true 
   else 
     return false 
   end
end


local function process_cfg(gnxid_output)
   local cfg = {}
 
   local lines = gnxid_output:gmatch("(GNX_%S+=%S+)\n")
   for line in lines do
        local key,value = line:match("(GNX_%S+)=(.+)")
        cfg[key] = value;
   end
   return cfg
end

local function get_units()
   local units={}
   local ls_units = exec([[ls ]]..unit_base..[[*]])
   for file in ls_units:gmatch("%S+") do
     unit = exec("basename "..file)
     unit = unit:match("(%S-)\n")
     ucfg = {}
     ucfg.name = unit
     ucfg.enabled = file_exists(systemd_enable_base..[[hwinit@]]..unit..[[.service]])
     table.insert(units,ucfg)
   end
   return units
end

local function do_hw_init(cfg,unit)
   if unit then
     print("Initialising unit "..unit)
   else
     print("We need a unit to run")
     return
   end   
   
   if not file_exists(unit_base..unit) then
      print("Could not find unit "..unit)
   end
   unit_cfg = {}
   unit_cfg.dev = ""
   unit_cfg.edit = ""
   unit_cfg.baud = ""
   unit_cfg.bits = ""
   unit_cfg.stop = ""
   unit_cfg.type = ""
   unit_cfg.gnxid = ""
   
   for key,value in pairs(cfg) do
      local dev,owner,edit,baud,bits,stop,type = value:match("(.-):(.-):(.-):(.-):(.-):(.-):(.-)")
      if dev and owner and edit and baud and bits and stop and type and owner == unit then
         unit_cfg.dev = dev
         unit_cfg.edit = edit
         unit_cfg.baud = baud
         unit_cfg.bits = bits
         unit_cfg.stop = stop
         unit_cfg.type = type
         unit_cfg.gnxid = key
      end
   end   
   local init_res = exec("/bin/bash "..unit_base..unit.." "..unit_cfg.gnxid.." "..unit_cfg.dev.." "..unit.." "..unit_cfg.edit.." "..unit_cfg.baud.." "..unit_cfg.bits.." "..unit_cfg.stop.." "..unit_cfg.type)
   print(init_res)
end

local function do_enable_unit(unit)
   print("Enabling HW Unit "..unit)
   if file_exists(unit_base..unit) then
      local init_res = exec([[/usr/bin/systemctl enable hwinit@]]..unit..[[.service]])
   else
      print("The friggin uniot does not exist")   
   end
end

local function do_disable_unit(unit)
   print("Enabling HW Unit "..unit)
   local init_res = exec([[/usr/bin/systemctl disable hwinit@]]..unit..[[.service]])
end

local function do_list_units()
   units = get_units()
   for idx,unit in ipairs(units) do
     local en = " "
     if unit.enabled == true then en = "*" end 
     print(" "..en..unit.name)     
   end
end

local function do_isenabled(unit)
   local init_res =  exec([[/usr/bin/systemctl is-enabled hwinit@]]..unit..[[.service]])
   print(init_res)
end

local function usage()
      print([[
      
  Usage: hwctl {COMMAND} [UNIT]
              Commands:
  list                  List available units
  init [UNIT]           Run a unit
  enable [UNIT]         Enable the systemd service for a unit
  disable [UNIT]        Disable the systemd service for a unit
  is-enabled [UNIT]     Check whether a profile is enabled
  ]])  
end

local function main()
   
   local cmd = nil
   local unit = nil
   
   if arg[1] and arg[2] then
      cmd = arg[1]
      unit = arg[2]
   elseif arg[1] and arg[1] == "list" then
      cmd = arg[1]
   else
      usage()
      os.exit(-1)       
   end
   
   if cmd == "init" then
      local gnxid_result = exec([[gnxid -l]])
      local cfg = process_cfg(gnxid_result)
      do_hw_init(cfg,unit)
   elseif cmd == "enable" then
        do_enable_unit(unit) 
   elseif cmd == "disable" then
        do_disable_unit(unit) 
   elseif cmd == "is-enabled" then
        do_isenabled(unit)
   elseif cmd == "list" then
        do_list_units()
   else
      print("Unknown Command "..cmd.."\n")
      usage()
      os.exit(-1)  
   end  
end



main()
