#!/usr/bin/lua

socket = require "socket"
json = require "json"
require "uci"
cur = uci.cursor()
require "wm_util"

config_name = arg[1]
if not config_name then
  config_name = "weathermon"
end  

weather_file = uci.get(config_name,"process","dump_file")
weather_db = uci.get(config_name,"process","logdb")

graph_duration = uci.get(config_name,"display","graph_duration")

if graph_duration and not(weather_db == "") then
  pcall(function ()
      local dbdriver = require "luasql.sqlite3"
      local env = assert(dbdriver.sqlite3())
      log_con = assert(env:connect(weather_db))
    end)  
end

function round(num, numDecimalPlaces)
  local mult = 10^(numDecimalPlaces or 0)
  return math.floor(num * mult + 0.5) / mult
end

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

function lpad(s, l, c)
  local res = string.rep(c or ' ', l - #s) .. s
  return res, res ~= s
end

function process_file()
  txt = get_file_content(weather_file)
  data = json.decode(txt)
  for k,v in pairs(data) do
    return v
  end
end

function get_display_config()
  width  = tonumber(uci.get(config_name,"display","width"))
  height = tonumber(uci.get(config_name,"display","height"))
  charwidth  = tonumber(uci.get(config_name,"display","charwidth"))
  charheight = tonumber(uci.get(config_name,"display","charheight"))
  cols   = tonumber(uci.get(config_name,"display","columns"))
  title  = uci.get(config_name,"display","title")
  col_width = math.floor(width/cols)
  
  pages = {}
  pagetitles = {}
  pagedurations = {}
  
  local page, def, line, col, pos
  
  cur.foreach(config_name,"display", function(s) 
      page = {}
      local pagetitle = s["title"]
      if not pagetitle then
        pagetitle = title
      end
      local pageduration = s["duration"]
      if not (pagetitle=="") then
        line = 2
      else
        line = 1
      end
      col = 1
      pos = 0
      for k,v in pairs(s["parameter"]) do
        def = {}
        if not(v == "") then
          v = split(v,":")
          def["sensor"] = v[1]
          def["param"] = v[2]
          def["label"] = v[3]
          def["unit"] = v[4]
          def["scale"] = tonumber(v[5])
          def["round"] = tonumber(v[6])
          def["pos"] = tonumber(v[7])
          def["line"] = line
          def["col"] = col
          page[#page+1] = def
        end
        col = col + 1
        if col > cols then
          col = 1
          line = line + 1
        end
      end
      pages[#pages+1] = page
      pagetitles[#pages] = pagetitle
      pagedurations[#pages] = pageduration
    end)

end

function connect_server()
  local ip = uci.get(config_name,"display","server")
  local port = uci.get(config_name,"display","port")
  conn = socket.connect(ip,port)
  return conn
end

function write_command(conn,command)
  conn:send(command.."\n")
  local str=conn:receive()
  return str
end

function setup_pages(conn)
  write_command(conn,"hello")
  write_command(conn,"client_set -name weathermon")
  for i,page in pairs(pages) do
    local pageid = "page"..trim(tostring(i))
    write_command(conn,"screen_add "..pageid)
    local pagetitle = pagetitles[i]
    write_command(conn,"screen_set "..pageid.." -cursor off")
    if not(pagetitle == "") then
      write_command(conn,"screen_set "..pageid.." -name "..pagetitle)
      write_command(conn,"widget_add "..pageid.." "..pageid..".title title")
      write_command(conn,"widget_set "..pageid.." "..pageid..".title \"".. pagetitle.."\"")
    end
    local pageduration = pagedurations[i]
    if pageduration and not(pageduration == "") then
      write_command(conn,"screen_set "..pageid.." -duration "..pageduration)
    end
    for j,def in pairs(page) do
      defid = def["sensor"].."."..def["param"]
      write_command(conn,"widget_add "..pageid.." "..defid.." string")
    end
  end
  if log_con then
    for i,page in pairs(pages) do
      for j,def in pairs(page) do
        pageid = def["sensor"].."."..def["param"]
        pagetitle = trim(def["label"])..", "..def["unit"]
        write_command(conn,"screen_add "..pageid)
        write_command(conn,"screen_set "..pageid.." -cursor off")
        write_command(conn,"screen_set "..pageid.." -name "..pagetitle.." -duration "..graph_duration)
        write_command(conn,"widget_add "..pageid.." "..pageid..".title title")
        write_command(conn,"widget_set "..pageid.." "..pageid..".title \"".. pagetitle.."\"")
        write_command(conn,"widget_add "..pageid.." "..pageid..".max string")
        write_command(conn,"widget_add "..pageid.." "..pageid..".min string")
        for k=1,width-def["pos"] do
          write_command(conn,"widget_add "..pageid.." "..pageid..".bar"..trim(tostring(k)).." vbar")
        end
      end
    end
  end
end

function process_vals(vals)
  for i,page in pairs(pages) do
    local pageid = "page"..trim(tostring(i))
    for j,def in pairs(page) do
      val = vals[def["sensor"]][def["param"]]
      if val then
        val = round(val * def["scale"], def["round"])
      end
      defid = def["sensor"].."."..def["param"]
      val = lpad(tostring(val),def["pos"]).." "..def["unit"]
      if not(def["label"] == "") then
        val = def["label"]..": "..val
      end
      write_command(conn,"widget_set "..pageid.." "..defid.." "..trim(tostring((def["col"]-1)*col_width+1)).." "..def["line"].." \""..val.."\"")
    end
  end
end  

function process_graphs()
  for i,page in pairs(pages) do
    for j,def in pairs(page) do
      local sensor = def["sensor"]
      local param  = def["param"]
      local pageid = sensor.."."..param
      local cur = assert (log_con:execute(string.format("select hour,avg(value) val from (select strftime('%%Y-%%m-%%d %%H',time_stamp)||':00' hour,value from log where sensor='%s' and param='%s') group by hour order by hour desc limit 24",sensor,param)))
      local row = cur:fetch ({}, "a")
      local vals = {}
      local maxval = -99999999
      local minval =  99999999
      for k=width-def["pos"],1,-1 do
        if row then
          val = row["val"]
          row = cur:fetch ({}, "a")
        else
          val = nil  
        end  
        vals[k] = val
        if val and (val > maxval) then maxval = val; end
        if val and (val < minval) then minval = val; end
      end          
      minval = math.floor(minval)
      minvalstr = trim(tostring(minval))
      maxval = math.ceil(maxval)
      maxvalstr = trim(tostring(maxval))
      write_command(conn,"widget_set "..pageid.." "..pageid..".max "..trim(tostring(width-string.len(maxvalstr)+1)).." 2 "..maxvalstr)
      write_command(conn,"widget_set "..pageid.." "..pageid..".min "..trim(tostring(width-string.len(minvalstr)+1)).." "..height.." "..minvalstr)
      for k = 1,width-def["pos"] do
        val = vals[k]
        if val then
          h = math.floor(0.5+(val-minval)/(maxval-minval)*(height-1)*charheight)
        else
          h = 0
        end  
        write_command(conn,"widget_set "..pageid.." "..pageid..".bar"..trim(tostring(k)).." "..k.." "..height.." "..h)
      end
    end
  end
end

get_display_config()

conn = connect_server()

setup_pages(conn)

while true do

  vals = process_file()
  process_vals(vals)

  if log_con then
    process_graphs()
  end  
      
  os.execute("inotifywait -e MODIFY \""..weather_file.."\"")
  socket.sleep(3)
  
end