#ifndef _LCM_H
#define _LCM_H

#include <stdint.h>
#include <glib.h>

#include "getopt.h"

/////////////////////////////////////////////////

#ifndef g_ptr_array_size
#define g_ptr_array_size(x) ((x)->len)
#endif

/////////////////////////////////////////////////
// lcm_typename_t: represents the name of a type, including package
//
//	Originally, the first field in the lcm_typename was named typename - which is a C++
//	keyword and caused much grief. Renamed to lctypename.
typedef struct lcm_typename lcm_typename_t;

struct lcm_typename
{
  	char *lctypename; // fully-qualified name, e.g., "edu.mit.dgc.laser_t"
    char *package;    // package name, e.g., "edu.mit.dgc"
    char *shortname;  // e.g., "laser_t"
};

/////////////////////////////////////////////////
// lcm_dimension_t: represents the size of a dimension of an
//                  array. The size can be either dynamic (a variable)
//                  or a constant.
//
typedef enum { LCM_CONST, LCM_VAR } lcm_dimension_mode_t;

typedef struct lcm_dimension lcm_dimension_t;

struct lcm_dimension
{
	lcm_dimension_mode_t mode;
	char *size;                // a string containing either a member variable name or a constant
};

/////////////////////////////////////////////////
// lcm_member_t: represents one member of a struct, including (if its
//               an array), its dimensions.
//
typedef struct lcm_member lcm_member_t;

struct lcm_member
{
    lcm_typename_t *type;
	char           *membername;

	// an array of lcm_dimension_t. A scalar is a 1-dimensional array
	// of length 1.
	GPtrArray *dimensions;

  // Comments in the LCM type definition immediately before a member
  // declaration are attached to that member
  char* comment;
};

/////////////////////////////////////////////////
// lcm_struct_t: a first-class LCM object declaration
//
typedef struct lcm_struct lcm_struct_t;

struct lcm_struct
{
  lcm_typename_t *structname; // name of the data type

  GPtrArray *members;  // lcm_member_t

  // recursive declaration of structs and enums
  GPtrArray *structs;  // lcm_struct_t
  GPtrArray *enums;    // locally-declared enums  DEPRECATED
  GPtrArray *constants; // lcm_constant_t

  char *lcmfile;       // file/path of function that declared it
  int64_t hash;

  // Comments in the LCM type defition immediately before a struct is declared
  // are attached to that struct.
  char* comment;
};

/////////////////////////////////////////////////
// lcm_constant_: the symbolic name of a constant and its value.
//
typedef struct lcm_constant lcm_constant_t;

struct lcm_constant
{
  char *lctypename;    // int8_t / int16_t / int32_t / int64_t / float / double
  char *membername;
  union {
    int8_t i8;
    int16_t i16;
    int32_t i32;
    int64_t i64;
    float f;
    double d;
  } val;
  char *val_str;   // value as a string, as specified in the .lcm file

  // Comments in the LCM type definition immediately before a constant are
  // attached to the constant.
  char* comment;
};

/////////////////////////////////////////////////
// DEPRECATED
// lcm_enum_value_t: the symbolic name of an enum and its constant
//                   value.
//
typedef struct lcm_enum_value lcm_enum_value_t;

struct lcm_enum_value
{
	char    *valuename;
	int32_t value;
};

/////////////////////////////////////////////////
// DEPRECATED
// lcm_enum_t: an enumeration, also a first-class LCM object.
//
typedef struct lcm_enum lcm_enum_t;
struct lcm_enum
{
    lcm_typename_t *enumname; // name of the enum
	
	GPtrArray *values;   // legal values for the enum
	char *lcmfile;      // file/path of function that declared it

    // hash values for enums are "weak". They only involve the name of the enum,
    // so that new enumerated values can be added without breaking the hash.
    int64_t hash;
};

/////////////////////////////////////////////////
// lcmgen_t: State used when parsing LCM declarations. The gopt is
//           essentially a set of key-value pairs that configure
//           various options. structs and enums are populated
//           according to the parsed definitions.
//
typedef struct lcmgen lcmgen_t;

struct lcmgen
{
    char      *package; // remembers the last-specified package name, which is prepended to other types.
    getopt_t  *gopt;
    GPtrArray *structs; // lcm_struct_t
    GPtrArray *enums;   // lcm_enum_t (declared at top level)

    gchar* comment_doc;
};

/////////////////////////////////////////////////
// Helper functions
/////////////////////////////////////////////////

// Returns 1 if the argument is a built-in type (e.g., "int64_t", "float").
int lcm_is_primitive_type(const char *t);

// Returns 1 if the argument is a legal constant type (e.g., "int64_t", "float").
int lcm_is_legal_const_type(const char *t);

// Returns the member of a struct by name. Returns NULL on error.
lcm_member_t *lcm_find_member(lcm_struct_t *lr, const char *name);

// Returns the constant of a struct by name. Returns NULL on error.
lcm_constant_t *lcm_find_const(lcm_struct_t *lr, const char *name);

// Returns 1 if the "lazy" option is enabled AND the file "outfile" is
// older than the file "declaringfile"
int lcm_needs_generation(lcmgen_t *lcmgen, const char *declaringfile, const char *outfile);

// create a new parsing context.
lcmgen_t *lcmgen_create();

// for debugging, emit the contents to stdout
void lcmgen_dump(lcmgen_t *lcm);

// parse the provided file
int lcmgen_handle_file(lcmgen_t *lcm, const char *path);

// Are all of the dimensions of this array constant?
// (scalars return 1)
int lcm_is_constant_size_array(lcm_member_t *lm);

#endif
