#ifndef _csv_h__
#define _csv_h__

/** csv **
 *
 * Create text logs (CSV) in conjunction with the DataLog module.
 * 
 * The first step to creating a CSV file is to obtain a DataLog. This could mean reading an
 * existing binary data log file in order to convert it to text. It could also mean creating
 * your own DataLog struct in order to use this module to write a CSV file directly, without
 * the need for an intermediary binary log file.
 * 
 * Refer to datalog.h for full usage instructions on the DataLog struct.
 * 
 * Next, open a FILE for writing using the standard C library. You may also just use `stdout`.
 * 
 * Then, set up a struct CSV_Options in order to configure the desired text format. This struct
 * also points to the FILE you have opened and the DataLog you have obtained.
 * 
 * You are now ready to initialize the module with csv_init() which returns a handle that you
 * must pass to all other functions.
 * 
 * The function csv_write_header() writes out the default header, as found in your DataLog struct.
 * 
 * If your application needs to write a custom header, there is a way, though it is a bit more
 * complicated. You must set up a struct CSV_Channel_Header for each channel. Then put your
 * CSV_Channel_Header structs in a List and pass it to csv_write_header_custom().
 * 
 * After the header is written, you may write each row one by one. You are responsible for
 * populating the DataLog struct with the row data. Then call csv_write_row().
 * 
 * You may have noticed that it is possible to open an existing binary log file using the
 * DataLog module, reading it row by row, and after each row, calling csv_write_row(), in
 * order to convert a binary log to text. That's true! But it's easier to just call
 * csv_convert() which does exactly that for you automatically.
 * 
 * In all cases, when finished, make sure to call csv_exit() to free the handle.
 */

#include <stdio.h>
#include <stdint.h>

#include "unit.h"
#include "datalog.h"

#ifdef __cplusplus
extern "C" {
#endif


/**
 * Option flag that indicates ASCII output, i.e. no Unicode symbols
 */
#define CSV_FLAG_ASCII  0x01

/**
 * Option flag that indicates to use a decimal comma instead of a period
 */
#define CSV_FLAG_DECIMAL_COMMA  0x02

// Timestamp formats
#define CSV_TIME_NONE                   0
#define CSV_TIME_EPOCH_MS               1
#define CSV_TIME_SYSTEM_DEFAULT         2
#define CSV_TIME_ISO_8601_SHORT         3
#define CSV_TIME_ISO_8601_SHORT_MS      4
#define CSV_TIME_ISO_8601_LONG          5
#define CSV_TIME_ISO_8601_LONG_MS       6
#define CSV_TIME_ISO_8601_LONG_DUAL     7
#define CSV_TIME_ISO_8601_LONG_MS_DUAL  8

/**
 * CSV handle.
 */
typedef void *CSV;

/**
 * Progress callback function that can be called periodically by csv_convert() to indicate
 * conversion progress.
 */
typedef void (*progress_fn_t)(uint32_t done, uint32_t total, void *user_data);

/**
 * CSV log configuration. See the top of this file for usage details.
 */
typedef struct CSV_Options {

    FILE *file;  // writing to this file
    DataLog *datalog;  // reading data from this struct

    const char *separator_str;  // null-terminated string
    const char *error_str;  // null-terminated string
    uint32_t flags;  // combination mask of the above CSV_FLAG constants
    int8_t frac_digits;  // number of fractional digits
    int8_t time_format;  // one of the CSV_TIME constants

    unit_t units[UNIT_CATEGORIES];  // preferred unit for each category

} CSV_Options;

/**
 * Custom channel header to use with csv_write_header_custom().
 */
typedef struct CSV_Channel_Header {

    char *name;  // null-terminated string; column name
    char *id;    // null-terminated string; column ID
    char *description;  // null-terminated string; column description
    unit_t unit;  // unit corresponding to this column

} CSV_Channel_Header;

/**
 * Initializes a handle for writing according to the given options.
 * Data will be read from opt->datalog and written to opt->file.
 * 
 * Return: a CSV handle which must be freed by csv_exit() later
 */
CSV csv_init(CSV_Options *opt);

/**
 * Free the given handle, which becomes invalid.
 */
void csv_exit(CSV csv);

/**
 * Write the default header, which is built from the DataLog header.
 */
void csv_write_header(CSV csv);

/**
 * Write a custom header based on the given List of CSV_Channel_Header structs.
 */
void csv_write_header_custom(CSV csv, List *channel_headers);

/**
 * Write row from DataLog to the file.
 */
void csv_write_row(CSV csv);

/**
 * Convert multiple rows of a DataLog to CSV.
 * 
 * This function first writes the default header, then iterates over rows in the DataLog,
 * writing each one to the CSV.
 * 
 * Arguments:
 * 
 *   csv: the csv handle
 *   start: index of the first row to write (NOTE: indexing starts at 1)
 *   n: number of rows to write
 *   progress_fn: progress callback function (NULL to disable this feature)
 *   user_data: arbitrary data to be passed back to the progress callback function
 */
void csv_convert(CSV csv, uint32_t start, uint32_t n, progress_fn_t progress_fn, void *user_data);


#ifdef __cplusplus
}
#endif

#endif
