/** \file
 * \brief UNIX System Information
 *
 * See Copyright Notice in "iup.h"
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

/* This module should depend only on IUP core headers
   and UNIX system headers. NO Motif headers allowed. */

#include <sys/utsname.h>
#include <unistd.h>
#include <errno.h>
#include <langinfo.h>
#include <syslog.h>

#include "iup_export.h"
#include "iup_str.h"
#include "iup_drvinfo.h"
#include "iup_varg.h"


IUP_SDK_API char* iupdrvLocaleInfo(void)
{
  return iupStrReturnStr(nl_langinfo(CODESET));
}

IUP_SDK_API char *iupdrvGetSystemName(void)
{
  struct utsname un;
  uname(&un);
  if (iupStrEqualNoCase(un.sysname, "Darwin"))
    return iupStrReturnStr("macOS");
  else
    return iupStrReturnStr(un.sysname);
}

IUP_SDK_API char *iupdrvGetSystemVersion(void)
{
  struct utsname un;
  uname(&un);
  if (iupStrEqualNoCase(un.sysname, "Darwin"))
  {
    int release = 0; iupStrToInt(un.release, &release);
    return iupStrReturnInt(release-4);
  }
  else
    return iupStrReturnStrf("%s.%s", un.release, un.version);
}

IUP_SDK_API int iupdrvSetCurrentDirectory(const char* dir)
{
  return chdir(dir) == 0 ? 1 : 0;
}

IUP_SDK_API char* iupdrvGetCurrentDirectory(void)
{
  size_t size = 256;
  char *buffer = (char *)iupStrGetMemory(size);

  for (;;)
  {
    if (getcwd(buffer, size) != NULL)
      return buffer;

    if (errno != ERANGE)
    {
      free(buffer);
      return NULL;
    }

    size += size;
    buffer = (char *)iupStrGetMemory(size);
  }

  return NULL;
}

/**************************************************************************/

static int iupUnixMakeDirectoryIfNeeded(const char* path)
{
  struct stat st;
  if (stat(path, &st) == 0)
  {
    if (S_ISDIR(st.st_mode))
      return 1;
    return 0;
  }
  {
    mode_t oldmask = umask((mode_t)0);
    int fail = mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH);
    umask(oldmask);
    if (fail)
      return 0;
    return 1;
  }
}

IUP_SDK_API char *iupdrvGetComputerName(void)
{
  static char hostname[256];
  if (gethostname(hostname, sizeof(hostname)) == 0)
    return iupStrReturnStr(hostname);
  return iupStrReturnStr("unknown");
}

IUP_SDK_API char *iupdrvGetUserName(void)
{
  const char* username = getenv("USER");
  if (!username)
    username = getenv("USERNAME");
  return iupStrReturnStr(username ? username : "unknown");
}

IUP_SDK_API int iupdrvGetPreferencePath(char *filename, const char *app_name, int use_system)
{
  char* home;

  if (!app_name || !app_name[0])
  {
    filename[0] = '\0';
    return 0;
  }

  if (use_system)
  {
    if (!iupdrvGetUserDir(filename, 10240, IUP_USER_DIR_CONFIG))
    {
      filename[0] = '\0';
      return 0;
    }
    snprintf(filename + strlen(filename), 10240 - strlen(filename), "/%s", app_name);
    iupUnixMakeDirectoryIfNeeded(filename);
    snprintf(filename + strlen(filename), 10240 - strlen(filename), "/config");
    return 1;
  }

  home = getenv("HOME");
  if (!home)
  {
    filename[0] = '\0';
    return 0;
  }
  snprintf(filename, 10240, "%s/.%s", home, app_name);
  return 1;
}

IUP_SDK_API int iupdrvGetUserDir(char* path, int size, int kind)
{
  const char* xdg_var;
  const char* fallback_subdir;
  char* xdg;
  char* home;

  if (!path || size <= 0)
    return 0;
  path[0] = '\0';

  if (kind == IUP_USER_DIR_TEMP)
  {
    char* tmp = getenv("TMPDIR");
    if (!tmp || !tmp[0]) tmp = getenv("TMP");
    if (!tmp || !tmp[0]) tmp = "/tmp";
    iupStrCopyN(path, size, tmp);
    return path[0] ? 1 : 0;
  }

  switch (kind)
  {
    case IUP_USER_DIR_CACHE:  xdg_var = "XDG_CACHE_HOME";  fallback_subdir = ".cache";       break;
    case IUP_USER_DIR_DATA:   xdg_var = "XDG_DATA_HOME";   fallback_subdir = ".local/share"; break;
    case IUP_USER_DIR_CONFIG: xdg_var = "XDG_CONFIG_HOME"; fallback_subdir = ".config";      break;
    default: return 0;
  }

  xdg = getenv(xdg_var);
  if (xdg && xdg[0])
  {
    iupStrCopyN(path, size, xdg);
  }
  else
  {
    home = getenv("HOME");
    if (!home || !home[0])
      return 0;
    snprintf(path, size, "%s/%s", home, fallback_subdir);
  }

  return 1;
}

/**************************************************************************/

IUP_API void IupLogV(const char* type, const char* format, va_list arglist)
{
  int options = LOG_CONS | LOG_PID;
  int priority = 0;

  if (iupStrEqualNoCase(type, "DEBUG"))
  {
    priority = LOG_DEBUG;
#ifdef LOG_PERROR
    options |= LOG_PERROR;
#endif
  }
  else if (iupStrEqualNoCase(type, "ERROR"))
    priority = LOG_ERR;
  else if (iupStrEqualNoCase(type, "WARNING"))
    priority = LOG_WARNING;
  else if (iupStrEqualNoCase(type, "INFO"))
    priority = LOG_INFO;

  openlog(NULL, options, LOG_USER);

  vsyslog(priority, format, arglist);

  closelog();
}

IUP_API void IupLog(const char* type, const char* format, ...)
{
  va_list arglist;
  va_start(arglist, format);
  IupLogV(type, format, arglist);
  va_end(arglist);
}
