/*
 * Copyright (c) 2013 Genome Research Ltd.
 * Author(s): James Bonfield, Rob Davies
 * 
 * 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: James Bonfield, Wellcome Trust Sanger Institute. 2010-3
 */

/*! \file
 * The primary SAM/BAM API.
 *
 * Consider using scram.h if you wish to also have support for CRAM.
 */ 

#ifndef _BAM_H_
#define _BAM_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <inttypes.h>
#include <stddef.h>
#include <zlib.h>

#include "io_lib/os.h"
#include "io_lib/hash_table.h"
#include "io_lib/sam_header.h"
#include "io_lib/thread_pool.h"
#include "io_lib/binning.h"
#include "io_lib/bgzip.h"

/* BAM header structs */
typedef struct tag_list {
    char *value; /* NULL => end of tags */
    int key;
    int length;  
} tag_list_t;

/* The main bam sequence struct */
typedef struct bam_seq_s {
    uint32_t alloc; /* total size of this struct + 'data' onwards */
    uint32_t blk_size;

    // 64-bit variant of our position, for general use
    int64_t pos;
    int64_t mate_pos;
    int64_t ins_size;

    /* The raw bam block follows, in same order as on the disk */
    /* This is the analogue of a bam1_core_t in samtools */
    int32_t  ref;
    int32_t  pos_32;

    union {
	struct {
	    uint32_t name_len:8, map_qual:8, bin:16;
	};
	uint32_t bin_packed;
    };
    union {
	struct {
	    uint32_t cigar_len:16, flag:16;
	};
	uint32_t flag_packed;
    };

    int32_t  len;
    int32_t  mate_ref;
    int32_t  mate_pos_32;
    int32_t  ins_size_32;

    /* Followed by arbitrary bytes of packed data */
    unsigned char data; /* unknown size */
} bam_seq_t;


/* Auxillary field handling */
typedef union {
    char    *s;
    int      i;
    uint64_t i64;
    float    f;
    double   d;
    struct {
	int n, t;
	unsigned char *s;
    } B;
} bam_aux_t;

/* Struct for making arrays of aux tags */

typedef struct {
    char     tag[2];
    char     type;
    uint32_t array_len;
    union {
	char    *z;
	uint8_t *h;
	char     a;
	int32_t  i;
	uint32_t ui;
	float    f;
	double   d;
	void    *array;
    } value;
} bam_aux_tag_t;

/*
 * Our bam stream consists of a zlib gzFile stream and a buffer for it to
 * output to. This allows us to call small bam_read requests while
 * translating these to fewer, larger, gzread calls. The overhead is
 * therefore minimal.
 */
#define Z_BUFF_SIZE 65536    /* Max size of a zlib block */
#define BGZF_BUFF_SIZE 65273 // 65535 - MIN_LOOKAHEAD to avoid fill_window()
typedef struct {
    FILE *fp;

    int mode, binary, level;
    z_stream s;

    unsigned char comp[Z_BUFF_SIZE];
    unsigned char *comp_p;
    size_t comp_sz;

    unsigned char uncomp[Z_BUFF_SIZE];
    unsigned char *uncomp_p;
    size_t uncomp_sz;

    /* BAM specifics */
    int32_t next_len;

    SAM_hdr *header;      /* Parsed SAM header */

    /* Cached bam_seq_t, to avoid excessive mallocs */
    bam_seq_t *bs;
    int bs_size;

    /* Boolean to indicate if we've finished the most recent z stream */
    int z_finish;

    /* Indicates whether gzipped, and if so with bgzf extra fields */
    int gzip;
    int bgzf; 

    /* Whether BAM or SAM format */
    int bam;

    /* If true, skip auxillary field parsing while reading SAM */
    int no_aux;

    /* line number (when in SAM mode) */
    int line;

    /* EOF block present in BAM */
    int eof_block;

    /* Static avoidance: used in sam_next_seq() */
    unsigned char *sam_str;
    size_t alloc_l;

    /* Thread pool for encoding */
    t_pool *pool;
    t_results_queue *equeue;

    /* Decoding queue */
    t_results_queue *dqueue;
    void *job_pending;
    int eof;
    int nd_jobs, ne_jobs;

    /* Quality binning */
    enum quality_binning binning;

    /* Disabling CRC checks */
    int ignore_chksum;

    /* Used when gzi files are supplied. */
    gzi *idx;
    char *idx_fn;
    uint64_t current_block;
    unsigned char bgbuf[Z_BUFF_SIZE];
    unsigned char *bgbuf_p;
    size_t bgbuf_sz;
} bam_file_t;

