#!/usr/bin/lua

json = require("json")

function getConfig(configname)

  local uci=require("uci")
  local cur=uci.cursor()
  local config
  if configname then
    config=configname
  else
    config="beacon"
  end

  logging = cur.get(config,"logging","enabled") 

  mqtt_host = cur.get(config,"mqtt","host")
  mqtt_port = cur.get(config,"mqtt","port")
  mqtt_id = cur.get(config,"mqtt","id")
  mqtt_topic = cur.get(config,"mqtt","topic")

  mqtt_user = cur.get(config,"mqtt","user")
  mqtt_passwd = cur.get(config,"mqtt","password")

  if mqtt_host and not mqtt_id then
    socket = require("socket")
    posix = require("posix")
    hostname = socket.dns.gethostname()
    pid = posix.getpid()
    mqtt_id="beaconmon-"..hostname.."-"..pid
  end

  if mqtt_host and not mqtt_port then
    mqtt_port = 1883
  end

  if mqtt_host and not mqtt_topic then
    mqtt_topic = 'beaconmon/{type}/{details}'
  end

end

function capture(cmd, raw)
  local f = assert(io.popen(cmd, 'r'))
  local s = assert(f:read('*a'))
  f:close()
  if raw then return s end
  s = string.gsub(s, '^%s+', '')
  s = string.gsub(s, '%s+$', '')
  s = string.gsub(s, '[\n\r]+', ' ')
  return s
end

function mqtt_encode(str)
  if (str) then
    str = string.gsub (str, "\n", "")
    str = string.gsub (str, "/", "-")
  end
  return str	
end

function printLog(str)
  if logging=="yes" then
    capture("logger -t beaconmon "..str)
  else 
    print(str)  
  end 
end

function run_command(cmd)

  local file = assert(io.popen(cmd, 'r'))
  local output = file:read('*all')
  file:close()

end

function open_dump()

  run_command("/bin/kill `/usr/bin/pgrep btmon`")
  run_command("/bin/kill `/usr/bin/pgrep hcitool`")
  f = assert(io.popen ("/usr/bin/stdbuf -o0 /usr/bin/btmon"))
  run_command("/usr/bin/hciconfig hci0 down")
  run_command("/usr/bin/hciconfig hci0 up")
  f_null = assert(io.popen ("/usr/bin/hcitool lescan --duplicates --passive"))

  return f

end

function dump(o)
  return json.encode(o)
end

function trim(s)
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

function mqtt_pub(path,value)
  res=mqtt_client:publish(path,value)
  printLog("Pub "..path.." "..value.." returned "..res);
  return res
end

function process_packet(packet)

  local mac
  local uuid
  local details
  local type
  local name

  mac = packet['Address']
  uuid = packet['uuid']
  type = packet['Type']
  name = packet['Name (complete)']

  if type=='iBeacon' then
    details=uuid
  elseif name then
    type="name"
    details=name
  else
    type='unknown'
    details=mac
  end

  if not (type=="unknown") then
    mqtt_path=string.gsub(mqtt_topic,"{(.-)}",
      function (name) 
        if name=="type" then
          return mqtt_encode(type)
        elseif name=="details" then
          return mqtt_encode(details)
        else
          return '{'..name..'}'
        end      
      end)
        
    if not pcall(mqtt_pub,mqtt_path,dump(packet)) then
      printLog('Reconnecting MQTT...')
      mqtt_client:connect(mqtt_id)
    end

  end

end

function starts(String,Start)
   return string.sub(String,1,string.len(Start))==Start
end

function split(inputstr, sep)
        if sep == nil then
                sep = "%s"
        end
        local t={} ; i=1
        for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
                t[i] = str
                i = i + 1
        end
        return t
end

function read_loop()

  packet={}
  inbound=false

  while true do

    str=inp:read("*l")

    if str then 

      str=trim(str)

      if inbound then
        t = split(str,':')
        if #t>=2 then
          name=t[1]
          value=trim(table.concat(t,':',2))
          if name=="Address" then
            value=split(value)[1]
          end
          packet[name]=value
        end  
      end  

      if starts(str,'> HCI Event: LE Meta Event (0x3e)') then
        inbound=true
      elseif starts(str,'RSSI:') then
        inbound=false
        process_packet(packet)
        packet={}  
      end
      
    end  
    
  end

end

io.stdout:setvbuf('no')
io.stdin:setvbuf('no') 

getConfig(arg[1])

if mqtt_host then
  MQTT = require "mosquitto"
  mqtt_client = MQTT.new(mqtt_id)
  if mqtt_user then
    mqtt_client:login_set(mqtt_user, mqtt_passwd)
  end
  mqtt_client:connect(mqtt_host,mqtt_port)
end

inp = open_dump()

read_loop()