#include "utils.h"
#include <tcl.h>

#ifndef __TCL_H__
#define __TCL_H__

#define TCL_FUNCTION(name, callback)                                         \
		C_ERROR((Tcl_CreateObjCommand(tcl, name, callback, NULL, NULL) == NULL), \
			"Error creating " name "command")			


#define Tcl_SetStringResult(interp, ...) \
  Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), __VA_ARGS__ , (char*)NULL)


#define Tcl_SetIntResult(interp, value) \
	Tcl_SetObjResult(interp, Tcl_NewIntObj(value))

#define Tcl_SetLongResult(interp, value) \
	Tcl_SetObjResult(interp, Tcl_NewLongObj(value))

#define TCL_CATCH(value, ...)                   \
	do{                                           \
		if( (value) != TCL_OK) {                    \
			Tcl_SetStringResult(interp, __VA_ARGS__); \
			return TCL_ERROR;                         \
		}                                           \
	}while (0)

#endif

/**
 * This overly complicated code below is to generate struct + TCL convert methods from one single definition in header file
 */
#undef TCL_OBJECT
#undef TCL_ITEM
#undef TCL_ENUM
#undef TCL_ENUM_ITEM

#ifdef __H__

#define TCL_OBJECT(Name, items) \
typedef struct        \
  items               \
t_##Name;

#define TCL_ITEM(Name, Type, ItemName)\
  Type ItemName;

#define TCL_ENUM(Name, items)\
typedef enum                 \
  items                      \
t_##Name;

#define TCL_ENUM_ITEM(Name) \
  Name, 

#else

#define TCL_OBJECT(Name, items)                                   \
  int Struct2Tcl_##Name(Tcl_Interp *interp, t_##Name *input, Tcl_Obj *result) {\
    items                                                         \
    return TCL_OK;                                                \
  }

#define TCL_CONVERT_int(x)  Tcl_NewIntObj(x)
#define TCL_CONVERT_long(x) Tcl_NewLongObj(x)
#define TCL_CONVERT_bool(x) Tcl_NewIntObj(x)


#define Q1(x) #x
#define Q(x) Q1(x)

#define TCL_ITEM(Name, Type, ItemName)\
  TCL_CATCH(Tcl_DictObjPut(interp, result, Tcl_NewStringObj(Q(ItemName), -1), TCL_CONVERT_##Type(input->ItemName)), "Addition to " Q(Name) "." Q(ItemName) "failed");


#define TCL_ENUM(Name, items) \
  static Tcl_Obj *TCL_CONVERT_t_##Name(t_##Name a) {\
    switch (a)                                    \
      items                                       \
    C_ERROR(0, "Default branch in TCL_CONVERT_t_" Q(Name) " triggered! Unsuppored item in enum data type");\
    exit(1);                                                                                               \
  }

#define TCL_ENUM_ITEM(Name) \
  case Name: return Tcl_NewStringObj(Q(Name),-1); 

#endif
