// vim:fileencoding=utf-8:foldmethod=marker

#ifndef DATATYPES_H
#define DATATYPES_H

#include "wapp_core.h"
#include "dbl_list.h"
#include "type_enums.h"

#ifndef CCGEN_BUF_MIN
#define CCGEN_BUF_MIN 16
#endif // !CCGEN_BUF_MIN

#ifndef CCGEN_BUF_TINY
#define CCGEN_BUF_TINY 32
#endif // !CCGEN_BUF_TINY

#ifndef CCGEN_BUF_SMALL
#define CCGEN_BUF_SMALL 512
#endif // !CCGEN_BUF_SMALL

#ifndef CCGEN_BUF_MEDIUM
#define CCGEN_BUF_MEDIUM 1024
#endif // !CCGEN_BUF_MEDIUM

#ifndef CCGEN_BUF_LARGE
#define CCGEN_BUF_LARGE 4096
#endif // !CCGEN_BUF_LARGE

#ifndef CCGEN_BUF_MAX
#define CCGEN_BUF_MAX 8192
#endif // !CCGEN_BUF_MAX

#define CENUM(NAME, VALUES, TYPEDEF) ((CEnum){ .name = (NAME), .values = (VALUES), .add_typedef = (TYPEDEF) })
#define CSTRUCT(NAME, ARGS, TYPEDEF_NAME_PTR) ((CStruct){ .name = (NAME), .args = (ARGS), .typedef_name = (TYPEDEF_NAME_PTR) })

#define CUSERTYPE_ENUM(ENUM) ((CUserType){ .kind = CUSERTYPE_CENUM, .type.c_enum = ENUM })
#define CUSERTYPE_STRUCT(STRUCT) ((CUserType){ .kind = CUSERTYPE_CSTRUCT, .type.c_struct = STRUCT })

#define CDATATYPE_CTYPE(VALUE) ((CDataType){ .kind = CDATATYPE_CTYPE, .type.c_type = (VALUE) })
#define CDATATYPE_USERTYPE(USERTYPE) ((CDataType){ .kind = CDATATYPE_CUSERTYPE, .type.c_usertype = (USERTYPE) })
#define CDATATYPE_STR(STR) ((CDataType){ .kind = CDATATYPE_STR, .type.str = (STR) })

#define CHEADERINCLUDE_STR(NAME) ((CHeaderInclude){ .kind = C_HEADER_INCLUDE_STR, .header.name = (NAME) })
#define CHEADERINCLUDE_CHEADER(CHEADER) ((CHeaderInclude){ .kind = C_HEADER_INCLUDE_HEADER, .header.header = (CHEADER) })

#define COBJECT_TYPE(VALUE)                                                    \
  ((CObject){ .kind = COBJECT_CTYPE, .object.c_type = (VALUE) })
#define COBJECT_QUALIFIER(VALUE)                                               \
  ((CObject){ .kind = COBJECT_CQUALIFIER, .object.c_qualifier = (VALUE) })
#define COBJECT_POINTERTYPE(VALUE)                                             \
  ((CObject){ .kind = COBJECT_CPOINTERTYPE, .object.c_pointertype = (VALUE) })
#define COBJECT_POINTER(CPOINTER)                                              \
  ((CObject){ .kind = COBJECT_CPOINTER, .object.c_pointer = (CPOINTER) })
#define COBJECT_ENUMVAL(CENUMVAL)                                              \
  ((CObject){ .kind = COBJECT_CENUMVAL, .object.c_enumval = (CENUMVAL) })
#define COBJECT_ENUM(ENUM)                                                     \
  ((CObject){ .kind = COBJECT_CENUM, .object.c_enum = (ENUM) })
#define COBJECT_MACRO(CMACRO)                                                  \
  ((CObject){ .kind = COBJECT_CMACRO, .object.c_macro = (CMACRO) })
#define COBJECT_STRUCT(STRUCT)                                                 \
  ((CObject){ .kind = COBJECT_CSTRUCT, .object.c_struct = STRUCT })
#define COBJECT_USERTYPE(USERTYPE)                                             \
  ((CObject){ .kind = COBJECT_CUSERTYPE, .object.c_usertype = (USERTYPE) })
#define COBJECT_DATATYPE(DATATYPE)                                             \
  ((CObject){ .kind = COBJECT_CDATATYPE, .object.c_datatype = (DATATYPE) })
#define COBJECT_ARG(CARG)                                                      \
  ((CObject){ .kind = COBJECT_CARG, .object.c_arg = (CARG) })
#define COBJECT_FUNC(CFUNC)                                                    \
  ((CObject){ .kind = COBJECT_CFUNC, .object.c_func = (CFUNC) })
#define COBJECT_INCULDE(CINCLUDE)                                              \
  ((CObject){ .kind = COBJECT_CINCULDE, .object.c_include = (CINCLUDE) })
#define COBJECT_HEADER(CHEADER)                                                \
  ((CObject){ .kind = COBJECT_CHEADER, .object.c_header = (CHEADER) })
#define COBJECT_SOURCE(CSOURCE)                                                \
  ((CObject){ .kind = COBJECT_CSOURCE, .object.c_source = (CSOURCE) })

typedef enum {
  CUSERTYPE_CENUM,
  CUSERTYPE_CSTRUCT,

  COUNT_CUSERTYPEKIND,
} CUserTypeKind;

