
import std / [strutils, sequtils]

proc elifSection(dest, destB: var string; values, enumVal: string; sections: var int) =
  let vals = values.strip.split(", ").filterIt(it.strip.len > 0)
  if vals.len > 0:
    inc sections
    let keyw = if sections == 1: "  if" else: "  elif"
    dest.add keyw & " err == "
    let firstVal = vals[0].strip
    dest.add firstVal
    for j in 1 ..< vals.len:
      dest.add " or err == "
      dest.add vals[j].strip
    dest.add ":\n"
    dest.add "    "
    dest.add enumVal
    dest.add "\n"

    destB.add "  of "
    destB.add enumVal
    destB.add ": "
    destB.add firstVal
    destB.add "\n"

proc addDecls(dest: var string; values: string) =
  let vals = values.strip.split(", ").filterIt(it.strip.len > 0)
  if vals.len > 0:
    for val in vals:
      let v = val.strip
      if v.len > 0 and v[0] == 'E':
        dest.add "var "
        dest.add v
        dest.add " {.importc: \""
        dest.add v
        dest.add "\", header: \"<errno.h>\".}: int32\n"

const
  Header = "# Generated by tools/gen_errorcodes.nim. DO NOT EDIT!\n"

proc importErrorCodes(): string =
  result = """
when not defined(nimony):
  import errorcodes
"""

proc main =
  var inp = open("errorcodes.md", fmRead)
  var enumDecl = """
type
  ErrorCode* = enum ## All possible errors in one enum."""

  var errnoDecls = ""

  var fromPosix = """proc posixToErrorCode*(err: int32): ErrorCode =
"""

  var fromWindows = """proc windowsToErrorCode*(err: int32): ErrorCode =
"""

  var fromHttp = """proc httpToErrorCode*(err: int): ErrorCode =
"""

  var toPosix = """proc errorCodeToPosix*(err: ErrorCode): int32 =
  case err
"""

  var toHttp = """proc errorCodeToHttp*(err: ErrorCode): int =
  case err
"""

  var toWindows = """proc errorCodeToWindows*(err: ErrorCode): int32 =
  case err
"""

  var i = 0
  var posixSections = 0
  var httpSections = 0
  var windowsSections = 0
  for line in lines(inp):
    inc i
    if i <= 2: continue # skip header
    var parts = line.split("|")
    if parts.len == 0: continue
    if parts.len != 7:
      quit "WRONG LINE: " & line
    parts = parts[1..^2] # ignore first and last entries which exist to due the surrounding |
    enumDecl.add "\n    "
    let enumVal = parts[0].strip
    enumDecl.add enumVal
    enumDecl.add "  ## "
    enumDecl.add parts[4].strip

    errnoDecls.addDecls parts[1]

    fromPosix.elifSection toPosix, parts[1], enumVal, posixSections
    fromHttp.elifSection toHttp, parts[2], enumVal, httpSections
    fromWindows.elifSection toWindows, parts[3], enumVal, windowsSections

  fromPosix.add """  else:
    Failure"""

  fromHttp.add """  else:
    Failure"""

  fromWindows.add """  else:
    Failure"""

  toPosix.add """  else: 1'i32"""

  toHttp.add """  else: 500"""

  toWindows.add """  else: 1124'i32"""

  inp.close()

  var outf = open("src/errorcodes.nim", fmWrite)
  outf.writeLine Header
  outf.writeLine enumDecl
  outf.close

  outf = open("src/errorcodes_posix.nim", fmWrite)
  outf.writeLine Header
  outf.writeLine importErrorCodes()
  outf.writeLine errnoDecls
  outf.writeLine ""
  outf.writeLine fromPosix
  outf.writeLine ""
  outf.writeLine toPosix
  outf.close

  outf = open("src/errorcodes_http.nim", fmWrite)
  outf.writeLine Header
  outf.writeLine importErrorCodes()
  outf.writeLine fromHttp
  outf.writeLine ""
  outf.writeLine toHttp
  outf.close

  outf = open("src/errorcodes_windows.nim", fmWrite)
  outf.writeLine Header
  outf.writeLine importErrorCodes()
  outf.writeLine fromWindows
  outf.writeLine ""
  outf.writeLine toWindows
  outf.close

main()
