/*
 * Copyright (c) 2005-2008, 2010 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.
 */

/*
 * Author(s): James Bonfield, Simon Dear, Rodger Staden,
 * 
 * Copyright (c) 1994-1998, 2000-2001 MEDICAL RESEARCH COUNCIL
 * All rights reserved
 * 
 * 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 name of the MEDICAL RESEARCH COUNCIL, THE LABORATORY OF
 *      MOLECULAR BIOLOGY 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT OWNER 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.
 */

/*
 * Copyright (c) Medical Research Council 1994. All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * this copyright and notice appears in all copies.
 *
 * This file was written by James Bonfield, Simon Dear, Rodger Staden,
 * as part of the Staden Package at the MRC Laboratory of Molecular
 * Biology, Hills Road, Cambridge, CB2 2QH, United Kingdom.
 *
 * MRC disclaims all warranties with regard to this software.
 */

/* 
 * File: 	Read.c
 * Purpose:	Performs read/write IO on the Read data stucture.
 * Last update: 01/09/94
 */


/*
    The Read data type is designed so that it can hold a varying degree
    of information about sequences, yet have a single set of calls
    to access the data.

    There are plenty of assumptions around that both the number of
    bases and the number of points will fit into an int_2, a short.

*/

/* ---- Includes ---- */

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h> /* Only need on windows for _O_BINARY */
#include <unistd.h>

#ifdef _MSC_VER
#include <io.h>
#endif

#include "io_lib/Read.h"
#include "io_lib/mFILE.h"

#ifdef IOLIB_ABI
# include "io_lib/abi.h"
#endif
#ifdef IOLIB_SCF
# include "io_lib/scf.h"
#endif
#ifdef IOLIB_ALF
# include "io_lib/alf.h"
#endif
#ifdef IOLIB_PLN
# include "io_lib/plain.h"
#endif
#ifdef IOLIB_ZTR
# include "io_lib/ztr.h"
#endif
#ifdef IOLIB_SFF
# include "io_lib/sff.h"
#endif
#ifdef IOLIB_EXP
# include "io_lib/expFileIO.h"
#endif
#ifdef USE_BIOLIMS
# include "spBiolims.h"
#endif

#include "io_lib/xalloc.h"
#include "io_lib/translate.h"
#include "io_lib/traceType.h"
#include "io_lib/misc.h"
#include "io_lib/open_trace_file.h"

/*
 * Read a sequence from a file "fnin" of format "format". If "format" is 0
 * (ANY_FORMAT), we automatically determine the correct format.
 * Returns:
 *   Read *   for success
 *   NULLRead for failure
 */
Read *read_reading(char *fn, int format) {
    Read *read;
    mFILE *fp;

#ifdef USE_BIOLIMS
    if( !strncmp(fn,BIOLIMS_TAG,strlen(BIOLIMS_TAG))){
	return spReadBiolimsReading(fn);
   }
#endif

    /*
     * If we're asking for an Experiment file, read it.
     * If the format is ANY then attempt EXP first following by trace.
     * Otherwise use the trace search mechanism.
     *
     * Note this is purely for locating files and not for forcing the file
     * format. It's here so that experiment files and trace files may be
     * given identical names but accessed through different search paths
     * (as is the case with the trace server).
     */
    if (format == TT_EXP) {
	if (NULL == (fp = open_exp_mfile(fn, NULL))) {
	    errout("'%s': couldn't open\n", fn);
	    return NULL;
	}
    } else {
	fp = NULL;
	if (format == TT_ANY)
	    fp = open_exp_mfile(fn, NULL);

	if (!fp && NULL == (fp = open_trace_mfile(fn, NULL))) {
	    errout("'%s': couldn't open\n", fn);
	    return NULL;
	}
    }

    read = mfread_reading(fp, fn, format);
    mfclose(fp);

    return read;
}

/*
 * Read a sequence from a FILE *fp of format "format". If "format" is 0
 * (ANY_FORMAT), we automatically determine the correct format.
 * We still pass a filename 'fn' although this isn't used other than for
 * filling in the read->trace_name field.
 *
 * NB this function should NOT be used when Biolims support is required
 * (as biolims readings are not stored in a file)
 *
 * Returns:
 *   Read *   for success
 *   NULLRead for failure
 */
