

#ifndef _SOMATSIE_
#define _SOMATSIE_

#include "file_utils.h"

#define SIE_NULL_ID   (~(sie_uint32)0)
#define SIE_SPIGOT_SEEK_END (~(size_t)0)
#define SIE_OUTPUT_NONE    0
#define SIE_OUTPUT_FLOAT64 1
#define SIE_OUTPUT_RAW     2

#define SIE_MAX_VALUE_SIZE 100
#define SIE_LONG_VALUE	""


typedef double sie_float64;
typedef unsigned int sie_uint32;

typedef void sie_Context;
typedef void sie_File;
typedef void sie_Iterator;
typedef void sie_Test;
typedef void sie_Tag;
typedef void sie_Spigot;
typedef void sie_Output;
typedef void sie_Channel;
typedef void sie_Dimension;
typedef void sie_Histogram;
typedef void sie_Exception;
typedef int sie_Progress_Set_Message;
typedef int sie_Progress_Percent;

typedef struct _sie_Output_Raw {
    unsigned char *ptr;
    size_t size;
    int claimed;
} sie_Output_Raw;

typedef struct _sie_Output_Dim {
    int type;
    sie_float64 *float64;
    sie_Output_Raw *raw;
} sie_Output_Dim;

typedef struct _sie_Output_Struct {
    size_t num_dims;
    size_t num_rows;
    size_t block;
    size_t row_offset;
    sie_Output_Dim *dim;
} sie_Output_Struct;


class SIEFile: public BinFile
{
private:
	sie_Context *m_pcontext;
	sie_File *m_pfileSIE;
	sie_Exception *m_pexception;
	int ReadTags(void*, TreeNode&);
public:
	SIEFile(LPCSTR lpcszFileName):BinFile(lpcszFileName)
	{ 
		m_strFileName = lpcszFileName;

		// Load the Sie File
		m_pcontext = sie_context_new();
		m_pfileSIE = sie_file_open(m_pcontext, m_strFileName);
	}
	~SIEFile()
	{
		int nleaked_objects;
		sie_release(m_pfileSIE);

		if (sie_check_exception(m_pcontext)){
			m_pexception = sie_get_exception(m_pcontext);
			printf("Something bad happened:\n  %s\n",
				sie_verbose_report(m_pexception));
			sie_release(m_pexception);
		}

		nleaked_objects = sie_context_done(m_pcontext);
		if (nleaked_objects != 0)
			printf("Warning:  Leaked %d SIE objects!\n", nleaked_objects);
	}
	int Import(TreeNode& trOption, TreeNode& trSelection=NULL, Worksheet& ws=NULL);

	int ReadHeader(Tree&);
};


#ifndef __cplusplus
	#pragma dll(libapr-1)
	#pragma dll(libsie)
#endif	//__cplusplus

/// end USING_VERSION_2_DLL_IN_OC
#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */

#pragma pack(push,1)


/* > Returns a new library context. */
sie_Context *sie_context_new(void);


/* > "Retains" `object` by raising its reference count by one. */
void *sie_retain(void *object);


/* > "Releases" `object`, lowering its reference count by one.  If its
 * > count reaches zero, the object is freed from memory. */
void sie_release(void *object);


/* > Attempts to release the library context `context`.  If
 * > successful, zero is returned.  Otherwise, the number returned is
 * > the number of objects that still have dangling references to them
 * > and as such were not freed. */
int sie_context_done(sie_Context *context);


/* > Opens an SIE file, returning a file object. */
/*
 * A file is a *reference*, as are tests, channels, dimensions, and
 * tags.  There are a set of common methods for references:
 */
sie_File *sie_file_open(void *context_object, const char *name);


/* > For files, returns an iterator containing all tests (as
 * > `sie_Test` objects) in the file.  Not applicable for any other
 * > type - returns `NULL`. */
sie_Iterator *sie_get_tests(void *reference);


/* > For files, returns an iterator containing all channels (as
 * > `sie_Channel` objects) in the file. For tests, returns an
 * > iterator containing all channels (as `sie_Channel` objects) in
 * > the test.  Not applicable for any other type - returns `NULL`. */
