#include "common/gpio.h"

#include <string>

#ifdef __APPLE__
int gpio_init(int pin_nr, bool output) {
  return 0;
}

int gpio_set(int pin_nr, bool high) {
  return 0;
}

int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) {
  return 0;
}

#else

#include <fcntl.h>
#include <unistd.h>

#include <cstring>
#include <linux/gpio.h>
#include <sys/ioctl.h>

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

int gpio_init(int pin_nr, bool output) {
  char pin_dir_path[50];
  int pin_dir_path_len = snprintf(pin_dir_path, sizeof(pin_dir_path),
                           "/sys/class/gpio/gpio%d/direction", pin_nr);
  if (pin_dir_path_len <= 0) {
    return -1;
  }
  const char *value = output ? "out" : "in";
  return util::write_file(pin_dir_path, (void*)value, strlen(value));
}

int gpio_set(int pin_nr, bool high) {
  char pin_val_path[50];
  int pin_val_path_len = snprintf(pin_val_path, sizeof(pin_val_path),
                           "/sys/class/gpio/gpio%d/value", pin_nr);
  if (pin_val_path_len <= 0) {
    return -1;
  }
  return util::write_file(pin_val_path, (void*)(high ? "1" : "0"), 1);
}

int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) {

  // Assumed that all interrupt pins are unexported and rights are given to
  // read from gpiochip0.
  std::string gpiochip_path = "/dev/gpiochip" + std::to_string(gpiochiop_id);
  int fd = open(gpiochip_path.c_str(), O_RDONLY);
  if (fd < 0) {
    LOGE("Error opening gpiochip0 fd");
    return -1;
  }

  // Setup event
  struct gpioevent_request rq;
  rq.lineoffset = pin_nr;
  rq.handleflags = GPIOHANDLE_REQUEST_INPUT;

  /* Requesting both edges as the data ready pulse from the lsm6ds sensor is
     very short(75us) and is mostly detected as falling edge instead of rising.
     So if it is detected as rising the following falling edge is skipped. */
  rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES;

  strncpy(rq.consumer_label, consumer_label, std::size(rq.consumer_label) - 1);
  int ret = util::safe_ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
  if (ret == -1) {
    LOGE("Unable to get line event from ioctl : %s", strerror(errno));
    close(fd);
    return -1;
  }

  close(fd);
  return rq.fd;
}

#endif