Read *mfread_reading(mFILE *fp, char *fn, int format) {
    Read *read;
    mFILE *newfp;

    if (!fn)
	fn = "(unknown)";

    newfp = freopen_compressed(fp, NULL);
    if (newfp != fp) {
	fp = newfp;
    } else {
	newfp = NULL;
    }

#ifdef _WIN32
    /*
     * jkb 16/05/00 comment below
     *
     * On windows "prog < file.abi" will work wrongly (compared to
     * "prog file.abi") because windows is rather stupid. It treats ascii
     * and binary streams differently, it considers stdin to be ascii unless
     * told otherwise, and it can only be told otherwise by using non-ansi
     * windows-specific function calls.
     */
    if (format != TT_EXP && format != TT_PLN && fp->fp)
	_setmode(_fileno(fp->fp), _O_BINARY);
#endif

    if (format == TT_ANY || format == TT_ANYTR) {
	format = fdetermine_trace_type(fp);
	mrewind(fp);
    }

    switch (format) {
    case TT_UNK:
    case TT_ERR:
	errout("File '%s' has unknown trace type\n", fn);
	read = NULLRead;
	break;

#ifdef IOLIB_SCF
    case TT_SCF: {
        Scf *scf;
	scf = mfread_scf(fp);

	if (scf) {
	    read = scf2read(scf);
	    scf_deallocate(scf);
	} else
	    read = NULLRead;

	break;
    }
#endif

#ifdef IOLIB_SFF
    case TT_SFF:
	read = mfread_sff(fp);
	break;
#endif

#ifdef IOLIB_ZTR
    case TT_ZTR:
    case TT_ZTR1:
    case TT_ZTR2:
    case TT_ZTR3: {
        ztr_t *ztr;

	if ((ztr = mfread_ztr(fp))) {
	    uncompress_ztr(ztr);
	    read = ztr2read(ztr);
	    delete_ztr(ztr);
	} else {
	    read = NULLRead;
	}
	break;
    }
#endif

#ifdef IOLIB_ABI
    case TT_ABI:
	read = mfread_abi(fp);
	break;
#endif

#ifdef IOLIB_ALF
    case TT_ALF:
	read = mfread_alf(fp);
	break;
#endif

#ifdef IOLIB_EXP
    case TT_EXP: {
	/* FIXME: we shouldn't redirect like this */
	Exp_info *e = exp_mfread_info(fp);
	
	read = e ? exp2read(e,fn) : NULLRead;
	break;
    }
#endif

#ifdef IOLIB_PLN
    case TT_PLN:
	read = mfread_pln(fp);
	break;
#endif

    default:
	errout("Unknown format %d specified to read_reading()\n", format);
	read = NULLRead;
    }

    if (read != NULLRead && (read->trace_name = (char *)xmalloc(strlen(fn)+1)))
	strcpy(read->trace_name, fn);

    if (newfp) mfclose(newfp);

    return read;
}

Read *fread_reading(FILE *fp, char *fn, int format) {
    return mfread_reading(mfreopen(fn, "rb", fp), fn, format);
}

/*
 * Write a sequence to a FILE *fp of format "format". If "format" is 0,
 * we choose our favourite - SCF.
 *
 * Returns:
 *   0 for success
 *  -1 for failure
 */
int mfwrite_reading(mFILE *fp, Read *read, int format) {
    int r = -1;
    int no_compress = 0;

#ifdef _WIN32
    /*
     * jkb 09/06/00 comment below
     *
     * On windows "prog > file.scf" will work wrongly (compared to
     * "prog file.scf") because windows is rather stupid. It treats ascii
     * and binary streams differently, it considers stdout to be ascii unless
     * told otherwise, and it can only be told otherwise by using non-ansi
     * windows-specific function calls.
     */
    if (format != TT_EXP && format != TT_PLN && fp->fp)
	_setmode(_fileno(fp->fp), _O_BINARY);
#endif

    switch (format) {
    default:
	/* Defaults to ZTR type */

#ifdef IOLIB_ZTR
    case TT_ZTR:
    case TT_ZTR2: {
        ztr_t *ztr;
	ztr = read2ztr(read);
	compress_ztr(ztr, 2);
	r = mfwrite_ztr(fp, ztr); 
	delete_ztr(ztr);
	no_compress = 1;
	break;
    }
    case TT_ZTR1: {
        ztr_t *ztr;
	ztr = read2ztr(read);
	compress_ztr(ztr, 1);
	r = mfwrite_ztr(fp, ztr); 
	delete_ztr(ztr);
	break;
    }
    case TT_ZTR3: {
        ztr_t *ztr;
	ztr = read2ztr(read);
	compress_ztr(ztr, 3);
	r = mfwrite_ztr(fp, ztr); 
	delete_ztr(ztr);
	no_compress = 1;
	break;
    }
#endif

#ifdef IOLIB_SCF
    case TT_SCF: {
        Scf *scf;
	scf = read2scf(read);
	r = mfwrite_scf(scf, fp);
	scf_deallocate(scf);
	break;
    }
#endif

#ifdef IOLIB_ABI
    case TT_ABI:
	/*return mfwrite_abi(fp, read); */
	break;
#endif

#ifdef IOLIB_SFF
    case TT_SFF:
	/*return mfwrite_sff(fp, read); */
	break;
#endif

#ifdef IOLIB_ALF
    case TT_ALF:
	/* return mfwrite_alf(fp, read); */
	break;
#endif

#ifdef IOLIB_EXP
    case TT_EXP: {
	Exp_info *e = read2exp(read, read->ident ? read->ident : "unknown");
	
	if (NULL == e) {
	    fprintf(stderr, "Failed to create experiment file.\n");
	    r = -1;
	} else {
	    exp_print_mfile(fp, e);
	    exp_destroy_info(e);
	    r = 0;
	}
	break;
    }
#endif

#ifdef IOLIB_PLN
    case TT_PLN:
	r = mfwrite_pln(fp, read);
	break;
#endif
    }

    mftruncate(fp, -1);
    if (r == 0 && !no_compress) {
	fcompress_file(fp);
    }
    mfflush(fp);

    return r;
}

