#!/usr/bin/lua

local host = os.getenv("HTTP_HOST")
local proto = os.getenv("SERVER_PROTOCOL")
local query = os.getenv("QUERY_STRING")
local method = os.getenv("REQUEST_METHOD")
local clength = os.getenv("CONTENT_LENGTH")
local ctype = os.getenv("CONTENT_TYPE")

function http_print(s)
  if s then
    io.stdout:write(tostring(s).."\r\n")
  else
    io.stdout:write("\r\n")
  end
end

if #arg> 0 then
  method="GET"
  query=arg[1]
  proto = "HTTP/1.0"
end

function SendError(err,desc)
  http_print(proto.." "..err)
  http_print("Content-Type: text/html")
  http_print()
  local file = io.open("e404.html")
  if file then
    local tmp = file:read("*a")
    tmp = string.gsub(tmp,"404 Not Found",err .. " " .. desc)
    http_print(tmp)
    file:close()
  end
end

function perr(s)
   io.stderr:write(tostring(s).."\n")
end


--
local newdecoder = require("lunajson.decoder")
local newencoder = require("lunajson.encoder")

local function LoadTransponderList()
   local tl = nil
   local f = nil
   f = io.open("/config/TransponderList.json","r")
   if not f then
      f = io.open("/var/channels/TransponderList.json","r")
   end

   if f then
      local t = f:read("*a")
      f:close()
      local decode = newdecoder()
      tl = decode(t)
   end
   return tl
end

local function CheckTransponderList(tl)
   local i,Source
   for i,Source in ipairs(tl.SourceList) do
      if not Source.Title then
         error("SourceList["..i.."].Title missing",0)
      end
      if not Source.Key then
         error("SourceList["..i.."].Key missing",0)
      end
      if not Source.DVBType then
         error("SourceList["..i.."].DVBType missing",0)
      end
      if not Source.TransponderList then
         error("SourceList["..i.."].TransponderList missing",0)
      end
      local j,Transponder
      for j,Transponder in ipairs(Source.TransponderList) do
         if not Transponder.Request then
            error("SourceList["..i.."].TransponderList["..j.."].Request missing",0)
         end
      end
   end
   if tl.CIMapList then
      local CIMap
      for i,CIMap in ipairs(tl.CIMapList) do
         if not CIMap.Slot then
            error("CIMapList["..i.."].Slot missing",0)
         end
      end
   end
   if tl.ChannelOverwriteList then
      local ChannelOverwrite
      for i,ChannelOverwrite in ipairs(tl.ChannelOverwriteList) do
         if not ChannelOverwrite.ID then
            error("ChannelOverwriteList["..i.."].ID missing",0)
         end
      end
   end
   if tl.GroupList then
      local Group
      for i,Group in ipairs(tl.GroupList) do
         if not Group.Title then
            error("GroupList["..i.."].Title missing",0)
         end
      end
   end
   return "TransponderList.json"
end

local function CheckChannelList(cl)
   local i,Group
   for i,Group in ipairs(cl.GroupList) do
      if not Group.Title then
         error("GroupList["..i.."].Title missing",0)
      end
      local j,Channel
      for j,Channel in ipairs(Group.ChannelList) do
         if not Channel.Title then
            error("GroupList["..i.."].ChannelList["..j.."].Title missing",0)
         end
         if not Channel.Request then
            error("GroupList["..i.."].ChannelList["..j.."].Request missing",0)
         end
      end
   end
   return "ChannelList.json"
end


local function GetCMD(s)
   local q,v
   local params = ""
   local cmd = ""
   for q,v in s:gmatch("(%a+)=([%w%.]+)") do
      if q == "select" then
         cmd = v
      elseif q ~= "t" then
         params = params.." "..q.."="..v
      end
   end
   return cmd,params
end

local function Keys()
   local data = nil
   local tl = LoadTransponderList()
   if tl then
      local kl = { KeyList = { } }
      local s
      for _,s in ipairs(tl.SourceList) do
         table.insert(kl.KeyList, { Key=s.Key,Title=s.Title,DVBType=s.DVBType })
      end
      kl.KeyList[0] = #kl.KeyList
      local encode = newencoder()
      data = encode(kl)
   end
   return data
end

local function Scan(cmd,params)
   local data = nil
   local rc = os.execute("mkdir /tmp/doscan.lock")
   if rc ~= 0 then
      data = '{"status":"busy"}'
   else
      data = '{"status":"retry"}'
      local f = io.open("/tmp/doscan.msg","w+")
      if f then
         f:write("Scanning")
         f:close()
      end
      os.execute("/var/channels/do"..cmd..".lua "..params.." >/tmp/doscan.log 2>&1 &")
   end
   return data
end