/* BAM flags */
#define BAM_FPAIRED           1
#define BAM_FPROPER_PAIR      2
#define BAM_FUNMAP            4
#define BAM_FMUNMAP           8
#define BAM_FREVERSE         16
#define BAM_FMREVERSE        32
#define BAM_FREAD1           64
#define BAM_FREAD2          128
#define BAM_FSECONDARY      256
#define BAM_FQCFAIL         512
#define BAM_FDUP           1024
#define BAM_FSUPPLEMENTARY 2048

#define BAM_CIGAR32    32768

/* Decoding the bam_seq_t struct */
#define bam_blk_size(b)       ((b)->blk_size)
#define bam_set_blk_size(b,v) ((b)->blk_size = (v))

#define bam_ref(b)       ((b)->ref)
#define bam_pos(b)       ((b)->pos)
#define bam_mate_ref(b)  ((b)->mate_ref)
#define bam_mate_pos(b)  ((b)->mate_pos)
#define bam_ins_size(b)  ((b)->ins_size)
#define bam_cigar_len(b) (((b)->flag & BAM_CIGAR32 ? ((b)->bin<<16) : 0) + (b)->cigar_len)
#define bam_name_len(b)  ((b)->name_len)
#define bam_map_qual(b)  ((b)->map_qual)
#define bam_bin(b)       ((b)->flag & BAM_CIGAR32 ? 0 : (b)->bin)
#define bam_flag(b)      ((b)->flag)
#define bam_seq_len(b)   ((b)->len)
#define bam_strand(b)    ((bam_flag((b)) & BAM_FREVERSE) != 0)
#define bam_mstrand(b)   ((bam_flag((b)) & BAM_FMREVERSE) != 0)

#define bam_set_ref(b,v)        ((b)->ref = (v))
#define bam_set_pos(b,v)        ((b)->pos = (v))
#define bam_set_mate_ref(b,v)   ((b)->mate_ref = (v))
#define bam_set_mate_pos(b,v)   ((b)->mate_pos = (v))
#define bam_set_ins_size(b,v)   ((b)->ins_size = (v))
#define bam_set_cigar_len(b, v) (((v)>>16) ? (((b)->flag |= BAM_CIGAR32), (b)->bin = ((v)>>16), (b)->cigar_len = (v)&0xffff) : ((b)->cigar_len = (v)))
#define bam_set_name_len(b,v)   ((b)->name_len = (v))
#define bam_set_map_qual(b,v)   ((b)->map_qual = (v))
static inline void bam_set_bin(bam_seq_t *b, uint32_t v) {
    if (!(b->flag & BAM_CIGAR32)) b->bin = v;
}
#define bam_set_flag(b,v)       ((b)->flag = (v))
#define bam_set_seq_len(b,v)    ((b)->len = (v))

// equivalent to (char *)(&(b)->data)
#define bam_name(b) ((char *)(b) + offsetof(bam_seq_t, data))