int fwrite_reading(FILE *fp, Read *read, int format) {
    int ret;
    mFILE *mf = mfreopen(NULL, "wbx", fp);
    if (mf) {
	ret = mfwrite_reading(mf, read, format);
	mfflush(mf);
	mf->fp = NULL; /* Don't want this closed here */
	mfclose(mf);
    } else {
	return -1;
    }

    return ret;
}

/*
 * Write a sequence to a file "fn" of format "format". If "format" is 0,
 * we choose our favourite - SCF.
 *
 * Returns:
 *   0 for success
 *  -1 for failure
 */
int write_reading(char *fn, Read *read, int format) {
    int ret;
    mFILE *fp = mfopen(fn, "wb");
    if (!fp)
	return -1;
    
    ret = mfwrite_reading(fp, read, format);
    mfclose(fp);
    return ret;
}

/*
 * Old style stub interfaces implemented simply as redirection through
 * fread_reading and frwrite_reading.
 */
#ifdef IOLIB_ABI
Read *fread_abi(FILE *fp) {
    return fread_reading(fp, NULL, TT_ABI);
}

int fwrite_abi(FILE *fp, Read *read) {
    return fwrite_reading(fp, read, TT_ABI);
}
#endif

#ifdef IOLIB_ALF
Read *fread_alf(FILE *fp) {
    return fread_reading(fp, NULL, TT_ALF);
}

int fwrite_alf(FILE *fp, Read *read) {
    return fwrite_reading(fp, read, TT_ALF);
}
#endif

#ifdef IOLIB_PLN
Read *fread_pln(FILE *fp) {
    return fread_reading(fp, NULL, TT_PLN);
}

int fwrite_pln(FILE *fp, Read *read) {
    return fwrite_reading(fp, read, TT_PLN);
}
#endif

#ifdef IOLIB_ZTR
ztr_t *fread_ztr(FILE *fp) {
    ztr_t *z;
    mFILE *mf;

    if (NULL == (mf = mfreopen(NULL, "rb", fp)))
	return NULL;

    z = mfread_ztr(mf);
    mfclose(mf);
    return z;
}

int fwrite_ztr(FILE *fp, ztr_t *z) {
    mFILE *mf;
    int r;

    if (NULL == (mf = mfreopen(NULL, "wbx", fp)))
	return -1;

    r = mfwrite_ztr(mf, z);
    mfflush(mf);
    mf->fp = NULL; /* Don't want this closed here */
    mfclose(mf);
    return r;
}
#endif

#ifdef IOLIB_SCF
Scf *fread_scf(FILE *fp) {
    Scf *s;
    mFILE *mf;

    if (NULL == (mf = mfreopen(NULL, "rb", fp)))
	return NULL;

    s = mfread_scf(mf);
    mf->fp = NULL; /* Don't want this closed here */
    mfclose(mf);
    return s;
}

int fwrite_scf(Scf *s, FILE *fp) {
    mFILE *mf;
    int r;

    if (NULL == (mf = mfreopen(NULL, "wbx", fp)))
	return -1;

    r = mfwrite_scf(s, mf);
    mfflush(mf);
    mf->fp = NULL; /* Don't want this closed here */
    mfclose(mf);
    return r;
}
#endif

#ifdef IOLIB_EXP
Exp_info *exp_fread_info(FILE *fp) {
    Exp_info *e;
    mFILE *mf;

    if (NULL == (mf = mfreopen(NULL, "rb", fp)))
	return NULL;

    e = exp_mfread_info(mf);
    mf->fp = NULL; /* Don't want this closed here */
    mfclose(mf);
    return e;
}

void exp_print_file(FILE *fp, Exp_info *e) {
    mFILE *mf;

    if (NULL == (mf = mfreopen(NULL, "wbx", fp)))
	return;

    exp_print_mfile(mf, e);
    mfflush(mf);
    mf->fp = NULL; /* Don't want this closed here */
    mfclose(mf);
}
#endif
