/* TabDelimFile- a datastructure to read a tab-delimited file, split each line
**               into fields, and provide access to one line at a time.
**               Only sequential access of lines is allowed, but random access
**               of the fields within a line is possible. The structure allows
**               for skiping blank lines and comments in a file. The number
**               of fields in a line is limited only by the amount of memory
**               available
*/
#ifndef _JSW_TABDELIMFILE_H_
#define _JSW_TABDELIMFILE_H_

#include <stdio.h>

/* size of the buffer, chosen to reduce internal fragmentation */
#define TABDELIMBUFF (1024-6)

/* TabDelimFile data structure, members should never be accessed directly */
typedef struct tabdelimfile_t TabDelimFile;

/*******************       constructors/destructor         *******************/
/* initTabDelimFile- initializes a TabDelimFile with a file pointer
**
** *input parameters*
** FILE         * file file open for reading in tab delimited format
**
** *return*
** TabDelimFile *      initialized TabDelimFile data structure or NULL on error
*/
TabDelimFile *initTabDelimFile(FILE *file);

/* openTabDelimFile- initializes a TabDelimFile with a file name
**
** *input parameters*
** const char   * fn   name of file in tab delimited format
**
** *return*
** TabDelimFile *      initialized TabDelimFile data structure or NULL on error
*/
TabDelimFile *openTabDelimFile(const char *fn);

/* freeTabDelimFile- frees a TabDelimFile data structure
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to free
*/
void freeTabDelimFile(TabDelimFile *tdf);

/*******************           accessor functions          *******************/
/* tdf_getField- get a char pointer to a field in the current line
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
** int            idx  index of the field to get, zero based
**
** *return*
** const char   *      the requested field or NULL on error
*/
const char *tdf_getField(TabDelimFile *tdf, int idx);

/* tdf_getFieldAsChar- get a character in the current line
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
** int            idx  index of the field to get, zero based
**
** *output parameters*
** int          * err  optional flag to determine if return is valid
**                              < 0 for error
**                              including if field is not a single character
**
** *return*
** char                the requested field if valid or 0 on error
*/
char tdf_getFieldAsChar(TabDelimFile *tdf, int idx, int *err);

/* tdf_getFieldAsInt- get an integer in the current line
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
** int            idx  index of the field to get, zero based
**
** *output parameters*
** int          * err  optional flag to determine if return is valid
**                              < 0 for error
**                              including if field is not a valid integer
**
** *return*
** long                the requested field if valid or 0 on error
*/
long tdf_getFieldAsInt(TabDelimFile *tdf, int idx, int *err);

/* tdf_getFieldAsFloat- get a floating point value in the current line
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
** int            idx  index of the field to get, zero based
**
** *output parameters*
** int          * err  optional flag to determine if return is valid
**                              < 0 for error
**                              including if field is not a valid float
**
** *return*
** char                the requested field if valid or 0 on error
*/
double tdf_getFieldAsFloat(TabDelimFile *tdf, int idx, int *err);

/* tdf_getCurLine- get the current line number
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
**
** *return*
** unsigned int        the current line number or 0 on error
*/
unsigned int tdf_getCurLine(TabDelimFile *tdf);

/* tdf_getNumFields- get the number of field in the current line
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
** 
** *return*
** unsigned int        the number of fields on the current line or 0 on error
*/
unsigned int tdf_getNumFields(TabDelimFile *tdf);

/*******************           modifier functions          *******************/
/* tdf_setCommentDelim- sets a string of length 1 or 2 to signify a comment
**                      comments include any non-tab whitespace before the
**                      delimiter and all characters after the delimiter
**                      up to the newline. If the comment is the only thing
**                      on the line, then the whole line will be skipped.
**                      If string is NULL or zero length, comments will no
**                      longer be recognized
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
** const char   * str  string containing the comment delimiter
**                     if zero length or NULL, will clear the comment delimiter
*/
void tdf_setCommentDelim(TabDelimFile *tdf, const char *str);

/* tdf_skipBlankLines- sets a flag to skip blank lines when getLine is called
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
*/
void tdf_skipBlankLines(TabDelimFile *tdf);

/* tdf_skipBlankLines- sets a flag to return blank lines when getLine is called
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
*/
void tdf_keepBlankLines(TabDelimFile *tdf);

/* tdf_getLine- get the next line in the tab delimited file and returns it
**              will skip comment lines and strip comments if a comment
**              delimiter is set to a non-zero string
**              will skip blank lines if the flag to do so has been set
**
** *input parameters*
** TabDelimFile * tdf TabDelimFile to access
**
** *return*
** int                 0 on success, EOF on end of file, < -1 on error
*/
int tdf_getLine(TabDelimFile *tdf);

#endif
