#ifndef _vcf_h_
#define _vcf_h_

#ifndef _xtend_h_
//#include <xtend.h>
#endif

#ifndef _sam_h_
#include "sam.h"
#endif

#ifndef _biolibc_h_
#include "biolibc.h"
#endif

#ifndef _bool_true_false_are_defined
#include <stdbool.h>
#endif

/*
 *  vcfio is meant to provide a very simple and fast method for processing
 *  VCF streams one call at a time.  As such, there should generally be only
 *  one or a few bl_vcf_t structures substantiated at a given moment, and
 *  we can afford to be generous with the max sizes.
 *  If you're writing programs that inhale many VCF calls into memory, vcfio
 *  is not for you.
 */

#define BL_VCF_ID_MAX_CHARS         256 // FIXME: Arbitrary guess
// Hack:
// Use different sizes for each so dsv_read_field() buffer overflow errors
// will point to a specific field.  Eventually should have dsv_read_field()
// return an error code rather than exit with an error message
#define BL_VCF_REF_MAX_CHARS        32
#define BL_VCF_ALT_MAX_CHARS        33
#define BL_VCF_QUAL_MAX_CHARS       34
#define BL_VCF_FILTER_MAX_CHARS     64

// We actually saw INFO fields over 512k in some dbGap BCFs
typedef struct
{
    char        chrom[BL_CHROM_MAX_CHARS + 1],
		id[BL_VCF_ID_MAX_CHARS + 1],
		ref[BL_VCF_REF_MAX_CHARS + 1],
		alt[BL_VCF_ALT_MAX_CHARS + 1],
		qual[BL_VCF_QUAL_MAX_CHARS + 1],
		filter[BL_VCF_FILTER_MAX_CHARS + 1],
		*info,
		*format,
		*single_sample; // Avoid using multi_samples
    uint64_t    pos;
    size_t      info_len,
		info_max,
		format_max,
		sample_max;
    unsigned    ref_count,
		alt_count,
		other_count;
    
    // Use bl_vcf_sample_alloc() to initialize this for multi-sample VCFs
    char        **multi_samples;
    
    // Apps can buffer phred scores from reads to collect stats
    unsigned char   *phreds;
    size_t          phred_count;
    size_t          phred_buff_size;
}   bl_vcf_t;

typedef unsigned int            vcf_field_mask_t;

#define BL_VCF_FIELD_ALL        0xfff
#define BL_VCF_FIELD_CHROM      0x001
#define BL_VCF_FIELD_POS        0x002
#define BL_VCF_FIELD_ID         0x004
#define BL_VCF_FIELD_REF        0x008
#define BL_VCF_FIELD_ALT        0x010
#define BL_VCF_FIELD_QUAL       0x020
#define BL_VCF_FIELD_FILTER     0x040
#define BL_VCF_FIELD_INFO       0x080
#define BL_VCF_FIELD_FORMAT     0x100
#define BL_VCF_FIELD_ERROR      0x000

/*
 *  Generated by /home/bacon/scripts/gen-get-set
 *
 *  Accessor macros.  Use these to access structure members from functions
 *  outside the bl_vcf_t class.
 *
 *  These generated macros are not expected to be perfect.  Check and edit
 *  as needed before adding to your code.
 */