#ifdef ALLOW_UAC
#define bam_cigar(b)     ((uint32_t *)(bam_name((b)) + bam_name_len((b))))
#else
#define bam_cigar(b)     ((uint32_t *)(bam_name((b)) + round4(bam_name_len((b)))))
#endif
#define bam_seq(b)       (((char *)bam_cigar((b))) + 4*bam_cigar_len(b))
#define bam_qual(b)      (bam_seq(b) + (int)(((b)->len+1)/2))
#define bam_aux(b)       (bam_qual(b) + (b)->len)

/* Rounds up to the next multiple of 4 or 8 */
#define round4(v) (((v-1)&~3)+4)
#define round8(v) (((v-1)&~7)+8)

/* CIGAR operations, taken from samtools bam.h */
#define BAM_CIGAR_SHIFT 4
#define BAM_CIGAR_MASK  ((1 << BAM_CIGAR_SHIFT) - 1)

enum cigar_op {
    BAM_UNKNOWN=-1,
    BAM_CMATCH=0,
    BAM_CINS=1,
    BAM_CDEL=2,
    BAM_CREF_SKIP=3,
    BAM_CSOFT_CLIP=4,
    BAM_CHARD_CLIP=5,
    BAM_CPAD=6,
    BAM_CBASE_MATCH=7,
    BAM_CBASE_MISMATCH=8
};

/*
 * Whether this cigar op marches along ref, seq or both
 *
 * Op   Ref   Seq
 * M    1     1
 * I    0     1
 * D    1     0
 * N    1     0
 * S    0     1
 * H    0     0
 * P    0     0
 * =    1     1
 * X    1     1
 */
#define BAM_CONSUME_REF(op) ((0x18d>>(op))&1)
#define BAM_CONSUME_SEQ(op) ((0x193>>(op))&1)


/* ----------------------------------------------------------------------
 * Function prototypes
 * We only support reading, so basically we have open, read, close along
 * with some utility functions for querying aux records.
 */

/*! Opens a SAM or BAM file.
 *
 * The mode parameter indicates the file
 * type (if not auto-detecting) and whether it is for reading or
 * writing. Use "rb" or "wb" for reading or writing BAM and "r" or
 * "w" or reading or writing SAM. When writing BAM, the mode may end
 * with a digit from 0 to 9 to indicate the compression to use with 0
 * indicating uncompressed data.
 *
 * @param fn The filename to open or create.
 * @param mode The input/output mode, similar to fopen().
 *
 * @return
 * Returns a bam_file_t pointer on success;
 *         NULL on failure.
 */
bam_file_t *bam_open(const char *fn, const char *mode);

bam_file_t *bam_open_block(const char *blk, size_t blk_size, SAM_hdr *sh);

/*! Closes a SAM or BAM file.
 * 
 * @param b The file to close.
 *
 * @return
 * Retrurns 0 on success;
 *         -1 on failure.
 */
int bam_close(bam_file_t *b);

/*! Deprecated: please use bam_get_seq() instead.
 */
int bam_next_seq(bam_file_t *b, bam_seq_t **bsp);

/*! Reads the next sequence.
 *
 * Fills out the next bam_seq_t struct.
 * This function will alloc and/or grow the memory accordingly, allowing for
 * efficient reuse.
 *
 * @param bsp Must be non-null, but *bsp may be NULL or an existing
 * bam_seq_t pointer.
 *
 * @return
 * Returns 1 on success;
 *         0 on eof;
 *        -1 on error.
 */
int bam_get_seq(bam_file_t *b, bam_seq_t **bsp);

/*!Looks for aux field 'key' and returns the value.
 * The type is the first char and the value is the 2nd character onwards.
 *
 * @return
 * Returns the value for key; NULL if not found.
 */
char *bam_aux_find(bam_seq_t *b, const char *key);

/*
 * Skips to the next tag start.
 * Returns next tag pointer (or end of buffer) on success,
 *         NULL on failure
 */
char *bam_aux_skip(const char *s);