sie_Iterator *sie_get_channels(void *reference);


/* > For channels, returns an iterator containing all dimensions (as
 * > `sie_Dimension` objects) in the channel.  Not applicable for any
 * > other type - returns `NULL`. */
sie_Iterator *sie_get_dimensions(void *reference);


/* > For files, returns all toplevel tags in the file. For tests,
 * > channels, and dimensions, returns all tags in the requested
 * > object.  Not valid for tags - returns `NULL`. */
sie_Iterator *sie_get_tags(void *reference);


/* > For channels, returns the test object representing the test the
 * > channel is a member of, if any.  Not applicable for any other
 * > type - returns `NULL`. */
sie_Test *sie_get_containing_test(void *reference);


/* > For channels, returns the name of the channel.  Not applicable
 * > for any other type - returns `NULL`. */
const char *sie_get_name(void *reference);


/* > For tests and channels, returns the id.  Not applicable for any
 * > other type - returns `SIE_NULL_ID`. */
sie_uint32 sie_get_id(void *reference);


/* > For dimensions, returns the index of the dimension.  Not
 * > applicable for any other type - returns `SIE_NULL_ID`. */
/*
 * The iterator interface is simple: 
 */
sie_uint32 sie_get_index(void *reference);


/* > Returns the next object from the iterator.  The returned object
 * > is "owned" by the spigot, is valid until the next call to
 * > `sie_iterator_next`, and does not need to be released.  If the
 * > object will be referenced after the next call to
 * > `sie_iterator_next` or the release of the iterator, it must be
 * > retained with `sie_retain()` and later released. */
void *sie_iterator_next(void *iterator);


/* > Returns the id (key) of `tag`.  The returned string is valid for
 * > the lifetime of the tag object - if needed longer, it must be
 * > `strdup()`'d or otherwise reallocated. */
const char *sie_tag_get_id(sie_Tag *tag);


/* > Returns a newly-allocated string containing the tag value of
 * > `tag`.  Because the amount of data in a tag value can be
 * > potentially huge, it can also be read with a spigot, as described
 * > below.  The string must be `free()`'d by the caller.  If the libc
 * > `free()` routine is not easy to access libsie provides
 * > `sie_system_free()` to use instead. */
char *sie_tag_get_value(sie_Tag *tag);


/* > KLUDGE document! */
int sie_tag_get_value_b(sie_Tag *tag, char **value, size_t *size);


/* > Calls free() on `pointer`.  This function is suitable for
 * > disposing of the return value of `sie_tag_get_value` if the libc
 * > `free()` function is not easy to access for some reason. */
void sie_system_free(void *pointer);


/* > Attaches a spigot to `reference` in preparation for reading
 * > data. */
sie_Spigot *sie_attach_spigot(void *reference);


/* > Reads the next output record out of `spigot`.  If it returns
 * > `NULL`, all data has been read.  The output record is "owned" by
 * > the spigot, is valid until the next call to `sie_spigot_get`, and
 * > does not need to be released.  If the output will be referenced
 * > after the next call to `sie_spigot_get` or the release of the
 * > spigot, it must be retained with `sie_retain()` and later
 * > released. */
sie_Output *sie_spigot_get(sie_Spigot *spigot);


/* > If `disable` is true, adjusts `spigot` such that data returned
 * > will not be transformed by sie xform nodes.  This typically means
 * > that raw decoder output will be returned instead of engineering
 * > values.  This can be useful, as many data schemas have dimension
 * > 0 being "time" when scaled, and "sample count" when
 * > unscaled. Setting the spigot to unscaled and binary searching
 * > dimension 0 can be used to find a particular sample number.  This
 * > is currently applicable only to channels. */
size_t sie_spigot_disable_transforms(void *spigot, int disable);


/* > Prepares `spigot` such that the next call to `sie_spigot_get`
 * > will return the data in the block `target`.  If the target is
 * > past the end of the data in the file, it will be set to the end
 * > of the data - i.e., one block after the last one (and calling
 * > `sie_spigot_get` immediately after will return `NULL` indicating
 * > the end of the data).  Returns the block position that was set.
 * > `SIE_SPIGOT_SEEK_END`, defined as "all ones" (i.e., 0xffffffff on
 * > 32-bit platforms) is provided as a convenient way to seek to the
 * > end of a file. */