#define BL_VCF_CHROM(ptr)               ((ptr)->chrom)
#define BL_VCF_CHROM_AE(ptr,c)          ((ptr)->chrom[c])
#define BL_VCF_ID(ptr)                  ((ptr)->id)
#define BL_VCF_ID_AE(ptr,c)             ((ptr)->id[c])
#define BL_VCF_REF(ptr)                 ((ptr)->ref)
#define BL_VCF_REF_AE(ptr,c)            ((ptr)->ref[c])
#define BL_VCF_ALT(ptr)                 ((ptr)->alt)
#define BL_VCF_ALT_AE(ptr,c)            ((ptr)->alt[c])
#define BL_VCF_QUAL(ptr)                ((ptr)->qual)
#define BL_VCF_QUAL_AE(ptr,c)           ((ptr)->qual[c])
#define BL_VCF_FILTER(ptr)              ((ptr)->filter)
#define BL_VCF_FILTER_AE(ptr,c)         ((ptr)->filter[c])
#define BL_VCF_INFO(ptr)                ((ptr)->info)
#define BL_VCF_INFO_AE(ptr,c)           ((ptr)->info[c])
#define BL_VCF_FORMAT(ptr)              ((ptr)->format)
#define BL_VCF_FORMAT_AE(ptr,c)         ((ptr)->format[c])
#define BL_VCF_SINGLE_SAMPLE(ptr)       ((ptr)->single_sample)
#define BL_VCF_SINGLE_SAMPLE_AE(ptr,c)  ((ptr)->single_sample[c])
#define BL_VCF_POS(ptr)                 ((ptr)->pos)
#define BL_VCF_INFO_LEN(ptr)            ((ptr)->info_len)
#define BL_VCF_INFO_MAX(ptr)            ((ptr)->info_max)
#define BL_VCF_FORMAT_MAX(ptr)          ((ptr)->format_max)
#define BL_VCF_SAMPLE_MAX(ptr)          ((ptr)->sample_max)
#define BL_VCF_REF_COUNT(ptr)           ((ptr)->ref_count)
#define BL_VCF_ALT_COUNT(ptr)           ((ptr)->alt_count)
#define BL_VCF_OTHER_COUNT(ptr)         ((ptr)->other_count)
#define BL_VCF_MULTI_SAMPLES(ptr)       ((ptr)->multi_samples)
#define BL_VCF_MULTI_SAMPLES_AE(ptr,c)  ((ptr)->multi_samples[c])
#define BL_VCF_PHREDS(ptr)              ((ptr)->phreds)
#define BL_VCF_PHREDS_AE(ptr,c)         ((ptr)->phreds[c])
#define BL_VCF_PHRED_COUNT(ptr)         ((ptr)->phred_count)
#define BL_VCF_PHRED_BUFF_SIZE(ptr)     ((ptr)->phred_buff_size)

/*
 *  Generated by /home/bacon/scripts/gen-get-set
 *
 *  Mutator macros for setting with no sanity checking.  Use these to
 *  set structure members from functions outside the bl_vcf_t
 *  class.  These macros perform no data validation.  Hence, they achieve
 *  maximum performance where data are guaranteed correct by other means.
 *  Use the mutator functions (same name as the macro, but lower case)
 *  for more robust code with a small performance penalty.
 *
 *  These generated macros are not expected to be perfect.  Check and edit
 *  as needed before adding to your code.
 */

