/*
简单说明:
针对libzip库,封装了两个接口,compressString 压缩字符串 ,uncompressString 解压字符串
zhangtao /2016/06/13   使用者请联系406878851@qq.com
测试代码:
char inbuf[]="HELLOWORLD";
int  inLen=strlen(inbuf);
char outbuf[8192];
int outLen=0;    
compressString(inbuf,inLen,outbuf,8192,&outLen) ;   

char outbuf2[8192];
int outLen2=0;    
uncompressString(outbuf,outLen,outbuf2,8192,&outLen2);
printf("uncompressString %d %s\n",outLen2,outbuf2);
*/
static const char* ziparchive="data.zip";
static const char* archive="data";

static void saveZip(char* apOutBuf,int auOutBufSize)
{
    FILE* fp;
    if ((fp=fopen(ziparchive, "wb")) == NULL) {
        fprintf(stderr, "fopen failed: %s\n", strerror(errno));            
        return ;
    }
    printf("fwrite size:%d\n",auOutBufSize);
    if (fwrite(apOutBuf, auOutBufSize, 1, fp) < 1) {
        fprintf(stderr, "fwrite failed: %s\n", strerror(errno));            
        fclose(fp);
        return;
    }

    if (fclose(fp) != 0) {
        fprintf(stderr, "fclose failed: %s\n", strerror(errno));
        return;
    }
}

static int compressString(const char* apData,int auDataSize,char* apOutBuf,int auOutBufSize,int* apOutBufLen)
{    
    int ret=-1;
    *apOutBufLen=0;
    
    zip_t *za;
    zip_source_t *zs;
    zip_stat_t zst;    
    struct stat st;
    zip_source_t *src;    
    zip_error_t error;    
    int err;

    
    do 
    {
        src = zip_source_buffer_create(NULL,0, 0, &error);
        if (src == NULL) {
            err = zip_error_code_zip(&error);
            errno = zip_error_code_system(&error);
            fprintf(stderr, "zip_source_buffer_create faild: %d\n",err);
            break;
        }

        za = zip_open_from_source(src, 1, &error);
        if (za == NULL) {
            err = zip_error_code_zip(&error);
            errno = zip_error_code_system(&error);            
            fprintf(stderr, "zip_open_from_source faild: %d\n",err);
            break;
        }
        
        zip_source_keep(src);

        if ((zs=zip_source_buffer(za, apData, auDataSize, 0)) == NULL) {
            fprintf(stderr, "can't create zip_source from buffer: %s\n", zip_strerror(za));
            break;
        }

        if (zip_add(za, archive, zs) == -1) {            
            fprintf(stderr, "can't add file '%s': %s\n", archive, zip_strerror(za));
            break;
        }

        if (zip_close(za) == -1) {
            fprintf(stderr, "can't close zip archive '%s': %s\n", archive, zip_strerror(za));
            break;
        }

        za=NULL;

        if (zip_source_stat(src, &zst) < 0) {
            fprintf(stderr, "zip_source_stat on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
            break;
        }

        if (zst.size <=0){
            printf(" size error 000\n");
            break;
        }

        if (zst.size >= auOutBufSize){
            printf(" size error 111\n");
            break;
        }
                
        if (zip_source_open(src) < 0) {
            if (zip_error_code_zip(zip_source_error(src)) == ZIP_ER_DELETED) {
                if (unlink(archive) < 0 && errno != ENOENT) {
                    fprintf(stderr, "unlink failed: %s\n", strerror(errno));
                    break;
                }
                break;
            }
            fprintf(stderr, "zip_source_open on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
            break;
        }
        

        if (zip_source_read(src, apOutBuf, zst.size) < (zip_int64_t)zst.size) {
            fprintf(stderr, "zip_source_read on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
            zip_source_close(src);            
            break;
        }
                
        zip_source_close(src);    
        *apOutBufLen = (int)(zst.size);
        ret=0;    

        //saveZip(apOutBuf,*apOutBufLen );

    } while (0);

    if (NULL != src)
    {
        zip_source_free(src);
        src=NULL;
    }

    if (NULL != za)
    {
        zip_close(za);
        za=NULL;
    }


    return ret;
}


static int uncompressString(const char* apData,int auDataSize,char* apOutBuf,int auOutBufSize,int* apOutBufLen)
{
    int ret=-1;

    *apOutBufLen=0;
    zip_error_t error;    
    int err=0;
    char* buf=apOutBuf;    
    int   totalSize=0;
    zip_int64_t n = 0;    
    zip_source_t *src=NULL;
    zip_t *za=NULL;
    struct zip_file *f=NULL;


    do 
    {
        zip_error_init(&error);
        
        /* create source from buffer */
        if ((src = zip_source_buffer_create(apData, auDataSize, 1, &error)) == NULL) {
            fprintf(stderr, "can't create source: %s\n", zip_error_strerror(&error));        
            zip_error_fini(&error);
            break;
        }

        /* open zip archive from source */
        if ((za = zip_open_from_source(src, 0, &error)) == NULL) {
            fprintf(stderr, "can't open zip from source: %s\n", zip_error_strerror(&error));            
            zip_error_fini(&error);
            break;
        }


        zip_error_fini(&error);    
        zip_source_keep(src);

        zip_int64_t  c = zip_get_num_entries(za, ZIP_FL_UNCHANGED);
        if ( c != 1)
        {
            printf("zip_get_num_entries 0 \n");
            break;
        }

        const char * name = zip_get_name(za, 0, ZIP_FL_ENC_GUESS);
        if (NULL == name)
        {
            printf("zip_get_name 0 \n");
            break;
        }

        f = zip_fopen(za, name, 0);
        if (NULL == f)
        {
            printf("zip_fopen 0 \n");
            break;
        }
        
        if ( auOutBufSize < 4096)
        {
            printf("auOutBufSize < 4096 \n");
            break;
        }
        
        totalSize=0;
        while( totalSize < auOutBufSize)
        {
            buf = apOutBuf+ totalSize;    
            n = zip_fread(f, buf, 4096);
            if (n <=0 )
            {
                break;
            }

            totalSize += n;        
        }

        if (totalSize >= auOutBufSize)
        {
            printf("totalSize too big \n");
            break;
        }

        *apOutBufLen=totalSize;
        ret=0;

    } while (0);
    

    if (NULL != f)
    {
        zip_fclose(f);
        f=NULL;
    }

    if (NULL != za)
    {
        //lt-in-memory: free(): invalid pointer: 0x00007fff9c75c6d0 ***
        
//zip_close(za);
        za=NULL;
    }

    if (NULL != src)
    {
        zip_source_free(src);
        src=NULL;
    }

    return ret;
}