#!/usr/bin/lua

require "uci"
local cur = uci.cursor()
local socket = require "socket"
local lfs = require "lfs"
local json = require "json"

require "wm_util"

function get_levels_list(config_name)
  local levels, engage, engage_mode, disengage_mode, levels_idx
  levels = {}
  levels_idx = {}
  engage = {}
  engage_mode = {}
  disengage_mode = {}
  cur.foreach(config_name, "alarm", function(s)
    local idx = #levels+1
    levels[idx] = s["name"]
    engage[idx] = s["engage"]
    engage_mode[idx] = s["engage_mode"]
    disengage_mode[idx] = s["disengage_mode"]
    levels_idx[s[".name"]] = idx
  end)
  return levels, engage, engage_mode, disengage_mode, levels_idx
end

function get_params(config_name,levels_idx)
  local a_names, a_formats, a_limits
  a_names = {}
  a_formats = {}
  a_limits = {}
  cur.foreach(config_name,"params", function(s)
    if s["name"] then
      a_names[s["param"]] = s["name"]
    end
    if s["format"] or s["scale"] then
      local format, scale
      if s["scale"] then
        scale = s["scale"]
      else
        scale = 1  
      end
      if s["format"] then
        format = s["format"]
      else
        format = "4s"
      end
      a_formats[s["param"]] = {format,scale}
    end  
    if s["limits"] then
      for i,record in pairs(s["limits"]) do
        rec = split(record,":")
        idx = levels_idx[rec[1]]
        low = tonumber(rec[2])
        high = tonumber(rec[3])
        if not a_limits[s["param"]] then
          a_limits[s["param"]] = {}
        end
        if not a_limits[s["param"]][idx] then
          a_limits[s["param"]][idx] = {}
        end
        rec = a_limits[s["param"]][idx]
        a_limits[s["param"]][idx][#rec+1] = {low,high}
      end  
    end
  end)
  return a_names,a_formats,a_limits;
end

function check_limit(param,value,limits)

  limit = limits[param]
  if limit then
  
    for level,ranges in pairs(limit) do
      for key,range in pairs(ranges) do
        if value>=range[1] and value<range[2] then
          return level
        end  
      end
    end

    return 0
  
  else
  
    return 0
  
  end

end

local config_name = arg[1]

if not config_name then
  config_name = "weathermon"
end

web_id = get_devid(config_name)

local a_levels, a_engage, a_engage_mode, a_disengage_mode, levels_idx = get_levels_list(config_name)

a_leds = {}

for level,leds in pairs(a_engage) do

  for key,led in pairs(leds) do
  
    a_leds[led] = a_disengage_mode[level]
  
  end

end

local a_names,a_formats,limits = get_params(config_name,levels_idx)

local w_led = cur.get(config_name, "process", "engage")
local w_engage = cur.get(config_name, "process", "engage_mode")
local w_disengage = cur.get(config_name, "process", "disengage_mode")

local senstemplate = string.gsub(cur.get(config_name, "display", "formatstr"),"~",string.char(223))
local out_file = cur.get(config_name, "display", "file")

local watch_file = cur.get(config_name,"process","dump_file")

if not watch_file then
  return
end  

local last = 0

local data = ""
local old_data = ""
local sensor_data
local res

local mute_file=cur.get(config_name, "process", "mute_file")
local mute_time=tonumber(cur.get(config_name, "process", "mute_time"))
local alarm_raise = 2
local onoff=true

local timestr_template = cur.get(config_name, "display", "timestr")
if not timestr_template then
  timestr_template = "  %d.%m.%y   %H:%M  "
end

local alarmstr_len = cur.get(config_name, "display", "strlen")
if not alarmstr_len then
  alarmstr_len = 20
end
alarmstr_len=tonumber(alarmstr_len)

while true do

  onoff = not onoff

  local timestr = os.date(timestr_template)

  pcall(function ()

    local mod = lfs.attributes(watch_file,"modification")
    local muted_mod = lfs.attributes(mute_file,"modification")
    local muted_beep = muted_mod and (muted_mod > (os.time() - mute_time))
  
    if mod then
   
      if last < mod then
        last = mod
        res,sensor_data = pcall(function ()
          local f = io.open(watch_file,"r")
          content = f:read("*all")
          io.close(f)
          return json.decode(content)
        end)      
    
        write_file(w_led,w_engage)
   
      else

        write_file(w_led,w_disengage)
      
      end 

      local values = {}
      local printable = {}

      for sensor,sensor_params in pairs(sensor_data[web_id]) do
        if sensor ~= "timestamp" then
          for param,value in pairs(sensor_params) do
            local name = sensor.."."..param
            values[name]=value
            if a_formats[name] then
              printable[name]=string.format("%"..a_formats[name][1],value*a_formats[name][2])
            end  
          end
        end  
      end

      level = 1
      alarms = ""

      if not muted_beep then
        for key,value in pairs(values) do
          value_level = check_limit(key,value,limits)
          if value_level > level then
            level = value_level
            alarms = a_names[key]
          elseif value_level == level then  
            alarms = alarms.." "..a_names[key]
          end   
        end
      end  

      leds_engage = {} 
    
      for key,value in pairs(a_engage[level]) do
        leds_engage[value] = a_engage_mode[level]
      end
 
      for led,mode in pairs(a_leds) do
        if leds_engage[led] then
          write_file(led,leds_engage[led])
        else  
          write_file(led,mode)
        end
      end
  
      alarms = trim(alarms)
      alarmstr = a_levels[level]..": "..alarms
    
      if alarmstr:len()>alarmstr_len then
        alarmstr=alarmstr:sub(1,alarmstr_len)
      elseif alarmstr:len()<alarmstr_len then
        local delta = alarmstr_len - alarmstr:len()
        local before = math.floor(delta/2)
        local after = delta - before
        alarmstr = string.rep(" ",before)..alarmstr..string.rep(" ",after)
      end

      sensstr = string.gsub(senstemplate,"%{.-%}", function (s) return printable[s:sub(2,s:len()-1)]; end)

      if onoff and (level>=alarm_raise) then
  	data = alarmstr..sensstr
      else
        data = timestr..sensstr
      end

      if old_data ~= data then
   
        old_data = data
        print(data)
        write_file(out_file,data)
   
      end   
  
    end
    
  end)  

  local t = os.date("*t",os.time()+60) 
  t["sec"] = 0
  local sec = os.time(t)-os.time()
  if sec>0 then
    if sec>5 then
      sec=5
    end
    socket.sleep(sec)
  end

end