import std/[asyncdispatch, asynchttpserver, asyncfile]
import std/[os, strutils, mimetypes, strformat, osproc]
import sprite/[common, options, syscalls]

const webRoot = "/sprite/www"

let mimes = newMimeTypes()

type
  # from host.h
  HostEntryPrefix {.bycopy.} = object
    name: cstring
    aliases: ptr cstring
    id: cint
    machineType: cstring
  SysMachineInfo {.bycopy.} = object
    architecture: cint
    kind: cint
    processors: cint

const
  sysSpur = 1.cint
  sysSun2 = 2.cint
  sysSun3 = 3.cint
  sysSun4 = 4.cint
  sysMicroVax2 = 5.cint
  sysDs3100 = 6.cint
  sysSymmetry = 7.cint
  sysDs5000 = 8.cint
  sysPc386 = 9.cint
  sysSunArchMask = 0xf0.cint
  sysSun4C = 0x50.cint
  sysSun375 = 0x11.cint
  sysSun350 = 0x12.cint
  sysSun360 = 0x17.cint

proc machineName(): tuple[name: string, status: int] =
  var info: SysMachineInfo
  let status = sysGetMachineInfo(cint(sizeof info), addr info)
  if not status.ok:
    return ("unknown", 1)
  case info.architecture
  of sysSun2: ("sun2", 0)
  of sysSun3:
    case info.kind
    of sysSun375: ("sun3/75", 0)
    of sysSun350: ("sun3/50", 0)
    of sysSun360: ("sun3/60", 0)
    else: ("sun3", 0)
  of sysSun4:
    if (info.kind and sysSunArchMask) == sysSun4C: ("sun4c", 0) else: ("sun4", 0)
  of sysDs3100: ("ds3100", 0)
  of sysSymmetry: ("symmetry", 0)
  of sysMicroVax2: ("microvax2", 0)
  of sysSpur: ("spur", 0)
  of sysDs5000: ("ds5000", 0)
  of sysPc386: ("pc386", 0)
  else: ("unknown", 1)

proc hostById(id: cint): ptr HostEntryPrefix {.importc: "Host_ByID", cdecl.}

proc returnInfo(hostId: cint): string =
  let host = hostById(hostId)
  if host.isNil:
    return
  else: return $host.name

proc resolveFilePath(urlPath: string): string =
  if urlPath == "/":
    return webRoot / "index.html"
  
  if urlPath.startsWith("/pub/"):
    var safePath = "pub"
    for segment in urlPath[5..^1].split('/'):
      if segment notin ["", ".", ".."]:
        safePath = safePath / segment
    return webRoot / safePath

  return webRoot / extractFilename(urlPath)

proc cb(req: Request) {.async, gcsafe.} =
#  echo (req.reqMethod, req.url, req.headers)
  let filename = resolveFilePath(req.url.path)

  if not fileExists(filename):
    await req.respond(Http404, "File not found :(", {"Content-type": "text/plain"}.newHttpHeaders())
    return

  let fileExtension = splitFile(filename).ext[1..^1]
  var contentType = mimes.getMimeType(fileExtension, default="application/octet-stream")
  if contentType.startsWith("text/"):
    contentType.add "; charset=utf-8"

  let headers = {"Content-type": contentType}.newHttpHeaders()
  var body = await openAsync(filename, fmRead).readAll()

  if filename == webRoot / "index.html":
    var virtualHost, physicalHost: cint
    let status = procGetHostIds(addr virtualHost, addr physicalHost)
    var physicalHostName = returnInfo(physicalHost)
    var virtualHostName = returnInfo(virtualHost)
    var loadavg = execProcess("/sprite/cmds/loadavg -a")
    var dmesg = execProcess("/sprite/cmds/dmesg")
    body.add "<h2>Cluster Load Average</h2>"
    body.add &"<pre>{loadavg}</pre>"
    body.add "<h2>Host Kernel Log</h2>"
    body.add &"<pre>{dmesg}</pre>"
    body.add &"<p>Served by physical Sprite host {physicalHostName}, from virtual host {virtualHostName}</p>"

  await req.respond(Http200, body, headers)

proc main {.async.} =
  var server = newAsyncHttpServer()

  let port = Port(80)
  server.listen(port)

  while true:
    if server.shouldAcceptRequest():
      await server.acceptRequest(cb)
    else:
      await sleepAsync(500)

waitFor main()
