local net = require "minitel"
local dl = {}
dl.protos = {}

-- Stolen from the old exec/fget
local function parseURL(url)
 local proto,addr = url:match("(.-)://(.+)")
 addr = addr or url
 local hp, path = addr:match("(.-)(/.*)")
 hp, path = hp or addr, path or "/"
 local host, port = hp:match("(.+):(.+)")
 host = host or hp
 return proto, host, port, path
end

function dl.protos.fget(host, optPort, path, dest) -- string string string number -- boolean -- Downloads path from host (on optPort or 70), printing the directory listing, or saving the file to dest.
 local socket = assert(net.open(host, optPort or 70))
 socket:write(string.format("t%s\n", path))
 local status
 repeat
  coroutine.yield()
  status = socket:read(1)
 until status ~= ""

 if status == "d" then
  io.write("Directory Listing:\n")
  local tmp = ""
  repeat
   coroutine.yield()
   tmp = socket:read("*a")
   
   io.write(tmp)
  until socket.state == "closed" and tmp == ""

  return true
 elseif status == "y" then
  if not dest then
   error("Must provide local path to save remote files.")
  end
  
  io.write(string.format("Saving %s to %s...\n", path, dest))
  local f = assert(io.open(dest, "wb"))
  local tmp = ""
  repeat
   coroutine.yield()
   tmp = socket:read("*a")
   
   f:write(tmp)
  until socket.state == "closed" and tmp == ""

  f:close()
  print("Done.")
  
  return true
 else
  local err, tmp = "", ""
  repeat
   coroutine.yield()
   tmp = socket:read("*a")

   err = err .. tmp
  until socket.state == "closed" and tmp == ""
  
  error(string.format("Got error from remote host: %s", err))
 end
end

function dl.protos.http(host, optPort, path, dest, url) -- string string string number -- boolean -- Downloads *url* to *dest* via the internet card, if available.
 if not component.list("internet")() then
  local proto,host,sPort,path = parseURL(url)
  local proxy = os.getenv(proto:upper().."_PROXY")
  if not proxy and fs.exists("/boot/cfg/"..proto.."_proxy") then
   local f = io.open("/boot/cfg/"..proto.."_proxy","rb")
   proxy = f:read()
   f:close()
  end
  if not proxy then error("No internet card or HTTP(S) proxy available") end
  print("Internet card unavailable, falling back to proxy "..proxy)
  if optPort then host=string.format("%s:%i",host,optPort) end
  return dl.wget(string.format("%s/%s%s",proxy,host,path),dest)
 end
 if not dest then
  error("Must provide local path to save remote files.")
 end
 local R,r=component.invoke(component.list("internet")(),"request",url)
 if not R then error(r) end
 repeat
  coroutine.yield()
 until R.finishConnect()
 local code, messsage, headers
 repeat
  coroutine.yield()
  code, message, headers = R.response()
 until code or message
 if code > 299 or code < 200 then
  return false, code, message
 end
 local f=io.open(dest,"wb")
 if not f then error("Unable to open file "..dest) end
 io.write(string.format("Saving %s to %s...\n", url, dest))
 repeat
  coroutine.yield()
  ns = R.read()
  f:write(ns or "")
 until not ns
 f:close()
 print("Done.")
 return true
end
dl.protos.https = dl.protos.http

function dl.wget(remotePath, dest) -- string string -- -- Downloads from remote *remotePath* to *dest*
 local proto, host, sPort, path = parseURL(remotePath)
 if dl.protos[proto] then
  local port
  if sPort then
   port = tonumber(sPort)
  end
  
  dl.protos[proto](host, port, path, dest, remotePath)
 else
  error("Unsupported protocol: " .. tostring(proto))
 end
end

return setmetatable(dl,{__call=function(_,path,dest) return dl.wget(path,dest) end})