size_t sie_spigot_seek(void *spigot, size_t target);


/* > Returns the current block position of `spigot` - i.e. the block
 * > that the next call to `sie_spigot_get` will return. */
size_t sie_spigot_tell(void *spigot);


/* > Given a spigot `spigot` to data for which the dimension specified
 * > by `dim` is non-decreasing, find and return the block and scan
 * > within the block where the value of the specified dimension is
 * > first greater or equal to `value`.  Returns true if a value is
 * > found (and sets `block` and `scan` to the found value), and
 * > returns false if the last point in the data is less than the
 * > value, or if some other error occurred.  If true, `block` and
 * > `scan` are always returned such that seeking to `block`, calling
 * > `sie_spigot_get`, and getting the scan number `scan` in that
 * > block will always be the first value in that dimension greater or
 * > equal to the search value.  The block the spigot is currently
 * > pointing at (see `sie_spigot_tell`) will not be affected. */
int sie_binary_search(void *spigot, size_t dim, sie_float64 value,
                      size_t *block, size_t *scan);


/* > Returns the block number from which the data originated (relative
 * > to the data source - i.e., the first data in a channel is always
 * > block 0.) */
size_t sie_output_get_block(sie_Output *output);


/* > Returns the number of dimensions in `output`. */
size_t sie_output_get_num_dims(sie_Output *output);


/* > Returns the number of rows of data in `output`. */
size_t sie_output_get_num_rows(sie_Output *output);


/* > Returns the type of the specified dimension of `output`.  This
 * > is one of: */
int sie_output_get_type(sie_Output *output, size_t dim);


/* > Returns a pointer to an array of float64 (double) data for the
 * > specified dimension.  This array has a size equal to the number
 * > of scans in the output.  This is only valid if the type of the
 * > dimension is `SIE_OUTPUT_FLOAT64`. */
sie_float64 *sie_output_get_float64(sie_Output *output, size_t dim);


/* > As `sie_output_get_float64`, but for raw data.  The `ptr` member
 * > of the `sie_Output_Raw` struct is a pointer to the actual data,
 * > `size` is the size of the data pointed at by `ptr`, in bytes.  By
 * > setting `claimed` to 1, the user is responsible for freeing the
 * > memory pointed at by `ptr`.  Otherwise, libsie will recover
 * > it.  */
sie_Output_Raw *sie_output_get_raw(sie_Output *output, size_t dim);


/*
 * For information about the SIE data model, read the appropriate
 * section of 'The SIE Format' paper and any applicable schema
 * documentation.
 */
sie_Output_Struct *sie_output_get_struct(sie_Output *output);

 	
/* > `sie_check_exception` returns NULL if no exception has happened
 * > since library initialization or the last call to
 * > `sie_get_exception`.  Otherwise, it returns a non-NULL
 * > pointer. */
sie_Exception *sie_check_exception(void *ctx_obj);


/* > `sie_get_exception` returns NULL if no exception has happened
 * > since library initialization or the last call to
 * > `sie_get_exception`.  Otherwise, it returns the exception object.
 * > The caller is responsible for releasing the exception object when
 * > they are done with it. */
sie_Exception *sie_get_exception(void *ctx_obj);


/* > `sie_report` returns a string describing an exception.  The
 * > string returned is valid for the lifetime of the exception object
 * > passed to `sie_report`, and does not have to be freed by the
 * > user. */
char *sie_report(void *exception);


/* > `sie_verbose_report` returns a string describing an exception,
 * > plus extra information describing what was happening when the
 * > exception occurred.  The string returned is valid for the
 * > lifetime of the exception object passed to `sie_report`, and does
 * > not have to be freed by the user. */
char *sie_verbose_report(void *exception);