local function Status()
   local data = nil
   local js = { }
   local f = io.open("/tmp/doscan.lock/doscan.msg")
   if f then
      js.status = "active"
      local m = f:read("*l")
      local count,msg = m:match("(%d+):(.*)")
      js.count = count
      js.msg = msg
      f:close()
   else
      f = io.open("/tmp/doscan.msg")
      if f then
         local m = f:read("*l")
         local count,msg = m:match("(%d+):(.*)")
         if count and msg then
            js.count = count
            js.msg = msg
            js.status = "done"
         else
            if m == "Scanning" then
               js.status = "retry"
            else
               js.status = nil
            end
         end
         f:close()
      else
         js.status = ""
      end
   end
   local encode = newencoder()
   data = encode(js)
   return data
end

local function Delete(params)
   local data = nil
   local rc = os.execute("mkdir /tmp/doscan.lock")
   if rc ~= 0 then
      data = '{"status":"busy"}'
   else
      data = '{"status":"deleted"}'
      os.execute("rm /config/ChannelList.json");
      if params == " all=true" then
         os.execute("rm /config/TransponderList.json");
      end
      os.execute("rm /tmp/doscan.msg");
      os.execute("rm -rf /tmp/doscan.lock");
   end
   return data
end

local function Restore()
   local data = nil
   local rc = os.execute("mkdir /tmp/doscan.lock")
   if rc ~= 0 then
      data = '{"status":"busy"}'
   else
      local rc = os.execute("mv /config/ChannelList.bak /config/ChannelList.json");
      if rc == 0 then
         data = '{"status":"restored", "count":1}'
      else
         data = '{"status":"restored", "count":0}'
      end
      os.execute("rm /tmp/doscan.msg");
      os.execute("rm -rf /tmp/doscan.lock");
   end
   return data
end

local function Upload()
   local data = nil
   local rc = os.execute("mkdir /tmp/doscan.lock")
   if rc ~= 0 then
      data = '{"status":"busy"}'
   else
      local boundary = string.match(ctype,"boundary=(.*)")
      if boundary then
         while true do
           local line = io.stdin:read()
           line = string.gsub(line,"\r","")
           if line == "" then break end
         end

         local filedata = io.stdin:read("*a")
         local i = filedata:find("--"..boundary,1,true)
         if i then
            filedata = filedata:sub(1,i-1)
         else
            filedata = "{}"
         end

         local decode = newdecoder()
         local rc,json = pcall(decode,filedata)

         if rc then
            rc = false
            local msg = "invalid list"
            if json.SourceList then
               rc,msg = pcall(CheckTransponderList,json)
               data = '{"status":"updated", "msg":"Transponder list"}'
            elseif json.GroupList then
               rc,msg = pcall(CheckChannelList,json)
            end
            if rc then
               local f = io.open("/config/"..msg,"w+")
               if f then
                  f:write(filedata)
                  f:close()
                  data = '{"status":"updated", "msg":"'..msg..'"}'
               else
                  data = '{"status":"error", "msg":"'..msg..' not saved"}'
               end
            else
               data = '{"status":"error", "msg":"'..msg..'"}'
            end
         else
            local msg = "unknown error"
            if json then
               msg = json:match(".-: (.*)")
            end
            data = '{"status":"error", "msg":"'..msg..'"}'
         end

      else
        data = '{"status":"error","msg":"malformed request"}'
      end
      os.execute("rm /tmp/doscan.msg");
      os.execute("rm -rf /tmp/doscan.lock");
   end
   return data
end


local filename = nil
local contenttype = "application/json; charset=utf-8"
local data = nil
local cmd = ""
local params

if method == "GET" then
   cmd,params = GetCMD(query)
elseif method == "POST" and clength and ctype then
   if ctype:match("application/x%-www%-form%-urlencoded") then
      local query = io.read(tonumber(clength))
      query = string.gsub(query,"\r","")
      cmd,params = GetCMD(query)
   elseif ctype:match("multipart/form%-data") then
      cmd = "upload"
   end
else
   SendError("500","What")
   return
end

if cmd == "keys" then
   data = Keys()
elseif cmd == "scan" then
   data = Scan("scan",params)
elseif cmd == "epg" then
   data = Scan("epgscan",params)
elseif cmd == "status" then
   data = Status()
elseif cmd == "delete" then
   data = Delete(params)
elseif cmd == "restore" then
   data = Restore()
elseif cmd == "upload" then
   data = Upload()
end

if data then
   http_print(proto.." 200" )
   http_print("Pragma: no-cache")
   http_print("Cache-Control: no-cache")
   http_print("Content-Type: "..contenttype)
   if filename then
      http_print('Content-Disposition: filename="'..filename..'"')
   end
   http_print(string.format("Content-Length: %d",#data))
   http_print()
   http_print(data)
else
   SendError("404","not found")
end
