#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/time.h>
#include <assert.h>
#include <math.h>
#include <ctime>
#include <chrono>
#include <map>
#include <vector>

#include "messaging.hpp"
#include <capnp/serialize.h>
#include "cereal/gen/cpp/log.capnp.h"

#include "common/util.h"
#include "common/params.h"
#include "common/swaglog.h"
#include "common/timing.h"

#include "ublox_msg.h"

volatile sig_atomic_t do_exit = 0; // Flag for process exit on signal

void set_do_exit(int sig) {
  do_exit = 1;
}

using namespace ublox;

int ubloxd_main(poll_ubloxraw_msg_func poll_func, send_gps_event_func send_func) {
  LOGW("starting ubloxd");
  signal(SIGINT, (sighandler_t) set_do_exit);
  signal(SIGTERM, (sighandler_t) set_do_exit);

  UbloxMsgParser parser;

  Context * c = Context::create();
  PubSocket * gpsLocationExternal = PubSocket::create(c, "gpsLocationExternal");
  PubSocket * ubloxGnss = PubSocket::create(c, "ubloxGnss");
  SubSocket * ubloxRaw = SubSocket::create(c, "ubloxRaw");

  assert(gpsLocationExternal != NULL);
  assert(ubloxGnss != NULL);
  assert(ubloxRaw != NULL);

  Poller * poller = Poller::create({ubloxRaw});


  while (!do_exit) {
    Message * msg = poll_func(poller);
    if (msg == NULL) continue;

    auto amsg = kj::heapArray<capnp::word>((msg->getSize() / sizeof(capnp::word)) + 1);
    memcpy(amsg.begin(), msg->getData(), msg->getSize());

    capnp::FlatArrayMessageReader cmsg(amsg);
    cereal::Event::Reader event = cmsg.getRoot<cereal::Event>();

    const uint8_t *data = event.getUbloxRaw().begin();
    size_t len = event.getUbloxRaw().size();
    size_t bytes_consumed = 0;
    while(bytes_consumed < len && !do_exit) {
      size_t bytes_consumed_this_time = 0U;
      if(parser.add_data(data + bytes_consumed, (uint32_t)(len - bytes_consumed), bytes_consumed_this_time)) {
        // New message available
        if(parser.msg_class() == CLASS_NAV) {
          if(parser.msg_id() == MSG_NAV_PVT) {
            //LOGD("MSG_NAV_PVT");
            auto words = parser.gen_solution();
            if(words.size() > 0) {
              auto bytes = words.asBytes();
              send_func(gpsLocationExternal, bytes.begin(), bytes.size());
            }
          } else
            LOGW("Unknown nav msg id: 0x%02X", parser.msg_id());
        } else if(parser.msg_class() == CLASS_RXM) {
          if(parser.msg_id() == MSG_RXM_RAW) {
            //LOGD("MSG_RXM_RAW");
            auto words = parser.gen_raw();
            if(words.size() > 0) {
              auto bytes = words.asBytes();
              send_func(ubloxGnss, bytes.begin(), bytes.size());
            }
          } else if(parser.msg_id() == MSG_RXM_SFRBX) {
            //LOGD("MSG_RXM_SFRBX");
            auto words = parser.gen_nav_data();
            if(words.size() > 0) {
              auto bytes = words.asBytes();
              send_func(ubloxGnss, bytes.begin(), bytes.size());
            }
          } else
            LOGW("Unknown rxm msg id: 0x%02X", parser.msg_id());
        } else if(parser.msg_class() == CLASS_MON) {
          if(parser.msg_id() == MSG_MON_HW) {
            //LOGD("MSG_MON_HW");
            auto words = parser.gen_mon_hw();
            if(words.size() > 0) {
              auto bytes = words.asBytes();
              send_func(ubloxGnss, bytes.begin(), bytes.size());
            }
          } else {
            LOGW("Unknown mon msg id: 0x%02X", parser.msg_id());
          }
        } else
          LOGW("Unknown msg class: 0x%02X", parser.msg_class());
        parser.reset();
      }
      bytes_consumed += bytes_consumed_this_time;
    }
    delete msg;
  }

  delete poller;
  delete ubloxRaw;
  delete ubloxGnss;
  delete gpsLocationExternal;
  delete c;

  return 0;
}