//!Converts an encoded integer value return by bam_aux_find to an integer.
//
//Analogous to the bam_aux2i functions in samtools.
int32_t bam_aux_i(const uint8_t *dat);
float bam_aux_f(const uint8_t *s);
double bam_aux_d(const uint8_t *s);
char bam_aux_A(const uint8_t *s);
char *bam_aux_Z(const uint8_t *s);

//int bam_aux_del(bam_seq_t *b, uint8_t *s); // not implemented yet

/*! Add auxiliary tags to a bam_seq_t structure.
 *
 * Appends a tag onto the end of a bam_seq_t structure.  The tag name
 * is supplied in the 'tag' parameter, and the data type code in 'type'.
 * Valid type codes are [AcCsSiIfdHZ], as described in the SAM specification.
 *
 * The array_len parameter is used for B type (i.e. array) tags.  If
 * array_len is 0, an ordinary non-array tag is added.  If it is greater
 * than zero, a B type tag of the apropriate data type is made.  Arrays
 * of types H and Z are not allowed, so array_len must be zero if these types
 * are specified.
 *
 * data should point to the data to be added.  This should be of appropriate
 * size for the tag type, i.e. (u)int8_t for A, C and c; (u)int16_t for S and s;
 * (u)int32_t for I and i; a float for f; a double for d; a NUL-terminated
 * string for H and Z.  If array_len is greater than zero, then data should
 * point to an array of the given type.  All data should be in the native
 * format for the machine - it will be converted to little-endian if necessary
 * by the function.
 *
 * @param b         Points to the location of a bam_seq_t *.  If (*b)->alloc
 *                  is too small, the bam_seq_t struct will be reallocated.
 *                  Neither b nor *b should be NULL.
 * @param tag       The tag name (RG, NM, etc.)
 * @param type      The tag data type.
 * @param array_len Array length for array tags, zero for ordinary ones.
 * @param data      Pointer to the tag value.
 *
 * @return
 * Returns  0 on success;
 *         -1 on error
 */

int bam_aux_add(bam_seq_t **b, const char tag[2], char type,
		uint32_t array_len, const void *data);

/*! Add multiple auxiliary tags to a bam_seq_t structure
 *
 * bam_aux_add_vec adds one or more tags listed in the bam_aux_tag_t array
 * to a bam_seq_t structure.  The bam_aux_tag_t struct has four elements,
 * the tag name (char[2]), the type code (char), the array length for B tags and
 * the value which is a union.
 * 
 * The type code determines both the data type of the tag, and the member
 * of value used to access the data.  If array_len is zero, data types 'H' and
 * 'Z' are accessed via member value.h or value.z; 'f' via value.f; 'd' via
 * value.d; 'A' via value.a.  Signed integers should have type 'i', and are
 * accessed through value.i.  Unsigned integers should use type 'I' and
 * value.ui.  The actual type used to store the integer will be the smallest
 * that it will fit in, so signed and unsigned integers that fit in one byte
 * will be stored as type 'c' or 'C'.  Similarly, integers that fit in two
 * bytes will be stored as type 's' or 'S' for unsigned.
 *
 * If array_len is non-zero, a B-type (array) tag is stored.  In this case
 * value.array should point to the data to be stored.  The type of array
 * is interpreted according to the requested tag type, i.e. char for 'A';
 * int8_t for 'c'; uint8_t for 'C'; int16_t for 's'; uint16_t for 'S';
 * int32_t for 'i'; uint32_t for 'I'; float for 'f'; double for 'd'.  No
 * attempt is made to adjust the size of integers when storing arrays. All
 * data should be in the native format for the machine - it will be converted
 * to little-endian if necessary by the function.
 *
 * @param b         Points to the location of a bam_seq_t *.  If (*b)->alloc
 *                  is too small, the bam_seq_t struct will be reallocated.
 *                  Neither b nor *b should be NULL.
 * @param count     The number of elements in the tags array
 * @param tags      Array of bam_aux_tag_t structs, listing the tags to add.
 * 
 * @return 
 * Returns  0 on success;
 *         -1 on error
 */

