#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdbool.h>
#include "utilityFunctions.h"
#include "constants.h"
// #include <stdatomic.h>
// #include <{{parser}}.h>

#define PORT 51003

static struct sockaddr_in si_me, si_other;
static socklen_t slen = sizeof(si_other);
static socklen_t recv_len;
static int s;
static pid_t ppid;
static sigset_t exitMask;
static char buf[{{max_buf_size}}];
// static char *outbuf;
static int buf_offset = 0;
static int parse_idx;
// static int in_sig_size;
static int num_packets= 0;
static int64_t numMS= -1 * LATENCY;
static bool parentSetup = true;

static uint32_t *pSignalMem;
static uint32_t *pLocalSignalMem;

void handle_exit(int exitStatus) {
//   created_mem = sa.list();
// {% for sig,args in out_signals.iteritems() %}
//   if any(['{{sig}}' == x[0] for x in created_mem]):
//     sa.delete("shm://{{sig}}");
// {% endfor %}
  close(s);
  exit(exitStatus);
}

void exit_handler(int signum) {
  handle_exit(0);
}

void bus_handler(int signum) {
  printf("Bus Error\n");
  handle_exit(1);
}

void segv_handler(int signum) {
  printf("Seg Fault\n");
  handle_exit(1);
}

void alrm_handler(int signum) {
  if (parentSetup) parentSetup = false;
  numMS++;
}

void usr2_handler(int signum) {}

int main(int argc, char* argv[]) {
  sigfillset(&exitMask);
  init_utils(&handle_exit, &exitMask);

  ppid = getppid();

  set_sighandler(SIGALRM, &alrm_handler, NULL);
  set_sighandler(SIGINT, &exit_handler, &exitMask);
  set_sighandler(SIGBUS, &bus_handler, &exitMask);
  set_sighandler(SIGSEGV, &segv_handler, &exitMask);
  set_sighandler(SIGUSR2, &usr2_handler, NULL);

  open_shared_mem((uint8_t **)&pSignalMem, "/smem_sig_offsets", PAGESIZE, O_RDWR,  PROT_READ | PROT_WRITE);
  pLocalSignalMem = pSignalMem + 0; // TODO number all internal signals for use by corresponding source/module

  // created_mem = sa.list()
  // {% for sig,args in out_signals.iteritems() %}
  // if any(['{{sig}}' == x[0] for x in created_mem]):
  //   sa.delete("shm://{{sig}}")
  //   die("numpy signal already created\n")
  // else:
  //   in_sig_size = {{args['shape']}} * {{args['history']}}
  //   in_sig = sa.create("shm://{{sig}}", in_sig_size, dtype=np.{{args['dtype']}}) // TODO, fix dtype stuff
  //   sa.mlock(in_sig)
  // {% endfor %}

  // 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_me, 0, sizeof(si_me));

  si_me.sin_family = AF_INET;
  si_me.sin_port = htons(PORT);
  // struct in_addr interface_ip = get_ip(INTERFACE_NAME);
  // si_me.sin_addr.s_addr = interface_ip.s_addr;

  // bind socket to port
  if( bind(s , (struct sockaddr *)&si_me, sizeof(si_me) ) == -1) {
    die("bind\n");
  }

  // make_realtime();

  fflush(stdout);
  kill(ppid, SIGUSR2); // TODO these kills should probably be semaphores
  pause();
  kill(ppid, SIGUSR2);

  while(true) {
    // recv_len = zmq_recv(dish, buf, {{max_buf_size}}, 0)
    recv_len = recvfrom(s, buf, {{max_buf_size}}, 0, (struct sockaddr *) &si_other, &slen);
    if (parentSetup) continue;
    num_packets++;
    if (recv_len == -1 && errno != EINTR) {
      die("NETWORK ERROR: recvfrom()");
    }
    if (recv_len == -1) {
      die("Read error.\n");
    }
    // int outlen = {{parser}}(buf, recv_len, &outbuf);
    parse_idx = 0;
    while (parse_idx < recv_len) {
      // in_sig[buf_offset] = buf[parse_idx];
      buf_offset++;
      // if (buf_offset >= in_sig_size) {
        // buf_offset = 0;
      // }
      parse_idx++;
    }
    pLocalSignalMem[2] = buf_offset;
    printf("source: %d\n", recv_len);
    // printf("%s\n", buf);
  }
}
