/*!\file gfstdlib.c
 *
 * \brief Fonctions et fonctionnalités standards. (ASTDLIB (amsi)
 * renommée GFSTDLIB (grafeet) le 29/04/13)
 *
 * \author Farès BELHADJ amsi@grafeet.fr (ou amsi@ai.univ-paris8.fr)
 * \date April 04, 2008
 */
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#if defined(_WIN32)
#  include <windows.h>
static int gettimeofday(struct timeval* p, void* tz) {
    ULARGE_INTEGER ul; // As specified on MSDN.
    FILETIME ft;
    // Returns a 64-bit value representing the number of
    // 100-nanosecond intervals since January 1, 1601 (UTC).
    GetSystemTimeAsFileTime(&ft);
    // Fill ULARGE_INTEGER low and high parts.
    ul.LowPart = ft.dwLowDateTime;
    ul.HighPart = ft.dwHighDateTime;
    // Convert to microseconds.
    ul.QuadPart /= 10ULL;
    // Remove Windows to UNIX Epoch delta.
    ul.QuadPart -= 11644473600000000ULL;
    // Modulo to retrieve the microseconds.
    p->tv_usec = (long) (ul.QuadPart % 1000000LL);
    // Divide to retrieve the seconds.
    p->tv_sec = (long) (ul.QuadPart / 1000000LL);
    return 0;
}
#else
#include <sys/time.h>
#endif

#include "gfstdlib.h"

/*!\brief Calcule la partie entiere du logarithme base 2 de l'entier
 * passe en argument.
 *
 * \param v l'entier dont la fonction calcule le logarithme.
 *
 * \return la partie entiere du logarithme base 2 de \a v.
*/
int log2i(long int v) {
  int i;
  for(i = ((sizeof v)<<3) - 1; i > 0; i--)
    if((1<<i) & v)
      break;
  return i;
}

/*!\brief Retourne un nombre pseudo-aleatoire dans l'intervalle [0,
 * 1[. Ici la distribution est uniforme.
 *
 * \return un nombre pseudo-aleatoire dans l'intervalle [0, 1[.
*/
double u_rand(void) {
  return (rand() / (RAND_MAX + 1.0));
}

/*!\brief Retourne un nombre pseudo-aleatoire dans l'intervalle [-1,
 * 1[. Ici la distribution est uniforme.
 *
 * \return un nombre pseudo-aleatoire dans l'intervalle [-1, 1[.
*/
double su_rand(void) {
  return 2.0 * (rand() / (RAND_MAX + 1.0)) - 1.0;
}

/*!\brief Retourne un nombre pseudo-aleatoire dans l'intervalle [0,
 * 1[. Ici la distribution est Gaussienne.
 *
 * Tire du knuth : le rand Gaussien a deux valeurs par passe deux rand
 * : X1 et X2.
 *
 * \return un nombre pseudo-aleatoire dans l'intervalle [0, 1[.
*/
double g_rand(void) {
  static int haveX2 = 0;
  static double X2 = 0.0;
  if (haveX2) {
    haveX2 = 0;
    return X2;
  } else {
    double V1, V2, S, precomp;
    do { 
      V1 = 2.0 * (rand() / (double)RAND_MAX) - 1.0;
      V2 = 2.0 * (rand() / (double)RAND_MAX) - 1.0;
      S = V1 * V1 + V2 * V2;
    } while (S >= 1.0);
    if(S == 0.0) return (X2 = !((haveX2 = 1)));
    precomp = sqrt(-2.0 * log(S) / S);
    X2 = V2 * precomp;
    haveX2 = 1;
    return V1 * precomp; /* X1 */
  }
}

/*!\brief Le temps initial du programme. A initialiser avec \ref
 * initTime0.
 *
 * \see initTime0
 */
static struct timeval t0;

static double getElapsedTime_sub1(void);
static double getElapsedTime_sub2(void);

static double (* getET)(void) = getElapsedTime_sub1;

static double getElapsedTime_sub1(void) {
	initTime0();
	getET = getElapsedTime_sub2;
	return 0.0;
}

static double getElapsedTime_sub2(void) {
  struct timeval t;
  double diff;
  gettimeofday(&t, (struct timezone*) 0);
  diff = (t.tv_sec - t0.tv_sec) * 1000000
    + (t.tv_usec - t0.tv_usec);
  return diff/1000.;
}