int bam_aux_add_vec(bam_seq_t **b, uint32_t count, bam_aux_tag_t *tags);

/*! Calculate the amount of space needed to store auxiliary tags
 *
 * The tags array should be filled out as described in bam_aux_add_vec.
 * This function iterates through the list of items in the tags array
 * to work out how much space will be needed to store them.  This value
 * can be passed to bam_construct_seq in the extra_len parameter to
 * ensure that enough space is allocated to store the tags.
 *
 * @param count     The number of elements in the tags array
 * @param tags      Array of bam_aux_tag_t structs, listing the tags to add.
 *
 * @return
 * Returns the total space required for the tags on success;
 *         -1 on failure.
 */

ssize_t bam_aux_size_vec(uint32_t count, bam_aux_tag_t *tags);

/*! Helper for filling in the bam_aux_tag_t struct
 * @param tag   Pointer to bam_aux_tag_t struct
 * @param name  Tag name
 * @param val   Tag value
 */
static inline void bam_aux_tag_char(bam_aux_tag_t *tag, char *name, char val) {
    tag->tag[0] = name[0];
    tag->tag[1] = name[1];
    tag->type = 'A';
    tag->array_len = 0;
    tag->value.a = val;
}
/*! Helper for filling in the bam_aux_tag_t struct
 * @param tag   Pointer to bam_aux_tag_t struct
 * @param name  Tag name
 * @param val   Tag value
 */
static inline void bam_aux_tag_int(bam_aux_tag_t *tag,
				   char *name, int32_t val) {
    tag->tag[0] = name[0];
    tag->tag[1] = name[1];
    tag->type = 'i';
    tag->array_len = 0;
    tag->value.i = val;
}
/*! Helper for filling in the bam_aux_tag_t struct
 * @param tag   Pointer to bam_aux_tag_t struct
 * @param name  Tag name
 * @param val   Tag value
 */
static inline void bam_aux_tag_uint(bam_aux_tag_t *tag,
				    char *name, uint32_t val) {
    tag->tag[0] = name[0];
    tag->tag[1] = name[1];
    tag->type = 'I';
    tag->array_len = 0;
    tag->value.ui = val;
}
/*! Helper for filling in the bam_aux_tag_t struct
 * @param tag   Pointer to bam_aux_tag_t struct
 * @param name  Tag name
 * @param val   Tag value
 */
static inline void bam_aux_tag_float(bam_aux_tag_t *tag,
				     char *name, float val) {
    tag->tag[0] = name[0];
    tag->tag[1] = name[1];
    tag->type = 'f';
    tag->array_len = 0;
    tag->value.f = val;
}
/*! Helper for filling in the bam_aux_tag_t struct
 * @param tag   Pointer to bam_aux_tag_t struct
 * @param name  Tag name
 * @param val   Tag value
 */
static inline void bam_aux_tag_double(bam_aux_tag_t *tag,
				      char *name, double val) {
    tag->tag[0] = name[0];
    tag->tag[1] = name[1];
    tag->type = 'd';
    tag->array_len = 0;
    tag->value.d = val;
}
/*! Helper for filling in the bam_aux_tag_t struct
 * @param tag   Pointer to bam_aux_tag_t struct
 * @param name  Tag name
 * @param val   Tag value
 */
static inline void bam_aux_tag_string(bam_aux_tag_t *tag,
				      char *name, char *str) {
    tag->tag[0] = name[0];
    tag->tag[1] = name[1];
    tag->type = 'Z';
    tag->array_len = 0;
    tag->value.z = str;
}
/*! Helper for filling in the bam_aux_tag_t struct
 * @param tag   Pointer to bam_aux_tag_t struct
 * @param name  Tag name
 * @param val   Tag value
 */
