/*
 *  Copyright 1994-2022 Olivier Girondel
 *
 *  This file is part of lebiniou.
 *
 *  lebiniou is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  lebiniou is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with lebiniou. If not, see <http://www.gnu.org/licenses/>.
 */

#include <glib/gstdio.h>
#include "globals.h"


void
xdebug(const char *fmt, ...)
{
#ifdef DEBUG
  va_list ap;

  fprintf(stderr, "==== ");
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);
#endif
}


void
xerror(const char *fmt, ...)
{
  va_list ap;

  fprintf(stderr, "O_o ");
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);

  abort(); /* to get a backtrace */
}


void
xperror(const char *why)
{
  fprintf(stderr, "[!] System error: ");
  perror(why);
  exit(1);
}


void
okdone(const char *what)
{
  if (libbiniou_verbose) {
    printf("[+] %s\n", what);
  }
}


void *
xmalloc(const size_t size)
{
  void *pouet = malloc(size);

  if (NULL == pouet) {
    xperror("malloc");
  }

  return pouet;
}


void *
xcalloc(const size_t nmemb, const size_t size)
{
  void *pouet = calloc(nmemb, size);

  if (NULL == pouet) {
    xperror("calloc");
  }

  return pouet;
}


void *
xrealloc(void *ptr, size_t size)
{
  void *pouet = realloc(ptr, size);

  if (NULL == pouet) {
    xperror("realloc");
  }

  return pouet;
}


double
xatof(const char *value)
{
  double f;

  errno = 0;
  f = strtod(value, NULL);
  if (errno != 0) {
    xperror("strtod");
  }

  return f;
}


long
xstrtol(const char *value)
{
  long l;

  errno = 0;
  l = strtol(value, NULL, 10);
#ifndef __FreeBSD__
  if (errno != 0) {
    xperror("strtol");
  }
#endif

  return l;
}


uint64_t
xstrtoull(const char *value)
{
  uint64_t l;

  errno = 0;
  l = strtoull(value, NULL, 10);
#ifndef __FreeBSD__
  if (errno != 0) {
    xperror("strtoull");
  }
#endif

  return l;
}


uint32_t
FNV_hash(const char* str)
{
  const unsigned int fnv_prime = 0x811C9DC5;
  unsigned int hash = 0;
  int c;

  while ((c = *str++)) {
    hash *= fnv_prime;
    hash ^= c;
  }

  return (uint32_t)hash;
}


void
ms_sleep(const uint32_t msec)
{
#if 0 /* nanosleep seems NOK */
  struct timespec ts;

  ts.tv_sec = (msec / 1000);
  ts.tv_nsec = (msec % 1000) * 1000000;

  nanosleep(&ts, NULL);
#else
  struct timeval tv;

  tv.tv_sec = (msec / 1000);
  tv.tv_usec = (msec % 1000) * 1000;

  select(0, 0, 0, 0, &tv);
#endif
}


/*!
 * Parses a string containing 2 shorts
 * eg "960x540" => 960, 540 (for resolution)
 * "15,30" => 15, 30 (for timers)
 */
int
parse_two_shorts(const char *str, const int delim, short *a, short *b)
{
  int rc = 0;
  char *dup = NULL;
  char *found = NULL;
  long a0, b0;

  if ((NULL == a) && (NULL == b)) {
    xerror("%s: No variable to set !\n", __FUNCTION__, a, b);
  }

  dup = strdup(str);
  if (NULL == dup) {
    xperror("strdup");
  }

  found = strchr(str, delim);
  if (NULL == found) {
    xerror("%s: Did not find delimiter '%c' in \"%s\"\n", __FUNCTION__, delim, str);
  }
  *found++ = '\0';

  if (NULL != a) {
    a0 = xstrtol(dup);
    if ((a0 >= SHRT_MIN) && (a0 <= SHRT_MAX)) {
      *a = a0;
    } else {
      rc = -1;
    }
  }

  if (NULL != b) {
    b0 = xstrtol(found);
    if ((b0 >= SHRT_MIN) && (b0 <= SHRT_MAX)) {
      *b = b0;
    } else {
      rc = -1;
    }
  }

  xfree(dup);

  return rc;
}


