#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include <unistd.h>

typedef struct KV
{
  char* dir;
} KV;

bool
KV_Open(KV* kv, const char* dir)
{
  struct stat st;
  if (stat(dir, &st) != 0 || !S_ISDIR(st.st_mode)) {
    return false;
  }
  kv->dir = strdup(dir);
  return true;
}

bool
KV_Close(KV* kv)
{
  free(kv->dir);
  return true;
}

ssize_t
KV_Load(KV* kv, const char* key, uint8_t* buffer, size_t limit)
{
  char pathname[PATH_MAX];
  int res = snprintf(pathname, sizeof(pathname), "%s/%s", kv->dir, key);
  if (res < 0 || res >= (int)sizeof(pathname)) {
    return -1;
  }

  int fd = open(pathname, O_RDONLY);
  if (fd < 0) {
    return -1;
  }

  ssize_t size = read(fd, buffer, limit);
  close(fd);
  return size;
}

bool
KV_Save(KV* kv, const char* key, const uint8_t* value, size_t size)
{
  char pathname[PATH_MAX];
  int res = snprintf(pathname, sizeof(pathname), "%s/%s", kv->dir, key);
  if (res < 0 || res >= (int)sizeof(pathname)) {
    return false;
  }

  int fd = open(pathname, O_WRONLY | O_CREAT, 0644);
  if (fd < 0) {
    return false;
  }

  ssize_t written = write(fd, value, size);
  close(fd);
  return (size_t)written == size;
}

bool
KV_Delete(KV* kv, const char* key)
{
  char pathname[PATH_MAX];
  int res = snprintf(pathname, sizeof(pathname), "%s/%s", kv->dir, key);
  if (res < 0 || res >= (int)sizeof(pathname)) {
    return false;
  }

  return unlink(pathname) == 0;
}

int
main()
{
  KV kv;
  if (!KV_Open(&kv, "/tmp")) {
    fprintf(stderr, "KV_Open error\n");
    return 1;
  }

  const char* key = "yoursunny";
  uint8_t value[] = { 0x61, 0x77, 0x65, 0x73, 0x6F, 0x6D, 0x65 };
  if (!KV_Save(&kv, key, value, sizeof(value))) {
    fprintf(stderr, "KV_Save error\n");
    return 1;
  }

  char buffer[16];
  ssize_t size = KV_Load(&kv, key, (uint8_t*)buffer, sizeof(buffer) - 1);
  if (size < 0) {
    fprintf(stderr, "KV_Load error\n");
    return 1;
  }
  buffer[size] = '\0';
  printf("%s = %s\n", key, buffer);

  if (!KV_Delete(&kv, key)) {
    fprintf(stderr, "KV_Delete error\n");
    return 1;
  }

  if (!KV_Close(&kv)) {
    fprintf(stderr, "KV_Close error\n");
    return 1;
  }
  return 0;
}
