#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
        #ifndef __USE_FILE_OFFSET64
                #define __USE_FILE_OFFSET64
        #endif
        #ifndef __USE_LARGEFILE64
                #define __USE_LARGEFILE64
        #endif
        #ifndef _LARGEFILE64_SOURCE
                #define _LARGEFILE64_SOURCE
        #endif
        #ifndef _FILE_OFFSET_BIT
                #define _FILE_OFFSET_BIT 64
        #endif
#endif

#ifdef __APPLE__
// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
#define FTELLO_FUNC(stream) ftello(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
#else
#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
#define FTELLO_FUNC(stream) ftello64(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <utime.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "zip.h"


#define WRITEBUFFERSIZE (16384)
#define MAXFILENAME (256)

static uLong filetime(f, tmzip, dt)
    char *f;               /* name of file to get info on */
    tm_zip *tmzip;         /* return value: access, modific. and creation times */
    uLong *dt;             /* dostime */
{
  int ret=0;
  struct stat s;        /* results of stat() */
  struct tm* filedate;
  time_t tm_t=0;

  if (strcmp(f,"-")!=0)
  {
    char name[MAXFILENAME+1];
    int len = strlen(f);
    if (len > MAXFILENAME)
      len = MAXFILENAME;

    strncpy(name, f,MAXFILENAME-1);
    /* strncpy doesnt append the trailing NULL, of the string is too long. */
    name[ MAXFILENAME ] = '\0';

    if (name[len - 1] == '/')
      name[len - 1] = '\0';
    /* not all systems allow stat'ing a file with / appended */
    if (stat(name,&s)==0)
    {
      tm_t = s.st_mtime;
      ret = 1;
    }
  }
  filedate = localtime(&tm_t);

  tmzip->tm_sec  = filedate->tm_sec;
  tmzip->tm_min  = filedate->tm_min;
  tmzip->tm_hour = filedate->tm_hour;
  tmzip->tm_mday = filedate->tm_mday;
  tmzip->tm_mon  = filedate->tm_mon ;
  tmzip->tm_year = filedate->tm_year;

  return ret;
}

#if 0
static int check_exist_file(filename)
    const char* filename;
{
    FILE* ftestexist;
    int ret = 1;
    ftestexist = FOPEN_FUNC(filename,"rb");
    if (ftestexist==NULL)
        ret = 0;
    else
        fclose(ftestexist);
    return ret;
}
#endif

/* calculate the CRC32 of a file,
   because to encrypt a file, we need known the CRC32 of the file before */
static int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc)
{
   unsigned long calculate_crc=0;
   int err=ZIP_OK;
   FILE * fin = FOPEN_FUNC(filenameinzip,"rb");

   unsigned long size_read = 0;
   unsigned long total_read = 0;
   if (fin==NULL)
   {
       err = ZIP_ERRNO;
   }

    if (err == ZIP_OK)
        do
        {
            err = ZIP_OK;
            size_read = (int)fread(buf,1,size_buf,fin);
            if (size_read < size_buf)
                if (feof(fin)==0)
            {
                printf("error in reading %s\n",filenameinzip);
                err = ZIP_ERRNO;
            }

            if (size_read>0)
                calculate_crc = crc32(calculate_crc,buf,size_read);
            total_read += size_read;

        } while ((err == ZIP_OK) && (size_read>0));

    if (fin)
        fclose(fin);

    *result_crc=calculate_crc;
    // printf("file %s crc %lx\n", filenameinzip, calculate_crc);
    return err;
}

static int isLargeFile(const char* filename)
{
  int largeFile = 0;
  ZPOS64_T pos = 0;
  FILE* pFile = FOPEN_FUNC(filename, "rb");

  if(pFile != NULL)
  {
    FSEEKO_FUNC(pFile, 0, SEEK_END);
    pos = FTELLO_FUNC(pFile);

                // printf("File : %s is %lld bytes\n", filename, pos);

    if(pos >= 0xffffffff)
     largeFile = 1;

                fclose(pFile);
  }

 return largeFile;
}


typedef struct _myminiz_status {
    void *buf;
    int size_buf;
    zipFile zf;
} myminiz_t;

