/*
 * Copyright (c) 2013 Genome Research Ltd.
 * Author(s): James Bonfield
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 * 
 *    2. Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 * 
 *    3. Neither the names Genome Research Ltd and Wellcome Trust Sanger
 *    Institute nor the names of its contributors may be used to endorse
 *    or promote products derived from this software without specific
 *    prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY GENOME RESEARCH LTD AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENOME RESEARCH
 * LTD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "io_lib_config.h"
#endif

#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include "io_lib/os.h"
#include "io_lib/zfio.h"

/* ------------------------------------------------------------------------ */
/* Some wrappers around FILE * vs gzFile *, allowing for either */

/*
 * gzopen() works on both compressed and uncompressed data, but it has
 * a significant performance hit even for uncompressed data (tested as
 * 25s using FILE* to 46s via gzOpen and 66s via gzOpen when gzipped).
 *
 * Hence we use our own wrapper 'zfp' which is a FILE* when uncompressed
 * and gzFile* when compressed. This also means we could hide bzopen in
 * there too if desired.
 */

off_t zftello(zfp *zf) {
    return zf->fp ? ftello(zf->fp) : -1;
}

int zfseeko(zfp *zf, off_t offset, int whence) {
    return zf->fp ? fseeko(zf->fp, offset, whence) : -1;
}


/*
 * A wrapper for either fgets or gzgets depending on what has been
 * opened.
 */
char *zfgets(char *line, int size, zfp *zf) {
    if (zf->fp)
	return fgets(line, size, zf->fp);
    else
	return gzgets(zf->gz, line, size);
}

/*
 * A wrapper for either fputs or gzputs depending on what has been
 * opened.
 */
int zfputs(char *line, zfp *zf) {
    if (zf->fp)
	return fputs(line, zf->fp);
    else
	return gzputs(zf->gz, line) ? 0 : EOF;
}

/*
 * Peeks at and returns the next character without consuming it from the
 * input. (Ie a combination of getc and ungetc).
 */
int zfpeek(zfp *zf) {
    int c;

    if (zf->fp) {
	c = getc(zf->fp);
	if (c != EOF)
	    ungetc(c, zf->fp);
    } else {
	c = gzgetc(zf->gz);
	if (c != EOF)
	    gzungetc(c, zf->gz);
    }

    return c;
}

/* A replacement for either feof of gzeof */
int zfeof(zfp *zf) {
    return zf->fp ? feof(zf->fp) : gzeof(zf->gz);
}

/* A replacement for either fopen or gzopen */
zfp *zfopen(const char *path, const char *mode) {
    char path2[1024];
    zfp *zf;

    if (!(zf = (zfp *)malloc(sizeof(*zf))))
	return NULL;
    zf->fp = NULL;
    zf->gz = NULL;

    /* Try normal fopen */
    if (mode[0] != 'z' && mode[1] != 'z' &&
	NULL != (zf->fp = fopen(path, mode))) {
	unsigned char magic[2];
	if (2 != fread(magic, 1, 2, zf->fp)) {
	    free(zf);
	    return NULL;
	}
	if (!(magic[0] == 0x1f &&
	      magic[1] == 0x8b)) {
	    fseeko(zf->fp, 0, SEEK_SET);
	    return zf;
	}

	fclose(zf->fp);
	zf->fp = NULL;
    }

    if ((zf->gz = gzopen(path, mode)))
	return zf;

    if (!strchr(mode, 'w')) {
	sprintf(path2, "%.*s.gz", 1020, path);
	if ((zf->gz = gzopen(path2, mode)))
	    return zf;
    }

    free(zf);
    return NULL;
}

int zfclose(zfp *zf) {
    int r = (zf->fp) ? fclose(zf->fp) : gzclose(zf->gz);
    free(zf);
    return r;
}