// FIXME: Assuming char array is a null-terminated string
#define BL_VCF_SET_CHROM_CPY(ptr,val,array_size) strlcpy((ptr)->chrom,val,array_size)
#define BL_VCF_SET_CHROM_AE(ptr,c,val)          ((ptr)->chrom[c] = (val))
// FIXME: Assuming char array is a null-terminated string
#define BL_VCF_SET_ID_CPY(ptr,val,array_size)   strlcpy((ptr)->id,val,array_size)
#define BL_VCF_SET_ID_AE(ptr,c,val)             ((ptr)->id[c] = (val))
// FIXME: Assuming char array is a null-terminated string
#define BL_VCF_SET_REF_CPY(ptr,val,array_size)  strlcpy((ptr)->ref,val,array_size)
#define BL_VCF_SET_REF_AE(ptr,c,val)            ((ptr)->ref[c] = (val))
// FIXME: Assuming char array is a null-terminated string
#define BL_VCF_SET_ALT_CPY(ptr,val,array_size)  strlcpy((ptr)->alt,val,array_size)
#define BL_VCF_SET_ALT_AE(ptr,c,val)            ((ptr)->alt[c] = (val))
// FIXME: Assuming char array is a null-terminated string
#define BL_VCF_SET_QUAL_CPY(ptr,val,array_size) strlcpy((ptr)->qual,val,array_size)
#define BL_VCF_SET_QUAL_AE(ptr,c,val)           ((ptr)->qual[c] = (val))
// FIXME: Assuming char array is a null-terminated string
#define BL_VCF_SET_FILTER_CPY(ptr,val,array_size) strlcpy((ptr)->filter,val,array_size)
#define BL_VCF_SET_FILTER_AE(ptr,c,val)         ((ptr)->filter[c] = (val))
#define BL_VCF_SET_INFO(ptr,val)                ((ptr)->info = (val))
// FIXME: Assuming char array is a null-terminated string
#define BL_VCF_SET_INFO_CPY(ptr,val,array_size) strlcpy((ptr)->info,val,array_size)
#define BL_VCF_SET_INFO_AE(ptr,c,val)           ((ptr)->info[c] = (val))
#define BL_VCF_SET_FORMAT(ptr,val)              ((ptr)->format = (val))
// FIXME: Assuming char array is a null-terminated string
#define BL_VCF_SET_FORMAT_CPY(ptr,val,array_size) strlcpy((ptr)->format,val,array_size)
#define BL_VCF_SET_FORMAT_AE(ptr,c,val)         ((ptr)->format[c] = (val))
#define BL_VCF_SET_SINGLE_SAMPLE(ptr,val)       ((ptr)->single_sample = (val))
// FIXME: Assuming char array is a null-terminated string
#define BL_VCF_SET_SINGLE_SAMPLE_CPY(ptr,val,array_size) strlcpy((ptr)->single_sample,val,array_size)
#define BL_VCF_SET_SINGLE_SAMPLE_AE(ptr,c,val)  ((ptr)->single_sample[c] = (val))
#define BL_VCF_SET_POS(ptr,val)                 ((ptr)->pos = (val))
#define BL_VCF_SET_INFO_LEN(ptr,val)            ((ptr)->info_len = (val))
#define BL_VCF_SET_INFO_MAX(ptr,val)            ((ptr)->info_max = (val))
#define BL_VCF_SET_FORMAT_MAX(ptr,val)          ((ptr)->format_max = (val))
#define BL_VCF_SET_SAMPLE_MAX(ptr,val)          ((ptr)->sample_max = (val))
#define BL_VCF_SET_REF_COUNT(ptr,val)           ((ptr)->ref_count = (val))
#define BL_VCF_SET_ALT_COUNT(ptr,val)           ((ptr)->alt_count = (val))
#define BL_VCF_SET_OTHER_COUNT(ptr,val)         ((ptr)->other_count = (val))
#define BL_VCF_SET_MULTI_SAMPLES(ptr,val)       ((ptr)->multi_samples = (val))
// FIXME: Assuming all elements should be copied
#define BL_VCF_SET_MULTI_SAMPLES_CPY(ptr,val,array_size) \
    for (size_t c = 0; c < (array_size); ++c) (ptr)->multi_samples[c] = val[c];
#define BL_VCF_SET_MULTI_SAMPLES_AE(ptr,c,val)  ((ptr)->multi_samples[c] = (val))
#define BL_VCF_SET_PHREDS(ptr,val)              ((ptr)->phreds = (val))
// FIXME: Assuming all elements should be copied
#define BL_VCF_SET_PHREDS_CPY(ptr,val,array_size) \
    for (size_t c = 0; c < (array_size); ++c) (ptr)->phreds[c] = val[c];
#define BL_VCF_SET_PHREDS_AE(ptr,c,val)         ((ptr)->phreds[c] = (val))
#define BL_VCF_SET_PHRED_COUNT(ptr,val)         ((ptr)->phred_count = (val))
#define BL_VCF_SET_PHRED_BUFF_SIZE(ptr,val)     ((ptr)->phred_buff_size = (val))

/* vcf.c */
FILE *bl_vcf_skip_header(FILE *vcf_stream);
void bl_vcf_get_sample_ids(FILE *vcf_stream, char *sample_ids[], size_t first_col, size_t last_col);
int bl_vcf_read_static_fields(FILE *vcf_stream, bl_vcf_t *vcf_call, vcf_field_mask_t field_mask);
int bl_vcf_read_ss_call(FILE *vcf_stream, bl_vcf_t *vcf_call, vcf_field_mask_t field_mask);
int bl_vcf_write_static_fields(FILE *vcf_stream, bl_vcf_t *vcf_call, vcf_field_mask_t field_mask);
int bl_vcf_write_ss_call(FILE *vcf_stream, bl_vcf_t *vcf_call, vcf_field_mask_t field_mask);
char **bl_vcf_sample_alloc(bl_vcf_t *vcf_call, size_t samples);
void bl_vcf_free(bl_vcf_t *vcf_call);
void bl_vcf_init(bl_vcf_t *vcf_call, size_t info_max, size_t format_max, size_t sample_max);
vcf_field_mask_t bl_vcf_parse_field_spec(char *spec);
_Bool bl_vcf_call_in_alignment(bl_vcf_t *vcf_call, bl_sam_t *sam_alignment);
_Bool bl_vcf_call_downstream_of_alignment(bl_vcf_t *vcf_call, bl_sam_t *alignment);
void bl_vcf_call_out_of_order(bl_vcf_t *vcf_call, char *previous_chrom, uint64_t previous_pos);

