#ifndef _LABCOMM_H_
#define _LABCOMM_H_

#ifdef ARM_CORTEXM3_CODESOURCERY
  #include <machine/endian.h>
#else
  #include <endian.h>
#endif

// Some projects can not use stdio.h.
#ifndef LABCOMM_NO_STDIO
  #include <stdio.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

/* Forward declaration */
struct labcomm_encoder;
struct labcomm_decoder;

/*
 * Signature entry
 */
typedef struct labcomm_signature{
  int type;
  char *name;
  int (*encoded_size)(struct labcomm_signature *, void *); // void * == encoded_sample *
  int size;
  unsigned char *signature; 
  int cached_encoded_size; // -1 if not initialized or type is variable size
} labcomm_signature_t;

/*
 * Error handling.
 */

/* Error IDs */
enum labcomm_error {
  LABCOMM_ERROR_ENUM_BEGIN_GUARD,	// _must_ be the first enum element. labcomm_error_get_str() depends on this.
  LABCOMM_ERROR_ENC_NO_REG_SIGNATURE, 	
  LABCOMM_ERROR_ENC_MISSING_DO_REG,
  LABCOMM_ERROR_ENC_MISSING_DO_ENCODE,
  LABCOMM_ERROR_ENC_BUF_FULL,
  LABCOMM_ERROR_DEC_MISSING_DO_REG,
  LABCOMM_ERROR_DEC_MISSING_DO_DECODE_ONE,
  LABCOMM_ERROR_DEC_UNKNOWN_DATATYPE,
  LABCOMM_ERROR_DEC_INDEX_MISMATCH,
  LABCOMM_ERROR_DEC_TYPE_NOT_FOUND,
  LABCOMM_ERROR_UNIMPLEMENTED_FUNC,
  LABCOMM_ERROR_MEMORY,
  LABCOMM_ERROR_USER_DEF,			
  LABCOMM_ERROR_ENUM_END_GUARD		// _must_ be the last enum element. labcomm_error_get_str() depends on this.
};

/* Error strings. _must_ be the same order as in enum labcomm_error */
extern const char *labcomm_error_strings[];

/* The callback prototype for error handling.
 * First parameter is the error ID.
 * The second paramters is the number of va_args that comes after this 
 * one. If none it should be 0.
 * Optionaly other paramters can be supplied depending on what is needed 
 * for this error ID.
 */
typedef void (*labcomm_error_handler_callback)(enum labcomm_error error_id, 
					       size_t nbr_va_args, ...); 

/* Default error handler, prints message to stderr. 
 * Extra info about the error can be supplied as char* as VA-args. Especially user defined errors should supply a describing string. if nbr_va_args > 1 the first variable argument must be a printf format string and the possibly following arguments are passed as va_args to vprintf. 
 */
void on_error_fprintf(enum labcomm_error error_id, size_t nbr_va_args, ...);

/* Register a callback for the error handler for this encoder. */
void labcomm_register_error_handler_encoder(struct labcomm_encoder *encoder, labcomm_error_handler_callback callback);

/* Register a callback for the error handler for this decoder. */
void labcomm_register_error_handler_decoder(struct labcomm_decoder *decoder, labcomm_error_handler_callback callback);

/* Get a string describing the supplied standrad labcomm error. */
const char *labcomm_error_get_str(enum labcomm_error error_id);

typedef int (*labcomm_handle_new_datatype_callback)(
  struct labcomm_decoder *decoder,
  labcomm_signature_t *sig);

void labcomm_decoder_register_new_datatype_handler(struct labcomm_decoder *d,
		labcomm_handle_new_datatype_callback on_new_datatype);


/*
 * Decoder
 */

typedef enum { 
  labcomm_reader_alloc, 
  labcomm_reader_free,
  labcomm_reader_start, 
  labcomm_reader_continue, 
  labcomm_reader_end,
  labcomm_reader_ioctl
} labcomm_reader_action_t;

typedef struct labcomm_reader {
  void *context;
  unsigned char *data;
  int data_size;
  int count;
  int pos;
  int (*read)(struct labcomm_reader *, labcomm_reader_action_t);
  int (*ioctl)(struct labcomm_reader *, int, va_list);
  labcomm_error_handler_callback on_error;
}  labcomm_reader_t;

struct labcomm_decoder *labcomm_decoder_new(
  int (*reader)(labcomm_reader_t *, labcomm_reader_action_t),
  void *reader_context);
int labcomm_decoder_decode_one(
  struct labcomm_decoder *decoder);
void labcomm_decoder_run(
  struct labcomm_decoder *decoder);
void labcomm_decoder_free(
  struct labcomm_decoder *decoder);

/*
 * Encoder
 */

typedef enum { 
  labcomm_writer_alloc,              /* Allocate all neccessary data */
  labcomm_writer_free,               /* Free all allocated data */
  labcomm_writer_start,              /* Start writing an ordinary sample */
  labcomm_writer_continue,           /* Buffer full during ordinary sample */
  labcomm_writer_end,                /* End writing ordinary sample */
  labcomm_writer_start_signature,    /* Start writing signature */
  labcomm_writer_continue_signature, /* Buffer full during signature */
  labcomm_writer_end_signature,      /* End writing signature */
} labcomm_writer_action_t;

typedef struct labcomm_writer {
  void *context;
  unsigned char *data;
  int data_size;
  int count;
  int pos;
  int error;
  int (*write)(struct labcomm_writer *, labcomm_writer_action_t, ...);
  int (*ioctl)(struct labcomm_writer *, int, va_list);
  labcomm_error_handler_callback on_error;
} labcomm_writer_t;

struct labcomm_encoder *labcomm_encoder_new(
  int (*writer)(labcomm_writer_t *, labcomm_writer_action_t, ...),
  void *writer_context);
void labcomm_encoder_free(
  struct labcomm_encoder *encoder);
/* See labcomm_ioctl.h for predefined ioctl_action values */
int labcomm_encoder_ioctl(struct labcomm_encoder *encoder, 
			  int ioctl_action,
			  ...);

void labcomm_encoder_start(struct labcomm_encoder *e,
                           labcomm_signature_t *s) ;

//HERE BE DRAGONS: is the signature_t* needed here?
void labcomm_encoder_end(struct labcomm_encoder *e,
                           labcomm_signature_t *s) ;
#endif