typedef enum {
  CDATATYPE_CTYPE,
  CDATATYPE_CUSERTYPE,
  CDATATYPE_STR,

  COUNT_CDATATYPEKIND,
} CDataTypeKind;

typedef enum {
  CFILE_EXT_H,
  CFILE_EXT_C,

  COUNT_CFILE_EXT,
} CFileExtension;

typedef enum {
  C_HEADER_INCLUDE_STR,
  C_HEADER_INCLUDE_HEADER,

  COUNT_CHEADERINCLUDEKIND,
} CHeaderIncludeKind;

typedef enum {
  COBJECT_CTYPE,
  COBJECT_CQUALIFIER,
  COBJECT_CPOINTERTYPE,
  COBJECT_CPOINTER,
  COBJECT_CENUMVAL,
  COBJECT_CENUM,
  COBJECT_CMACRO,
  COBJECT_CSTRUCT,
  COBJECT_CUSERTYPE,
  COBJECT_CDATATYPE,
  COBJECT_CARG,
  COBJECT_CFUNC,
  COBJECT_CINCULDE,
  COBJECT_CHEADER,
  COBJECT_CSOURCE,

  COUNT_COBJECTKIND,
} CObjectKind;

typedef struct cpointer        CPointer;
typedef struct cenumval        CEnumVal;
typedef struct cenum           CEnum;
typedef struct cmacro          CMacro;
typedef struct cstruct         CStruct;
typedef struct cusertype       CUserType;
typedef struct cdatatype       CDataType;
typedef struct carg            CArg;
typedef struct cfunc           CFunc;
typedef struct cheader_include CHeaderInclude;
typedef struct cinclude        CInclude;
typedef struct cheader         CHeader;
typedef struct csource         CSource;
typedef struct cobject         CObject;

struct cpointer {
  CPointerType type;
  CQualifier qualifier;
};

struct cenumval {
  Str8 name;
  i32 *value;
};

struct cenum {
  Str8 name;
  CEnumValList values;
  b8 add_typedef;
};

struct cmacro {
  Str8 name;
  Str8 value;
};

struct cstruct {
  Str8 name;
  CArgList args;
  Str8 typedef_name;
};

struct cusertype {
  CUserTypeKind kind;
  union {
    CEnum c_enum;
    CStruct c_struct;
  } type;
};

struct cdatatype {
  CDataTypeKind kind;
  union {
    CType c_type;
    CUserType c_usertype;
    Str8 str;
  } type;
};

struct carg {
  Str8 name;
  CDataType type;
  CPointer pointer;
  CQualifier qualifier;
  b8 is_array;
};

struct cfunc {
  Str8 name;
  CDataType ret_type;
  CArgList args;
  Str8 body;
  CPointer pointer;
  CQualifierList qualifiers;
};

#define CFILE_ARGS          \
  Str8 name;                \
  CFileExtension extension; \
  CIncludeList includes;    \
  CUserTypeList types;      \
  CFuncList funcs;          \
  CStructList decl_types;   \
  CMacroList macros;        \
  CMacroList c_macros;      \
  CMacroList cpp_macros     \

struct cheader {
  CFILE_ARGS;
};

struct csource {
  CFILE_ARGS;
  CFuncList internal_funcs;
};

struct cheader_include {
  CHeaderIncludeKind kind;
  union {
    Str8 name;
    CHeader header;
  } header;
};

struct cinclude {
  CHeaderInclude header;
  b8 is_local;
  b8 same_dir;
};

struct cobject {
  CObjectKind kind;
  union {
    CType c_type;
    CQualifier c_qualifier;
    CPointerType c_pointertype;
    CPointer c_pointer;
    CEnumVal c_enumval;
    CEnum c_enum;
    CMacro c_macro;
    CStruct c_struct;
    CUserType c_usertype;
    CDataType c_datatype;
    CArg c_arg;
    CFunc c_func;
    CInclude c_include;
    CHeader c_header;
    CSource c_source;
  } object;
};

void cobject_to_string(Str8 *dst, const CObject *object);
void ctype_to_string(Str8 *dst, CType ctype);
void cqualifier_to_string(Str8 *dst, CQualifier cqualifier);
void cpointertype_to_string(Str8 *dst, CPointerType cpointertype);
void cpointer_to_string(Str8 *dst, const CPointer *cpointer);
void cenumval_to_string(Str8 *dst, const CEnumVal *cenumval);
void cenum_to_string(Str8 *dst, const CEnum *cenum);
void cmacro_to_string(Str8 *dst, const CMacro *cmacro);
void cstruct_to_string(Str8 *dst, const CStruct *cstruct);
void declare_cstruct(Str8 *dst, const CStruct *cstruct);
void define_cstruct(Str8 *dst, const CStruct *cstruct);
void cusertype_to_string(Str8 *dst, const CUserType *cusertype);
void cdatatype_to_string(Str8 *dst, const CDataType *cdatatype);
void carg_to_string(Str8 *dst, const CArg *carg);
void cfunc_to_string(Str8 *dst, const CFunc *cfunc);
void declare_cfunc(Str8 *dst, const CFunc *cfunc);
void define_cfunc(Str8 *dst, const CFunc *cfunc);
void cinclude_to_string(Str8 *dst, const CInclude *cinclude);
void cheader_to_string(Str8 *dst, const CHeader *cheader);
void csource_to_string(Str8 *dst, const CSource *csource);
b8 cheaderinclude_to_string(Str8 *dst, const CHeaderInclude *cheaderinclude);

#endif // !DATATYPES_H