/* > By default, the library will refuse to open SIE files with any
 * > detectable corruption.  `sie_file_open` will return NULL and the
 * > library exception will be set with an explanation of the error.
 * > However, a somewhat common corruption is a file truncated in the
 * > middle of a block.  This can happen when reading a file that is
 * > being written at the same time.  `sie_ignore_trailing_garbage`
 * > tells the library to open the file anyway as long as it finds a
 * > valid block in the last `amount` bytes of the file. */
void sie_ignore_trailing_garbage(void *ctx_obj, size_t amount);


/*
 * Progress information
 * --------------------
 *
 * Some operations, such as sie_file_open on very large SIE files, can
 * take enough time that a GUI may want to provide progress
 * information.  The following interface allows one to configure the
 * SIE library context to provide information on the progress of libsie
 * activities.
 *
 * If a callback returns non-zero, the current API function will be
 * aborted.  The API function will return a failure value and an
 * "operation aborted" exception.
 */
void sie_set_progress_callbacks(void *ctx_obj, void *data, sie_Progress_Set_Message *set_message_callback, sie_Progress_Percent *percent_callback);


/* > Quickly tests to see if the file specified by `name` looks like
 * > an SIE file.  Returns non-zero if it looks like an SIE file, or
 * > zero otherwise. */
int sie_file_is_sie(void *ctx_obj, const char *name);


/*
 * Histogram access
 * ================
 * 
 * Histograms are presented with a data schema which is comprehensive but
 * somewhat inconvenient to access.  However, libsie provides a utility
 * for reconstructing a more traditional representation.
 * 
 * The SoMat histogram data schema for each bin is:
 * 
 *     dim 0: count
 *     dim 1: dimension 0 lower bound
 *     dim 2: dimension 0 upper bound
 *     dim 3: dimension 1 lower bound
 *     dim 4: dimension 1 upper bound
 *     ...
 * 
 * with as many dimensions as are present in the histogram.  If a bin is
 * repeated, the new count replaces the old one.  This presents all the
 * data needed to reconstruct the histogram in one place.
 * 
 * In SoMat files, this schema is used whenever the "core:schema" tag is
 * "somat:histogram" (or "somat:rainflow", in which case this schema is
 * used with an additional tag of rainflow stack data).
 * 
 * To access a histogram in a more convenient way, use the following
 * interface:
 */
 
 
/* > Create a new histogram convenience object from the specified
 * > channel.  This will read all data from the channel.  The
 * > following accessors can then be called on the histogram
 * > object. */
sie_Histogram *sie_histogram_new(sie_Channel *channel);


/* > Returns the number of dimensions in the histogram. */
size_t sie_histogram_get_num_dims(sie_Histogram *hist);


/* > Returns the number of bins in the specified dimension of the
 * > histogram. */
size_t sie_histogram_get_num_bins(sie_Histogram *hist, size_t dim);


/* > Fills the arrays of 64-bit floats `lower` and `upper` with the
 * > lower and upper bounds of the bins in the specified dimension.
 * > The arrays must have enough space for the number of bins in the
 * > dimension (see the `sie_histogram_get_num_bins` function). */
void sie_histogram_get_bin_bounds(sie_Histogram *hist, size_t dim,
                                  sie_float64 *lower, sie_float64 *upper);


/* > Get the bin value for the specified indices.  `indices` must
 * > point to an array of `size_t` of a size being the number of
 * > dimensions of the histogram. */
sie_float64 sie_histogram_get_bin(sie_Histogram *hist, size_t *indices);


/* > Starting with absolute bin position `start`, find the next
 * > non-zero bin.  Returns the bin value, sets `indices` to the
 * > indices of the found bin, and `start` to a value such that it can
 * > be used in a future invocation of this function to continue the
 * > search with the bin after the found bin.  `start` should point to
 * > a zero value to start a new search.  `indices` must point to an
 * > array of size_t of a size being the number of dimensions of the
 * > histogram.  There are no more non-zero bins when this function
 * > returns 0.0. */
sie_float64 sie_histogram_get_next_nonzero_bin(sie_Histogram *hist,
                                               size_t *start,
                                               size_t *indices);


#ifdef __cplusplus
}
#endif  /* __cplusplus */
