#ifndef _LCM_H
#define _LCM_H

#include <glib.h>
#include <stdint.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 an array dimension type (e.g., "int*_t").
int lcm_is_array_dimension_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