static inline void bam_aux_tag_hexstring(bam_aux_tag_t *tag,
					 char *name, uint8_t *str) {
    tag->tag[0] = name[0];
    tag->tag[1] = name[1];
    tag->type = 'H';
    tag->array_len = 0;
    tag->value.h = str;
}
/*! Helper for filling in the bam_aux_tag_t struct
 * @param tag   Pointer to bam_aux_tag_t struct
 * @param name  Tag name
 * @param val   Tag value
 */
static inline void bam_aux_tag_array(bam_aux_tag_t *tag, char *name, char type,
				     uint32_t array_len, void *data) {
    tag->tag[0] = name[0];
    tag->tag[1] = name[1];
    tag->type = type;
    tag->array_len = array_len;
    tag->value.array = data;
}

/*! Add SAM formatted aux tags to a bam_seq_t.
 *
 * Appends one or more SAM-format tags onto the end of a bam_seq_t structure.
 * If multiple tags are present, they should be separated by tabs, as in
 * a SAM file.
 *
 * @param bsp       Points to the location of a bam_seq_t *.  If (*bsp)->alloc
 *                  is too small, the bam_seq_t struct will be reallocated.
 *                  Neither bsp nor *bsp should be NULL.
 * @param sam       SAM-foratted tag string.
 *
 * @return
 * Returns  0 on success;
 *         -1 on error
 */

int bam_aux_add_from_sam(bam_seq_t **bsp, char *sam);

/*! Add preformated raw aux data to the bam_seq.
 *
 * This interface is similar to the samtools bam_aux_append function.
 * It creates a tag with the given name and type, and then appends the
 * supplied data to it as the value.  It is up to the caller to ensure that
 * the data has been formatted correctly as given in the SAM specification.
 * This function makes no checks on the data, but simply copies it.
 *
 * Consider using bam_aux_add instead if you have information in a more
 * integer or string form.
 *
 * @param b         Points to the location of a bam_seq_t *.  If (*b)->alloc
 *                  is too small, the bam_seq_t struct will be reallocated.
 *                  Neither b nor *b should be NULL.
 * @param tag       The tag name (RG, NM, etc.)
 * @param type      The tag data type.
 * @param len       The number of bytes of data present.
 * @param data      Pre-formatted data.
 *
 * @return
 * Returns 0 on success;
 *        -1 on failure
 */
int bam_aux_add_data(bam_seq_t **b, const char tag[2],
		     char type, size_t len, const uint8_t *data);

/*! Add raw data to a bam structure.
 *
 * This could be useful if you wish to manually construct your own bam
 * entries or if you need to append an entire block of preformatting
 * aux data.
 *
 * @param b         Points to the location of a bam_seq_t *.  If (*b)->alloc
 *                  is too small, the bam_seq_t struct will be reallocated.
 *                  Neither b nor *b should be NULL.
 * @param len       The number of bytes of data present.
 * @param data      Pre-formatted data.
 *
 * @retrun
 * Returns 0 on success;
 *        -1 on failure
 */
int bam_add_raw(bam_seq_t **b, size_t len, const uint8_t *data);

/*! An iterator on bam_aux_t fields.
 *
 * NB: This code is not reentrant or multi-thread capable. The values
 * returned are valid until the next call to this function.
 *
 * @param key  points to an array of 2 characters (eg "RG", "NM")
 * @param type points to an address of 1 character (eg 'Z', 'i')
 * @paran val  points to an address of a bam_aux_t union.
 * @param iter_handle NULL to initialise the search, and then the
 * returned (modified) iter_handle on each subsequent call to continue
 * the iteration.
 *
 * @return
 * Returns 0 if the next value is valid, setting key, type and val;
 *        -1 when no more found.
 */
int bam_aux_iter(bam_seq_t *b, char **iter_handle,
		 char *key, char *type, bam_aux_t *val);