static myminiz_t g_myminiz;
void *create_zip_file(char *filename_try, int opt_overwrite)
{
   int dot_found = 0;
   int len=(int)strlen(filename_try);

    g_myminiz.size_buf = WRITEBUFFERSIZE;
    g_myminiz.buf = (void*)malloc(g_myminiz.size_buf);
    if (g_myminiz.buf == NULL) {
        printf("Error allocating memory\n");
        return NULL;
    }

   for (int i = 0; i < len; i++)
       if (filename_try[i] == '.')
           dot_found = 1;

   if (dot_found == 0)
       strcat(filename_try, ".zip");


   g_myminiz.zf = zipOpen64(filename_try, (opt_overwrite == 2) ? 2 : 0);
   if (g_myminiz.zf == NULL) {
       free(g_myminiz.buf);
       g_myminiz.buf = NULL;
       printf("error opening %s\n", filename_try);
   }
   else
       printf("creating %s\n", filename_try);

   return (void *)g_myminiz.zf;
}

int write_file_to_zipfile(char *filename, char *password)
{
    FILE *fin;
    int err = 0;
    int size_read;
    int opt_compress_level = Z_DEFAULT_COMPRESSION;
    const char *filenameinzip = filename;
    const char *savefilenameinzip;
    zip_fileinfo zi;
    unsigned long crcFile = 0;
    int zip64 = 0;

    zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
        zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
    zi.dosDate = 0;
    zi.internal_fa = 0;
    zi.external_fa = 0;
    filetime(filenameinzip, &zi.tmz_date, &zi.dosDate);

    if ((password != NULL) && (err == ZIP_OK))
        err = getFileCrc(filenameinzip, g_myminiz.buf, g_myminiz.size_buf, &crcFile);

    zip64 = isLargeFile(filenameinzip);

    /* The path name saved, should not include a leading slash. */
    /*if it did, windows/xp and dynazip couldn't read the zip file. */
    savefilenameinzip = filenameinzip;
    while (savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/')
    {
        savefilenameinzip++;
    }

    /**/
    err = zipOpenNewFileInZip3_64(g_myminiz.zf, savefilenameinzip, &zi,
                                  NULL, 0, NULL, 0, NULL /* comment*/,
                                  (opt_compress_level != 0) ? Z_DEFLATED : 0,
                                  opt_compress_level, 0,
                                  /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
                                  -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
                                  password, crcFile, zip64);

    if (err != ZIP_OK)
        printf("error in opening %s in zipfile\n", filenameinzip);
    else
    {
        fin = FOPEN_FUNC(filenameinzip, "rb");
        if (fin == NULL)
        {
            err = ZIP_ERRNO;
            printf("error in opening %s for reading\n", filenameinzip);
        }
    }

    if (err == ZIP_OK)
        do
        {
            err = ZIP_OK;
            size_read = (int)fread(g_myminiz.buf, 1, g_myminiz.size_buf, fin);
            if (size_read < g_myminiz.size_buf)
                if (feof(fin) == 0)
                {
                    printf("error in reading %s\n", filenameinzip);
                    err = ZIP_ERRNO;
                }

            if (size_read > 0)
            {
                err = zipWriteInFileInZip(g_myminiz.zf, g_myminiz.buf, size_read);
                if (err < 0)
                {
                    printf("error in writing %s in the zipfile\n",
                           filenameinzip);
                }
            }
        } while ((err == ZIP_OK) && (size_read > 0));

    if (fin)
        fclose(fin);

    if (err < 0)
        err = ZIP_ERRNO;
    else
    {
        err = zipCloseFileInZip(g_myminiz.zf);
        if (err != ZIP_OK)
            printf("error in closing %s in the zipfile\n", filenameinzip);
    }

    return err;
}

int close_zip_file(void)
{
    int errclose = zipClose(g_myminiz.zf, NULL);
    if (errclose != ZIP_OK)
        printf("error in closing .zip file\n");
    else {
        free(g_myminiz.buf);
        g_myminiz.buf = NULL;
    }

    return errclose;
}