-- ************************************************************************
--
--    Read a file just prior to sending it
--    Copyright 2019 by Sean Conner.  All Rights Reserved.
--
--    This program is free software: you can redistribute it and/or modify
--    it under the terms of the GNU General Public License as published by
--    the Free Software Foundation, either version 3 of the License, or
--    (at your option) any later version.
--
--    This program is distributed in the hope that it will be useful,
--    but WITHOUT ANY WARRANTY; without even the implied warranty of
--    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--    GNU General Public License for more details.
--
--    You should have received a copy of the GNU General Public License
--    along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
--    Comments, questions and criticisms can be sent to: [email protected]
--
-- ************************************************************************
-- luacheck: ignore 611

local syslog   = require "org.conman.syslog"
local mimetype = require "org.conman.parsers.mimetype"
local fsys     = require "org.conman.fsys"
local magic    = require "org.conman.fsys.magic"
local url      = require "org.conman.parsers.url.gopher"
              + require "org.conman.parsers.url"
local mklink   = require "port70.mklink"
local cgi      = require "port70.cgi"
local lpeg     = require "lpeg"
local io       = require "io"
local table    = require "table"

local require  = require
local type     = type
local assert   = assert
local pcall    = pcall

-- ************************************************************************

local parseline do
 local Cc = lpeg.Cc
 local C  = lpeg.C
 local P  = lpeg.P
 local R  = lpeg.R

 local entry = P"\t"
             * C(R" ~"^1) * P"\t"^1 -- type
             * C(R" ~"^1) * P"\t"^1 -- selector
             * C(R" \255"^0)        -- display

 local code  = P"\t"
             * C"Lua{"              -- type
             * Cc""                 -- selector
             * Cc""                 -- display

 local info  = Cc"info"             -- type
             * Cc""                 -- selector
             * C(R" \255"^0)        -- display

 parseline = entry + code + info
end

local cleanpath do
 local char = lpeg.P"/"^1 / "/"
            + lpeg.P(1)
 cleanpath  = lpeg.Cs(char^0)
end

-- ************************************************************************

local function execblock(name,file,ios)
 local acc = {}

 repeat
   local line = file:read("*L")
   table.insert(acc,line)
 until line:match "}Lua"

 table.remove(acc)

 local code  = table.concat(acc," ")
 local env   = { require = require }
 local f,err = load(code,name,"t",env)

 if not f then
   syslog('error',"%s: %s",name,err)
   ios:write(mklink {
       type = 'info',
       display = "Nothing in particular right now"
   })
 else
   local okay,data = pcall(f)
   if not okay then
     syslog('error',"name=%q err=%q",name,data)
   else
     if type(data) == 'table' then
       ios:write(table.concat(data,"\r\n"))
     else
       ios:write(data)
     end
   end
 end
end

-- ************************************************************************

return function(filename,ext,info,request,ios)
 assert(ios)
 assert(ios.write)
 assert(ios._drain)

 filename = cleanpath:match(filename)

 if not filename then
   syslog('warning',"readfile() bad cleanpath")
   ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
   return false
 end

 if info.cgi and fsys.access(filename,"rx") then
   return cgi(filename,info,request,ios)
 end

 if filename:match(ext) then
   local file = io.open(filename,"r")
   if not file then
     ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
     return false
   end

   for line in file:lines() do
     local gtype,selector,display = parseline:match(line)
     if gtype == 'url' then
       local uri = url:match(selector)
       if uri.scheme == 'gopher' then
         uri.display = display
         ios:write(mklink(uri))
       else
         ios:write(mklink {
               type     = 'html',
               display  = display,
               selector = "URL:" .. selector
         })
       end
     elseif gtype == 'Lua{' then
       execblock(filename,file,ios)
     else
       ios:write(mklink {
               type     = gtype,
               display  = display,
               selector = selector
       })
     end
   end

   file:close()
   return true
 end

 local mime = mimetype:match(magic(filename))

 if not mime then
   syslog('error',"%s: failed to get MIME type",filename)
   ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
   return false

 elseif mime.type:match "^text/" then
   local file,err = io.open(filename,"r")
   if not file then
     syslog('error',"io.open(%q) = %s",filename,err)
     ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
     return false
   end

   for line in file:lines() do
     ios:write(line,"\r\n")
   end

   file:close()
   return true

 else
   local file,err = io.open(filename,"rb")
   if not file then
     syslog('error',"io.open(%q) = %s",filename,err)
     ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
     return false
   end

   repeat
     local data = file:read(1024)
     if data then
       ios:write(data)
     end
   until not data

   file:close()
   return true,true
 end
end