/**
 * @file lfile.c
 *
 * @brief Handle read/write abstractions to the users directory.
 *
 * @todo Add support for windows and mac OS.
 */

#include <string.h>
#include <stdarg.h>
#ifdef LINUX
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
#endif

#include "lephisto.h"
#include "log.h"
#include "lfile.h"

/**
 * @fn char* lfile_basePath(void)
 *
 * @brief Get Lephisto's base path (for saves etc..).
 *    @return The base path to Lephisto.
 */
static char lephisto_base[128] = "\0"; /**< Store Lephisto's base path. */
char* lfile_basePath(void) {
  char* home;

  if(lephisto_base[0] == '\0') {
#ifdef LINUX
    home = getenv("HOME");
#else
#error "Needs implentation."
#endif
    snprintf(lephisto_base, PATH_MAX, "%s/.lephisto/", home);
  }

  return lephisto_base;
}

/**
 * @fn int lfile_dirMakeExist(const char* path)
 *
 * @brief Create a directory if it doesn't exist.
 *
 * Uses relative path to basePath.
 *    @param path Path to create directory if it doesn't exist.
 *    @return 0 on success.
 */
int lfile_dirMakeExist(const char* path) {
  char file[PATH_MAX];

#ifdef LINUX
  struct stat buf;

  if(strcmp(path, ".")==0) {
    strncpy(file, lfile_basePath(), PATH_MAX);
    file[PATH_MAX-1] = '\0';
  }
  else
    snprintf(file, PATH_MAX, "%s%s", lfile_basePath(), path);
  stat(file, &buf);
  if(!S_ISDIR(buf.st_mode))
    if(mkdir(file, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
      WARN("Dir '%s' does not exist and unable to create", path);
      return -1;
    }
#else
#error "Needs implentation."
#endif

  return 0;
}

/**
 * @fn int lfile_fileExists(const char* path, ...)
 *
 * @brief Check to see if a file exists.
 *    @param path printf formatted string pointing to the file to check for existance.
 *    @return 1 if file exists, 0 if it doesn't or -1 on error.
 */
int lfile_fileExists(const char* path, ...) {
  char file[PATH_MAX], name[PATH_MAX];
  va_list ap;
  size_t l;

  l = 0;
  if(path == NULL) return -1;
  else { /* Get the message. */
    va_start(ap, path);
    vsnprintf(name, PATH_MAX-l, path, ap);
    l = strlen(name);
    va_end(ap);
  }

  snprintf(file, PATH_MAX, "%s%s", lfile_basePath(), name);
#ifdef LINUX
  struct stat buf;

  if(stat(file, &buf)==0) /* Stat worked, file must exist. */
    return 1;
#else
#error "Needs implementation."
#endif
  return 0;
}

/**
 * @fn char** lfile_readDir(int* lfiles, const char* path)
 *
 * @brief Lists all the visible files in a directory.
 *
 * Should also sort by last modified but thats up to the OS in question.
 * Paths are relative to base directory.
 *    @param[out] lfiles Returns how many files there are.
 *    @param path Directory to read.
 */
char** lfile_readDir(int* lfiles, const char* path) {
  char file[PATH_MAX];
  char** files;

  snprintf(file, PATH_MAX, "%s%s", lfile_basePath(), path);

#ifdef LINUX
  int i, j, k, n;
  DIR* d;
  struct dirent *dir;
  char* name;
  int mfiles;
  struct stat sb;
  time_t* tt, *ft;
  char** tfiles;

  (*lfiles) = 0;
  mfiles = 100;
  tfiles = malloc(sizeof(char*)*mfiles);
  tt = malloc(sizeof(time_t)*mfiles);

  d = opendir(file);
  if(d == NULL)
    return NULL;

  /* Get the file list. */
  while((dir = readdir(d)) != NULL) {
    name = dir->d_name;

    /* Skip hidden directories. */
    if(name[0] == '.')
      continue;

    /* Stat the file. */
    snprintf(file, PATH_MAX, "%s%s/%s", lfile_basePath(), path, name);
    if(stat(file, &sb) == -1)
      continue; /* Unable to stat. */

    /* Enough memory? */
    if((*lfiles)+1 > mfiles) {
      mfiles += 100;
      tfiles = realloc(files, sizeof(char*) * mfiles);
      tt = realloc(tt, sizeof(time_t) * mfiles);
    }

    /* Write the information. */
    tfiles[(*lfiles)] = strdup(name);
    tt[(*lfiles)] = sb.st_mtime;
    (*lfiles)++;
  }

  closedir(d);

  /* Sort by last changed date. */
  if((*lfiles) > 0) {
    /* Need to allocate some stuff. */
    files = malloc(sizeof(char*)*(*lfiles));
    ft = malloc(sizeof(time_t)*(*lfiles));

    /* Fill the list. */
    for(i = 0; i < (*lfiles); i++) {
      n = -1;

      /* Get the next lowest. */
      for(j = 0; j < (*lfiles); j++) {
        /* Is lower? */
        if((n == -1) || (tt[j] > tt[n])) {
          /* Check if it's already there. */
          for(k = 0; k < i; k++)
            if(strcmp(files[k], tfiles[j])==0)
              break;

          /* New lowest. */
          if(k >= i)
            n = j;
        }
      }
      files[i] = tfiles[n];
      ft[i] = tt[n];
    }
    free(ft);
  } else
    files = NULL;

  /* Free temp stuff. */
  free(tfiles);
  free(tt);

#else
#error "Needs implementation."
#endif

  /* What if we find nothing? */
  if((*lfiles) == 0) {
    free(files);
    files = NULL;
  }

  return files;
}