/* vcf-mutators.c */
int bl_vcf_set_chrom_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char new_chrom_element);
int bl_vcf_set_chrom_cpy(bl_vcf_t *bl_vcf_ptr, char new_chrom[], size_t array_size);
int bl_vcf_set_id_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char new_id_element);
int bl_vcf_set_id_cpy(bl_vcf_t *bl_vcf_ptr, char new_id[], size_t array_size);
int bl_vcf_set_ref_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char new_ref_element);
int bl_vcf_set_ref_cpy(bl_vcf_t *bl_vcf_ptr, char new_ref[], size_t array_size);
int bl_vcf_set_alt_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char new_alt_element);
int bl_vcf_set_alt_cpy(bl_vcf_t *bl_vcf_ptr, char new_alt[], size_t array_size);
int bl_vcf_set_qual_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char new_qual_element);
int bl_vcf_set_qual_cpy(bl_vcf_t *bl_vcf_ptr, char new_qual[], size_t array_size);
int bl_vcf_set_filter_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char new_filter_element);
int bl_vcf_set_filter_cpy(bl_vcf_t *bl_vcf_ptr, char new_filter[], size_t array_size);
int bl_vcf_set_info(bl_vcf_t *bl_vcf_ptr, char *new_info);
int bl_vcf_set_info_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char new_info_element);
int bl_vcf_set_info_cpy(bl_vcf_t *bl_vcf_ptr, char *new_info, size_t array_size);
int bl_vcf_set_format(bl_vcf_t *bl_vcf_ptr, char *new_format);
int bl_vcf_set_format_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char new_format_element);
int bl_vcf_set_format_cpy(bl_vcf_t *bl_vcf_ptr, char *new_format, size_t array_size);
int bl_vcf_set_single_sample(bl_vcf_t *bl_vcf_ptr, char *new_single_sample);
int bl_vcf_set_single_sample_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char new_single_sample_element);
int bl_vcf_set_single_sample_cpy(bl_vcf_t *bl_vcf_ptr, char *new_single_sample, size_t array_size);
int bl_vcf_set_pos(bl_vcf_t *bl_vcf_ptr, uint64_t new_pos);
int bl_vcf_set_info_len(bl_vcf_t *bl_vcf_ptr, size_t new_info_len);
int bl_vcf_set_info_max(bl_vcf_t *bl_vcf_ptr, size_t new_info_max);
int bl_vcf_set_format_max(bl_vcf_t *bl_vcf_ptr, size_t new_format_max);
int bl_vcf_set_sample_max(bl_vcf_t *bl_vcf_ptr, size_t new_sample_max);
int bl_vcf_set_ref_count(bl_vcf_t *bl_vcf_ptr, unsigned new_ref_count);
int bl_vcf_set_alt_count(bl_vcf_t *bl_vcf_ptr, unsigned new_alt_count);
int bl_vcf_set_other_count(bl_vcf_t *bl_vcf_ptr, unsigned new_other_count);
int bl_vcf_set_multi_samples(bl_vcf_t *bl_vcf_ptr, char **new_multi_samples);
int bl_vcf_set_multi_samples_ae(bl_vcf_t *bl_vcf_ptr, size_t c, char *new_multi_samples_element);
int bl_vcf_set_multi_samples_cpy(bl_vcf_t *bl_vcf_ptr, char **new_multi_samples, size_t array_size);
int bl_vcf_set_phreds(bl_vcf_t *bl_vcf_ptr, unsigned char *new_phreds);
int bl_vcf_set_phreds_ae(bl_vcf_t *bl_vcf_ptr, size_t c, unsigned char new_phreds_element);
int bl_vcf_set_phreds_cpy(bl_vcf_t *bl_vcf_ptr, unsigned char *new_phreds, size_t array_size);
int bl_vcf_set_phred_count(bl_vcf_t *bl_vcf_ptr, size_t new_phred_count);
int bl_vcf_set_phred_buff_size(bl_vcf_t *bl_vcf_ptr, size_t new_phred_buff_size);

#endif // _vcf_h_
