#include <carmen/carmen.h>
#include <carmen/carmen_stdio.h>

carmen_FILE *carmen_fopen(const char *filename, const char *mode)
{
  carmen_FILE *fp;
  
  /* allocate a new file pointer */
  fp = (carmen_FILE *)calloc(1, sizeof(carmen_FILE));
  carmen_test_alloc(fp);

  /* look at filename extension to determine if file is compressed */
#ifndef NO_ZLIB
  if(strcmp(filename + strlen(filename) - 3, ".gz") == 0)
    fp->compressed = 1;
  else
    fp->compressed = 0;
#else
  if(strcmp(filename + strlen(filename) - 3, ".gz") == 0)
    carmen_die("Error: cannot write compressed file.\n"
	       "CARMEN was not compiled with ZLIB support.\n");
  fp->compressed = 0;
#endif

  if(!fp->compressed) {
    fp->fp = fopen(filename, mode);
    if(fp->fp == NULL) {
      free(fp);
      return NULL;
    }
  }
#ifndef NO_ZLIB
  else {
    fp->fp = fopen(filename, mode);
    if(fp->fp == NULL) {
      free(fp);
      return NULL;
    }
    fp->comp_fp = gzdopen(fileno(fp->fp), mode);
    if(fp->comp_fp == NULL) {
      fclose(fp->fp);
      free(fp);
      return NULL;
    }
  }
#endif
  return fp;
}

int carmen_fgetc(carmen_FILE *fp)
{
#ifndef NO_ZLIB
  if(!fp->compressed)
    return fgetc(fp->fp);
  else
    return gzgetc(fp->comp_fp);
#else
  return fgetc(fp->fp);
#endif
}

int carmen_feof(carmen_FILE *fp)
{
#ifndef NO_ZLIB
  if(!fp->compressed)
    return feof(fp->fp);
  else
    return gzeof(fp->comp_fp);
#else
  return feof(fp->fp);
#endif
}

int carmen_fseek(carmen_FILE *fp, long offset, int whence)
{
#ifndef NO_ZLIB
  if(!fp->compressed)
    return fseek(fp->fp, offset, whence);
  else
    return gzseek(fp->comp_fp, offset, whence);
#else
  return fseek(fp->fp, offset, whence);
#endif
}

long carmen_ftell(carmen_FILE *fp)
{
#ifndef NO_ZLIB
  if(!fp->compressed)
    return ftell(fp->fp);
  else
    return gztell(fp->comp_fp);
#else
  return ftell(fp->fp);
#endif
}

int carmen_fclose(carmen_FILE *fp)
{
#ifndef NO_ZLIB
  if(!fp->compressed)
    return fclose(fp->fp);
  else
    return gzclose(fp->comp_fp);
#else
  return fclose(fp->fp);
#endif
}

size_t carmen_fread(void *ptr, size_t size, size_t nmemb, carmen_FILE *fp)
{
#ifndef NO_ZLIB
  if(!fp->compressed)
    return fread(ptr, size, nmemb, fp->fp);
  else
    return gzread(fp->comp_fp, ptr, size * nmemb) / size;
#else
  return fread(ptr, size, nmemb, fp->fp);
#endif
}

size_t carmen_fwrite(const void *ptr, size_t size, size_t nmemb, 
		     carmen_FILE *fp)
{
#ifndef NO_ZLIB
  if(!fp->compressed)
    return fwrite(ptr, size, nmemb, fp->fp);
  else
    return gzwrite(fp->comp_fp, (void *)ptr, size * nmemb) / size;
#else
  return fwrite(ptr, size, nmemb, fp->fp);
#endif
}

char *carmen_fgets(char *s, int size, carmen_FILE *fp)
{
#ifndef NO_ZLIB
  if(!fp->compressed)
    return fgets(s, size, fp->fp);
  else
    return gzgets(fp->comp_fp, s, size);
#else
  return fgets(s, size, fp->fp);
#endif
}

int carmen_fputc(int c, carmen_FILE *fp)
{
#ifndef NO_ZLIB
  if(!fp->compressed)
    return fputc(c, fp->fp);
  else
    return gzputc(fp->comp_fp, c);
#else
  return fputc(c, fp->fp);
#endif
}

void carmen_fprintf(carmen_FILE *fp, const char *fmt, ...)
{
  /* Guess we need no more than 100 bytes. */
  int n, size = 100;
  char *p;
  va_list ap;

  if((p = (char *)malloc(size)) == NULL)
    return;
  while(1) {
    /* Try to print in the allocated space. */
    va_start(ap, fmt);
    n = vsnprintf(p, size, fmt, ap);
    va_end(ap);
    /* If that worked, return the string. */
    if(n > -1 && n < size) {
      carmen_fwrite(p, strlen(p), 1, fp);
      free(p);
      return;
    }
    /* Else try again with more space. */
    if(n > -1)    /* glibc 2.1 */
      size = n + 1; /* precisely what is needed */
    else           /* glibc 2.0 */
      size *= 2;  /* twice the old size */
    if((p = (char *)realloc(p, size)) == NULL)
      return;
  }
}