/*!\brief Initialise \a t0.
 */
void initTime0(void) {
  gettimeofday(&t0, (struct timezone*) 0);
}

/*!\brief Donne le temps ecoule en millisecondes depuis \a t0.
 *
 * \see initTime0
 *
 * \return le temps ecoule en millisecondes.
 */
double getElapsedTime(void) {
	return getET();
}

/*!\brief Une variable de temps. A initialiser avec \ref
 * initTime.
 *
 * \see initTime
 */
static struct timeval ti;

/*!\brief Initialise \a ti.
 */
void initTime(void) {
  gettimeofday(&ti, (struct timezone*) 0);
}

/*!\brief Donne le temps ecoule en millisecondes depuis \a ti.
 *
 * \see initTime
 *
 * \return le temps ecoule en millisecondes.
 */
double getTime(void) {
  struct timeval t;
  double diff;
  gettimeofday(&t, (struct timezone*) 0);
  diff = (t.tv_sec - ti.tv_sec) * 1000000
    + (t.tv_usec - ti.tv_usec);
  return diff/1000.;
}

/*!\brief Calcule le FPS - Frames Per Second.
 *
 * Mettre un appel a cette fonction juste apres (ou avant) chaque
 * affichage.
 *
 * \see initTime0
 * \see getElapsedTime
 *
 * \return le nombre d'images par seconde.
 */
double getFps(void) {
  static int f = 0;
  static double t0 = 0, t1 = 0, fps = 0.0;
  t1 = getTime(); f++;
  if(t1 - t0 > 1000) {
    fps = 1000.0 * f / (t1 - t0);
    t0 = t1; f = 0;
  }
  return fps;
}

/*!\brief Déplace un fichier.
 *
 * \param src le chemin vers le fichier à dépacer
 *
 * \param dst le nouveau chemin/nom
 *
 * \return 1 en cas de succès sinon 0.
 *
 * \todo gérer les dossiers, les unlink (liés à l'OS) ...
 */
int mv(const char * src, const char * dst) {
  size_t l;
  char buf[BUFSIZ] = {0};
  FILE * in = fopen(src, "rb"), * out = fopen(dst, "wb") ;
  if(in == NULL || out == NULL)
    return 0;
  while((l = fread(buf, 1, sizeof buf, in)) > 0)
    fwrite(buf, 1, l, out);
  fclose(out); fclose(in);
  if(remove(src))
    return 1;
  return 0;
}

/*!\brief Renvoie le chemin vers le dossier contenant le fichier passé en argument.
 *
 * Fabrique la chaîne allant jusqu'à la dernière occurence du
 * '/'. Cette chaîne doit être libérée avec free après usage.
 *
 * \param path le chemin vers le fichier/dossier
 * 
 * \return le chemin vers le dossier contenant le fichier passé en
 * argument. La chaîne renvoyée doit être libérée avec free après
 * usage.
 *
 */
char * pathOf(const char * path) {
  int spos = -1;
  char * tmp, * ptr;
  tmp = malloc((strlen(path) + 1) * sizeof * tmp); assert(tmp); strcpy(tmp, path); //strdup(path);
  ptr = tmp;
  while(*ptr) {
    if(*ptr == '/' || *ptr == '\\')
      spos = ptr - tmp;
    ++ptr;
  }
  tmp[spos >= 0 ? spos : 0] = 0;
  return tmp;
}

/*!\brief Renvoie le nom de fichier sans le chemin.
 *
 * Prend le nom du fichier à partir de la dernière occurence du
 * '/'. Cette chaîne doit être libérée avec free après usage.
 *
 * \param path le chemin vers le fichier/dossier
 * 
 * \return le nom de fichier sans le chemin. La chaîne renvoyée doit
 * être libérée avec free après usage.
 *
 */
char * filenameOf(const char * path) {
  int spos = -1, l;
  const char * ptr = path;
  char * tmp;
  while(*ptr) {
    if(*ptr == '/' || *ptr == '\\')
      spos = ptr - path;
    ++ptr;
  }
  tmp = malloc((l = strlen(&path[++spos]) + 1) * sizeof *tmp);
  assert(tmp);
  strncpy(tmp, &path[spos], l - 1); tmp[l - 1] = 0;
  return tmp;
}
