# __DRIVER_CODE__ imports

cdef extern from "<arpa/inet.h>":
  uint16_t htons(uint16_t hostshort)
  int inet_aton(const char *cp, in_addr *inp)

cdef extern from "<netinet/in.h>":
  enum: IPPROTO_UDP
  ctypedef uint16_t in_port_t
  ctypedef unsigned short sa_family_t
  struct in_addr:
    uint32_t s_addr
  struct sockaddr_in:
    sa_family_t sin_family
    in_port_t sin_port
    in_addr sin_addr
    unsigned char sin_zero[8]

cdef extern from "<sys/socket.h>":
  enum: AF_INET
  enum: SOCK_DGRAM
  ctypedef uint32_t socklen_t
  struct sockaddr:
    unsigned char sa_len
    unsigned char sa_family  
    char  sa_data[14]
  ssize_t recvfrom(int, void *restrict, size_t, int, sockaddr *restrict, socklen_t *restrict)
  int bind(int, const sockaddr *, socklen_t)
  int socket(int domain, int type, int protocol)
  ssize_t sendto(int socket, const void *message, size_t length,
       int flags, const sockaddr *dest_addr,
       socklen_t dest_len)


# __DRIVER_CODE__ variables

cdef int s
cdef sockaddr_in si_other
cdef socklen_t slen=sizeof(si_other)

# temporary pointer to pass to output APIs
cdef uint8_t *outBuf
cdef size_t sentBytes
cdef size_t outBufLen


# __DRIVER_CODE__ setup

# create a UDP socket
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
if (s == -1):
  die("NETWORK ERROR: socket\n")
# zero out the structure
memset(<char *> &si_other, 0, sizeof(si_other))
si_other.sin_family = AF_INET
si_other.sin_port = htons({{out_signal['args']['port']}})
if (inet_aton("{{out_signal['args']['addr']}}", &si_other.sin_addr)==0):
  die("inet_aton() failed\n")
{% elif sig_type == 'pipe' %}
named_pipe = open(FIFOFILE, O_RDWR)
if (named_pipe == -1):
  die("Could not open pipe \n")


# __DRIVER_CODE__ write

  for sentBytes in range(0,outBufLen,1472): # TODO read in MTU or make constant for user. or could use sendto max
    if (sendto(s, outBuf + sentBytes, 1472, 0, <sockaddr *>&si_other, slen)==-1):
      die("sendto() failed")


# __DRIVER_CODE__ exit_handler
  close(s)
