# Copyright (c) 2019 Status Research & Development GmbH
# Licensed under either of
#  * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
#  * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.

# This is the equivalent of miniupnpc/pymoduletest.py (without the command line args).

import nat_traversal/miniupnpc, result, strformat

template checkError(expr, body: untyped): untyped =
  block:
    let res {.inject.} = expr
    if res.isOk:
      body
    else:
      echo res.error
      quit(1)

echo &"miniupnpc-{MINIUPNPC_VERSION}, API version {MINIUPNPC_API_VERSION}"
var upnp = newMiniupnp()
upnp.discoverDelay = 200
echo &"Discovering... (delay={upnp.discoverDelay})"

checkError upnp.discover():
  echo res.value, " device(s) detected"

case upnp.selectIGD():
  of IGDNotFound:
    echo "Internet Gateway Device not found. Giving up."
    quit(1)
  of IGDFound:
    echo "Internet Gateway Device found."
  of IGDNotConnected:
    echo "Internet Gateway Device found but it's not connected. Trying anyway."
  of NotAnIGD:
    echo "Some device found, but it's not recognised as an Internet Gateway Device. Trying anyway."

echo "Local ip address: ", upnp.lanaddr
var externalIP: string
checkError upnp.externalIPAddress():
  externalIP = res.value
  echo "External ip address: ", res.value
checkError upnp.statusInfo():
  echo &"Status: {res.value.status}, uptime: {res.value.uptime}, lastConnError: {res.value.lastconnerror}"
checkError upnp.connectionType():
  echo &"Connection type: {res.value}"

var bytesSent, bytesReceived, packetsSent, packetsReceived: culonglong
checkError upnp.totalBytesSent():
  bytesSent = res.value
checkError upnp.totalBytesReceived():
  bytesReceived = res.value
checkError upnp.totalPacketsSent():
  packetsSent = res.value
checkError upnp.totalPacketsReceived():
  packetsReceived = res.value
echo &"Total bytes: sent {bytesSent}, received {bytesReceived}"
echo &"Total packets: sent {packetsSent}, received {packetsReceived}"

proc printPortMapping(pm: PortMappingRes) =
  echo &"Port mapping: {externalIP}:{pm.externalPort} -> {pm.internalClient}:{pm.internalPort} ({pm.protocol}, \"{pm.description}\", enabled: {pm.enabled}, lease duration: {pm.leaseDuration})"

## enable this if you don't already have a redirection for port 64000:
if false:
  let port = "64000"
  checkError upnp.addPortMapping(port, TCP, upnp.lanAddr, port, "port mapping test", 0, externalIP):
    echo "Added port mapping for: ", port
  checkError upnp.getSpecificPortMapping(port, TCP):
    printPortMapping(res.value)
  checkError upnp.deletePortMapping(port, TCP):
    echo "Deleted port mapping for: ", port
  doAssert(upnp.getSpecificPortMapping(port, TCP).isErr)

var i = 0
while true:
  let res = upnp.getGenericPortMapping(i)
  if res.isErr:
    break
  printPortMapping(res.value)
  inc(i)

let res = upnp.getPortMappingNumberOfEntries()
if res.isOk:
  echo "Port mapping number of entries: ", res.value
else:
  echo &"getPortMappingNumberOfEntries() is not supported by this IGD. Error message: \"{res.error}\""