/* Taken from samtools/bam.h */
#define bam_seqi(s, i) ((s)[(i)/2] >> 4*(1-(i)%2) & 0xf)
#define bam_nt16_rev_table "=ACMGRSVTWYHKDBN"

/* Output code */

/*! Writes a single bam sequence object.
 *
 * @param fp The SAM/BAM file handle.
 * @param b  The bam_seq_t pointer
 *
 * @return
 * Returns 0 on success;
 *        -1 on failure
 */
int bam_put_seq(bam_file_t *fp, bam_seq_t *b);

/*! Constructs a bam_seq_t from separate components.
 *
 * Note: ignores auxiliary tags for now. These need to be appended
 * manually by the calling function.
 *
 * @param b         Points to the location of a bam_seq_t *.  If *b is NULL
 *                  or (*b)->alloc is too small, the bam_seq_t struct will
 *                  be reallocated.
 * @param extra_len Extra space to allocate for auxiliary tags
 * @param qname     Query name
 * @param qname_len Query name length
 * @param flag      BAM flags
 * @param rname     Reference ID
 * @param pos       Mapped position (N.B. 1-based)
 * @param end       Last aligned base
 * @param mapq      Mapping quality
 * @param ncigar    Number of CIGAR elements
 * @param cigar     CIGAR alignment information
 * @param mrnm      Mate reference ID
 * @param mpos      Mate position (N.B. 1-based)
 * @param isize     Insert size
 * @param len       Sequence length
 * @param seq       Sequence (ASCII format)
 * @param qual      Quality values (phred scale, 8-bit binary, no offset)
 *                  Passing in NULL to qual will cause all quality values
 *                  to be treated as absent (i.e. set to 0xff).
 *
 * @return
 * Returns number of bytes written to bam_seq_t on success (ie tag offset);
 *         -1 on error.
 */
int bam_construct_seq(bam_seq_t **b, size_t extra_len,
		      const char *qname, size_t qname_len,
		      int flag,
		      int rname,      // Ref ID
		      int64_t pos,
		      int64_t end, // aligned start/end coords
		      int mapq,
		      uint32_t ncigar, const uint32_t *cigar,
		      int mrnm,       // Mate Ref ID
		      int64_t mpos,
		      int64_t isize,
		      int len,
		      const char *seq,
		      const char *qual);

/*! Duplicates a bam_seq_t structure.
 *
 * @return
 * Returns the new bam_seq_t pointer on success;
 *         NULL on failure.
 */
bam_seq_t *bam_dup(bam_seq_t *b);

/*! Writes a SAM header block.
 *
 * @return
 * Returns 0 for success;
 *        -1 for failure
 */
int bam_write_header(bam_file_t *out);

enum bam_option {
    BAM_OPT_THREAD_POOL,
    BAM_OPT_BINNING,
    BAM_OPT_IGNORE_CHKSUM,
    BAM_OPT_WITH_BGZIP_IDX,
    BAM_OPT_OUTPUT_BGZIP_IDX
};

/*! Sets options on the bam_file_t.
 *
 * Sets options on the bam_file_t. See BAM_OPT_* definitions in bam.h.
 * Use this immediately after opening.
 *
 * @return
 * Returns 0 on success;
 *        -1 on failure
 */
int bam_set_option(bam_file_t *fd, enum bam_option opt, ...);

/*! Sets options on the bam_file_t.
 *
 * Sets options on the bam_file_t. See BAM_OPT_* definitions in bam.h.
 * Use this immediately after opening.
 *
 * @return
 * Returns 0 on success;
 *        -1 on failure
 */
int bam_set_voption(bam_file_t *fd, enum bam_option opt, va_list args);



/*
 * Signed and unsigned fast functions to act as equiv to sprintf(cp, "%d", i)
 */
unsigned char *append_int(unsigned char *cp, int32_t i);
unsigned char *append_uint(unsigned char *cp, uint32_t i);

#ifdef __cplusplus
}
#endif

#endif /* _BAM_H_ */
