/** \file
 *
 *  Contains the ZipEncoder class implementation
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 *
 */

#include "ZipEncoder.h"

#include <cstdio>
#include <ctime>
#include <unistd.h>
#include <utime.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "utils/Str.h"
#include "../../Lib/zlib/contrib/minizip/zip.h"

ZipEncoder::ZipEncoder( const char* defaultExtension )
    : defaultExtension_( defaultExtension )
{
    *writeBuffer_ = 0;
}

ZipEncoder::~ZipEncoder()
{}

#define COMPRESSLEVEL (1)
#define MAXFILENAME (256)

/// char *f;               /* name of file to get info on */
/// tm_zip *tmzip;         /* return value: access, modific. and creation times */
/// uLong *dt;             /* dostime */
void zip_encoder_filetime( const char *f, tm_zip *tmzip, uLong *dt )
{
    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;
        }
    }
    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;

}

bool ZipEncoder::encode( const char* inFilename, const char* outFilename, const char* renameFilename )
{
    size_t size_buf = WRITEBUFFERSIZE;

    Str outFilenameStr( outFilename );
    if( outFilename == NULL )
    {
        Str inFilenameStr( inFilename );
        outFilenameStr = Str( inFilename, inFilenameStr.findLastOf( '.' ) )
                         + ( NULL != defaultExtension_ ? defaultExtension_ : ".zip" );
    }

    zipFile zf( zipOpen( outFilenameStr.cStr(), 0 ) );

    if( zf == NULL )
    {
        printf( "error opening %s\n", outFilenameStr.cStr() );
        return false;
    }

    FILE * fin( NULL );
    size_t size_read;
    zip_fileinfo zi;
    unsigned long crcFile = 0;

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

    int err = zipOpenNewFileInZip3( zf, inFilename, &zi,
                                    NULL, 0, NULL, 0, NULL /* comment*/,
                                    Z_DEFLATED,
                                    COMPRESSLEVEL, 0,
                                    -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
                                    NULL, crcFile );

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

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

            if( size_read > 0 )
            {
                err = zipWriteInFileInZip( zf, writeBuffer_, size_read );
                if( err < 0 )
                {
                    printf( "error in writing %s in the zipfile\n",
                            inFilename );
                }

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

    if( fin )
        fclose( fin );

    if( err >= 0 )
    {
        err = zipCloseFileInZip( zf );
        if( err != ZIP_OK )
            printf( "error in closing %s in the zipfile\n",
                    inFilename );
    }

    int errclose = zipClose( zf, NULL );

    if( errclose != ZIP_OK )
    {
        printf( "error in closing %s\n", outFilenameStr.cStr() );
    }

    if( err == ZIP_OK && errclose == ZIP_OK )
    {
        if( renameFilename != NULL )
        {
            rename( inFilename, renameFilename );
        }
    }
    else
    {
        remove( outFilenameStr.cStr() );
    }

    return err == ZIP_OK && errclose == ZIP_OK ;
}