/* returns 0 if command was executed, -1 otherwise */
int
check_command(const char *cmd)
{
  if (NULL != cmd) {
    int res = system(cmd);
    if (!res) {
      return 0;
    }
  }
  return -1;
}


time_t
unix_timestamp(void)
{
  struct timeval t;

  gettimeofday(&t, NULL);
  return t.tv_sec;
}


inline int
is_equal(const char *s1, const char *s2)
{
  return ((NULL != s1) && (NULL != s2)) ? !strcmp(s1, s2) : 0;
}


// these characters aren't safe in a filename
static const char unsafe[] = "$`'\"(){}[]*?!\t\r\n/.";

uint8_t
safe_filename(const char *filename)
{
  if ((NULL == filename) || !strlen(filename)) {
    return 0;
  }

  for (uint8_t i = 0; i < strlen(unsafe); i++)
    if (NULL != strchr(filename, unsafe[i])) {
      return 0;
    }

  return 1;
}


json_t *
json_strtok(const char *str, const char *delim)
{
  json_t *res = NULL;

  if (str != NULL) {
    char *s = strdup(str), *p;

    res = json_array();
    for (char *token = strtok_r(s, delim, &p); NULL != token; token = strtok_r(NULL, delim, &p)) {
      json_array_append_new(res, json_string(token));
    }
    xfree(s);
  }

  return res;
}


#define DESKTOP_DIR "Le_Biniou"


char *
get_desktop_dir(void)
{
  FILE *fp;
  char dir[1035];

  fp = popen("xdg-user-dir DESKTOP", "r");
  if (NULL == fp) {
    fprintf(stderr, "[!] Failed to run xdg-user-dir\n" );
    return NULL;
  }
  if (fgets(dir, sizeof(dir), fp) != NULL) {
    pclose(fp);
    dir[strlen(dir) - 1] = '\0';

    return strdup(dir);
  } else {
    return NULL;
  }
}


uint8_t
has_desktop_symlink(void)
{
  char *dir = get_desktop_dir();
  int ret = 0;

  if (NULL != dir) {
    gchar *d = g_strdup_printf("%s/%s", dir, DESKTOP_DIR);
    GStatBuf sbuf;

    if (!g_lstat(d, &sbuf)) {
      if ((sbuf.st_mode & S_IFMT) == S_IFLNK) {
        ret = 1;
      }
    }
    g_free(d);
    xfree(dir);
  }

  return ret;
}


int8_t
create_dirs(void)
{
  char *dir = get_desktop_dir();

  if (NULL != dir) {
    gchar *d = g_strdup_printf("%s/." PACKAGE_NAME, g_get_home_dir());
    mkdir(d, DIRECTORY_MODE);
    g_free(d);

    gchar *target = g_strdup_printf("%s/.%s", g_get_home_dir(), PACKAGE_NAME);
    gchar *linkpath = g_strdup_printf("%s/%s", dir, DESKTOP_DIR);
    unlink(linkpath);
    int ret = symlink(target, linkpath);
    if (ret == -1) {
      xperror("symlink");
    }
    g_free(target);
    g_free(linkpath);

    d = g_strdup_printf("%s/%s/images", dir, DESKTOP_DIR);
    g_mkdir_with_parents(d, DIRECTORY_MODE);
    g_free(d);

    d = g_strdup_printf("%s/%s/videos", dir, DESKTOP_DIR);
    g_mkdir_with_parents(d, DIRECTORY_MODE);
    g_free(d);

    d = g_strdup_printf("%s/%s/sequences", dir, DESKTOP_DIR);
    g_mkdir_with_parents(d, DIRECTORY_MODE);
    g_free(d);

    xfree(dir);

    return 0;
  } else {
    return -1;
  }
}
