//
//  EXTADT.h
//  extobjc
//
//  Created by Justin Spahr-Summers on 19.06.12.
//  Copyright (C) 2012 Justin Spahr-Summers.
//  Released under the MIT license.
//

#import "EXTRuntimeExtensions.h"
#import "metamacros.h"

/**
 * Creates an algebraic data type with the given name and one or more data
 * constructors. Each constructor should be specified using the \c constructor()
 * macro, the first argument of which is the constructor name. Zero or more
 * parameters, which should look just like variable declarations (e.g., "int x")
 * may follow the constructor name.
 *
 * Among many other things, this can be used to create type-safe enumerations
 * which can be printed as strings (and optionally associated with data).
 *
 * This macro will create:
 *
 *  - A structure type NameT, where "Name" is the first argument to this macro.
 *  Instances of the structure will each contain a member "tag", which is filled
 *  in with the enum value of the constructor used to create the value.
 *  - A function NSStringFromName(), which will convert an ADT value into
 *  a human-readable string.
 *  - A function NameEqualToName(), which determines whether two ADT values are
 *  equal. For the purposes of the check, object parameters are compared with \c
 *  isEqual:.
 *  - And, for each constructor Cons:
 *      - An enum value Cons, which can be used to refer to that data
constructor.
 *      - A function Name.Cons(...), which accepts the parameters of the
 *      constructor and returns a new NameT structure.
 *      - Members (properties) for each of the constructor's named parameters,
 *      which can be used to get and set the data associated with that
 *      constructor.
 *
 * @code

// this invocation:
ADT(Color,
    constructor(Red),
    constructor(Green),
    constructor(Blue),
    constructor(Gray, double alpha),
    constructor(Other, double r, double g, double b)
);

// produces (effectively) these definitions:
typedef struct {
    enum { Red, Green, Blue, Gray, Other } tag;

    union {
        struct {
            double alpha;
        }

        struct {
            double r;
            double g;
            double b;
        }
    }
} ColorT;

NSString *NSStringFromColor (ColorT c);

ColorT Color.Red ();
ColorT Color.Green ();
ColorT Color.Blue ();
ColorT Color.Gray (double alpha);
ColorT Color.Other (double r, double g, double b);

 * @endcode
 *
 * @note Each constructor parameter must have a name that is unique among all of
 * the ADT's constructors, so that dot-syntax (e.g., "color.r") works without
 * needing to prefix the name of the data constructor (e.g., "color.Other.r").
 *
 * @note To define recursive types, ADT parameters can be pointers to the type
 * already being defined. For example, a parameter for the Color ADT is allowed
 * to be a pointer to a ColorT.
 *
 * @warning Accessing members that do not correspond to the structure's tag
 * (e.g., parameter data from other constructors) is considered undefined
 * behavior.
 *
 * @bug Currently, only up to nineteen data constructors are supported, and each
 * constructor may only have up to nineteen parameters. This is a limitation
 * imposed primarily by #metamacro_foreach_cxt.
 *
 * @bug An ADT value nested within another ADT value will not be very readable
 * when printed out with the generated NSStringFrom… function. All other data
 * will behave correctly.
 */
#define ADT(NAME, ...)                                                         \
  /* a type (NameT) for values defined by this ADT */                          \
  typedef struct ADT_CURRENT_T NAME##T;                                        \
                                                                               \
  /* create typedefs for all of the parameters types used with any constructor \
   */                                                                          \
  /* this will append ADT_typedef_ to each constructor() call, thus invoking   \
   * ADT_typedef_constructor() instead */                                      \
  metamacro_foreach_concat(ADT_typedef_, , __VA_ARGS__)                        \
                                                                               \
      struct ADT_CURRENT_T {                                                   \
    /* an enum listing all the constructor names for this ADT */               \
    /* this will also be how we know the type of this value */                 \
    const enum { metamacro_foreach_concat(ADT_enum_, , __VA_ARGS__) } tag;     \
                                                                               \
    /* overlapping storage for all the possible constructors */                \
    /* the tag above determines which parts of this union are in use */        \
    union {                                                                    \
      metamacro_foreach_concat(ADT_payload_, , __VA_ARGS__)                    \
    };                                                                         \
  };                                                                           \
                                                                               \
  /* defines the actual constructor functions for this type */                 \
  metamacro_foreach_concat(ADT_, , __VA_ARGS__)                                \
                                                                               \
      /* this structure is used like a simple namespace for the constructors:  \
       */ /* ColorT c = Color.Red(); */                                        \
      const struct {                                                           \
    /* as the structure definition, list the function pointers and names of    \
     * the constructors */                                                     \
    metamacro_foreach_concat(ADT_fptrs_, , __VA_ARGS__)                        \
  } NAME = {/* then fill them in with the actual function addresses */         \
            metamacro_foreach_concat(ADT_fptrinit_, , __VA_ARGS__)};           \
                                                                               \
  /* implements NSStringFromName(), to describe an ADT value */                \
  static inline NSString *NSStringFrom##NAME(NAME##T s) {                      \
    NSMutableString *str = [[NSMutableString alloc] init];                     \
                                                                               \
    /* only values with parameters will have braces added */                   \
    BOOL addedBraces = NO;                                                     \
                                                                               \
    /* construct the description differently depending on the constructor used \
     */                                                                        \
    switch (s.tag) {                                                           \
      metamacro_foreach_concat(ADT_tostring_, , __VA_ARGS__) default           \
          : return nil;                                                        \
    }                                                                          \
                                                                               \
    if (addedBraces)                                                           \
      [str appendString:@" }"];                                                \
                                                                               \
    return str;                                                                \
  }                                                                            \
                                                                               \
  /* implements NameEqualToName(), to compare ADT values for equality */       \
  static inline BOOL NAME##EqualTo##NAME(NAME##T a, NAME##T b) {               \
    if (a.tag != b.tag)                                                        \
      return NO;                                                               \
                                                                               \
    /* construct the check differently depending on the constructor used */    \
    switch (a.tag) {                                                           \
      metamacro_foreach_concat(ADT_equalto_, , __VA_ARGS__) default:;          \
    }                                                                          \
                                                                               \
    return YES;                                                                \
  }

/*** implementation details follow ***/

/*
 * This macro simply creates an enum entry for the given constructor name.
 */
#define ADT_enum_constructor(...)                                              \
  /* pop off the first variadic argument instead of using a named argument */  \
  /* (necessary because the ... in the argument list needs to match at least   \
   * one argument) */                                                          \
  metamacro_head(__VA_ARGS__),

/*
 * The "payload" terminology here refers to the data actually stored in an ADT
 * value, and more specifically to the data pertaining only to the constructor
 * that was used.
 *
 * These macros generate the internal layout of the NameT structure.
 *
 * For the following ADT:

ADT(Color,
    constructor(Red),
    constructor(Green),
    constructor(Blue),
    constructor(Gray, double alpha),
    constructor(Other, double r, double g, double b)
);

 * The resulting structure is laid out something like this:

typedef struct {
    const enum { Red, Green, Blue, Gray, Other } tag;

    union {
        // Gray
        struct {
            union {
                union {
                    double alpha;
                } Gray_payload_0;

                double alpha;
            };
        };

        // Other
        struct {
            union {
                union {
                    double r;
                } Other_payload_0;

                double r;
            };

            union {
                union {
                    double g;
                } Other_payload_1;

                double g;
            };

            union {
                union {
                    double b;
                } Other_payload_2;

                double b;
            };
        };
    };
} ColorT;

 * The use of anonymous structures and the containing anonymous union allows
 * a user to access parameter data without intervening steps (e.g.,
 * 'color.alpha', instead of 'color.Gray.alpha'), but does prevent two
 * parameters -- even for different constructors -- from having the same name.
 */
#define ADT_payload_constructor(...)                                                              \
  /* our first argument will always be the constructor name, so if we only                        \
   * have one argument, we only have a constructor */                                             \
  metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(                                            \
      /* this constructor has only a name, don't add any structures */)(/* create                 \
                                                                           structures             \
                                                                           for                    \
                                                                           the                    \
                                                                           parameters             \
                                                                           that                   \
                                                                           exist                  \
                                                                         */                       \
                                                                        ADT_payload_constructor_( \
                                                                            __VA_ARGS__))

#define ADT_payload_constructor_(CONS, ...)                                    \
  struct {                                                                     \
    metamacro_foreach_cxt_recursive(ADT_payload_entry_iter, , CONS,            \
                                    __VA_ARGS__)                               \
  };

#define ADT_payload_entry_iter(INDEX, CONS, PARAM)                             \
  ADT_CURRENT_CONS_UNION_T(CONS, INDEX, PARAM);

/*
 * The next few macros create type definitions for the unions used for each
 * parameter in the current ADT (e.g., those within the example structures
 * above).
 *
 * We do this so that instead of a type and name for each parameter (e.g.,
 * 'double alpha'), we can manipulate a union (e.g., 'Gray_payload_0') that
 * matches the type, but has a name we define.
 *
 * The type definitions are later used for function parameters created by
 * ADT_prototype_iter().
 */
#define ADT_typedef_constructor(...)                                           \
  /* our first argument will always be the constructor name, so if we only     \
   * have one argument, we only have a constructor */                          \
  metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(                         \
      /* no parameters, don't add any typedefs */)(/* there are actually       \
                                                      parameters */            \
                                                   ADT_typedef_constructor_(   \
                                                       __VA_ARGS__))

#define ADT_typedef_constructor_(CONS, ...)                                    \
  metamacro_foreach_cxt_recursive(ADT_typedef_iter, , CONS, __VA_ARGS__)

#define ADT_typedef_iter(INDEX, CONS, PARAM)                                   \
  typedef ADT_CURRENT_CONS_UNION_T(CONS, INDEX, PARAM)                         \
      __attribute__((transparent_union))                                       \
          ADT_CURRENT_CONS_ALIAS_T(CONS, INDEX);

/*
 * This macro generates an inline function corresponding to one of the data
 * constructors, which will initialize an ADT value and return it.
 *
 * The first variadic argument here is always the name of the constructor, with
 * the rest of the argument list being the parameters given by the user.
 *
 * Unfortunately, we cannot give an argument name to the constructor, since
 * constructors may have no parameters, and the ... in the macro's argument list
 * needs to always match at least one argument. Instead, metamacro_head() is
 * used to get the constructor name.
 */
#define ADT_constructor(...)                                                   \
  static inline struct ADT_CURRENT_T                                           \
                                                                               \
  /* the function name (e.g., 'Red_init_') */                                  \
  metamacro_concat(metamacro_head(__VA_ARGS__), _init_)                        \
                                                                               \
      /* the parameter list for this function */                               \
      (metamacro_foreach_cxt_recursive(                                        \
          ADT_prototype_iter, , metamacro_head(__VA_ARGS__), __VA_ARGS__)) {   \
    /* the actual work of initializing the structure */                        \
    /* the local variables for this function are defined in the first          \
     * iteration of the loop */                                                \
    metamacro_foreach_cxt_recursive(ADT_initialize_iter, ,                     \
                                    metamacro_head(__VA_ARGS__),               \
                                    __VA_ARGS__) return s;                     \
  }

/*
 * The macros below define each parameter to the constructor function.
 *
 * We use the type definitions created by ADT_typedef_iter() and give them
 * simple, sequentially-numbered names, so we don't have to do any more work to
 * parse or otherwise manipulate the declarations given by the user.
 *
 * Because the first argument to metamacro_foreach_recursive_cxt() is the
 * constructor name, our parameters actually need to be shifted down --
 * index 1 actually corresponds to v0, our first parameter.
 */
#define ADT_prototype_iter(INDEX, CONS, PARAM)                                 \
  metamacro_if_eq(0, INDEX)(                                                   \
      /* the constructor name itself is passed as the first argument (again to
         * work around the caveat with variadic arguments), but we don't want it in
         * the parameter list, so we do nothing */)(/* insert a comma for      \
                                                       every argument after    \
                                                       index 1 */              \
                                                    metamacro_if_eq_recursive( \
                                                        1, INDEX)()(, )        \
                                                                               \
                                                    /* parameter type */       \
                                                    ADT_CURRENT_CONS_ALIAS_T(  \
                                                        CONS,                  \
                                                        metamacro_dec(INDEX))  \
                                                                               \
                                                    /* parameter name */       \
                                                    metamacro_concat(          \
                                                        v,                     \
                                                        metamacro_dec(INDEX)))

/*
 * The macros below generate the initialization code that is actually executed
 * in the constructor function.
 *
 * We merely need to map the given arguments onto the internal unions defined by
 * ADT_payload_entry_iter(). Because the union is itself unioned with the
 * user-facing fields, the value can then be read by the user with the name they
 * gave it.
 *
 * As with ADT_prototype*(), our parameter numbers need to be shifted down to
 * correspond to the values in the structure.
 */
#define ADT_initialize_iter(INDEX, CONS, PARAM)                                \
  metamacro_if_eq(0, INDEX)(/* initialize the tag when the structure is        \
                               created, because it cannot change later */      \
                            struct ADT_CURRENT_T s = {.tag = CONS})(           \
      ADT_initialize_memcpy(                                                   \
          ADT_CURRENT_CONS_PAYLOAD_T(CONS, metamacro_dec(INDEX)), s,           \
          metamacro_concat(v, metamacro_dec(INDEX))));

#define ADT_initialize_memcpy(UNION_NAME, ADT, ARG)                            \
  memcpy(&ADT.UNION_NAME, &ARG.UNION_NAME, sizeof(ADT.UNION_NAME));

/*
 * The macros below declare and initialize the function pointers used to
 * psuedo-namespace the data constructors.
 *
 * As with ADT_constructor(), the first variadic argument here is always the
 * constructor name. The arguments following are the parameters (as given by the
 * user).
 *
 * The result looks something like this (using the Color example from above):

const struct {
    ColorT (*Red)();
    ColorT (*Green)();
    ColorT (*Blue)();
    ColorT (*Gray)(double v0);
    ColorT (*Other)(double v0, double v1, double v2);
} Color = {
    .Red = &Red_init_,
    .Green = &Green_init_,
    .Blue = &Blue_init_,
    .Gray = &Gray_init_,
    .Other = &Other_init_,
};

 * Thanks goes out to Jon Sterling and his CADT project for this idea:
 * http://www.jonmsterling.com/CADT/
 */
#define ADT_fptrs_constructor(...)                                             \
  struct ADT_CURRENT_T                                                         \
                                                                               \
      /* the function pointer name (matches that of the constructor) */        \
      (*metamacro_head(__VA_ARGS__))                                           \
                                                                               \
      /* the parameter list for the function -- we just reuse                  \
         ADT_prototype_iter() for this */                                      \
      (metamacro_foreach_cxt_recursive(                                        \
          ADT_prototype_iter, , metamacro_head(__VA_ARGS__), __VA_ARGS__));

#define ADT_fptrinit_constructor(...)                                          \
  /* this uses designated initializer syntax to fill in the function pointers  \
   * with the actual addresses of the inline functions created by              \
   * ADT_constructor() */                                                      \
  .metamacro_head(__VA_ARGS__) =                                               \
      &metamacro_concat(metamacro_head(__VA_ARGS__), _init_),

/*
 * The following macros are used to generate the code for the
 * NSStringFromName() function. They essentially do something similar to
 * an Objective-C implementation of -description.
 *
 * As with ADT_constructor(), the first variadic argument here is always the
 * constructor name. The arguments following are the parameters (as given by the
 * user).
 *
 * As with ADT_prototype*(), our parameter numbers need to be shifted down to
 * correspond to the values in the structure.
 */
#define ADT_tostring_constructor(...)                                          \
  /* try to match each constructor against the value's tag */                  \
  case metamacro_head(__VA_ARGS__): {                                          \
    /* now create a description from the constructor name and any parameters   \
     */                                                                        \
    metamacro_foreach_cxt_recursive(                                           \
        ADT_tostring_iter, , metamacro_head(__VA_ARGS__), __VA_ARGS__) break;  \
  }

#define ADT_tostring_iter(INDEX, CONS, PARAM)                                  \
  /* dispatches to one of the case macros below, based on the INDEX */         \
  metamacro_if_eq(0, INDEX)(ADT_tostring_case0(CONS, PARAM))(                  \
      metamacro_if_eq_recursive(1, INDEX)(ADT_tostring_case1(CONS, PARAM))(    \
          ADT_tostring_defaultcase(INDEX, CONS, PARAM)))

#define ADT_tostring_case0(CONS, PARAM)                                        \
  /* this is the first (and possibly) only part of the description: the name   \
   * of the data constructor */                                                \
  [str appendString:@ #CONS];

#define ADT_tostring_case1(CONS, PARAM)                                        \
  /* we know now that we have arguments, so we insert a brace (and flip the    \
   * corresponding flag), then begin by describing the first argument */       \
  [str appendFormat:@" { %@",                                                  \
                    ADT_CURRENT_PARAMETER_DESCRIPTION(CONS, PARAM, s, 0)];     \
  addedBraces = YES;

#define ADT_tostring_defaultcase(INDEX, CONS, PARAM)                           \
  [str appendFormat:@", %@", ADT_CURRENT_PARAMETER_DESCRIPTION(                \
                                 CONS, PARAM, s, metamacro_dec(INDEX))];

/*
 * The following macros are used to generate the code for the
 * NameEqualToName() function. They essentially do something similar to
 * an Objective-C implementation of -isEqual:.
 *
 * As with ADT_constructor(), the first variadic argument here is always the
 * constructor name. The arguments following are the parameters (as given by the
 * user).
 *
 * As with ADT_prototype*(), our parameter numbers need to be shifted down to
 * correspond to the values in the structure.
 */
#define ADT_equalto_constructor(...)                                           \
  /* try to match each constructor against the value's tag */                  \
  case metamacro_head(__VA_ARGS__): {                                          \
    metamacro_if_eq(1, metamacro_argcount(...))(                               \
        /* no parameters, so we're equal merely if the tags are the same */)(  \
        metamacro_foreach_cxt_recursive(ADT_equalto_iter, , __VA_ARGS__))      \
                                                                               \
        break;                                                                 \
  }

#define ADT_equalto_iter(INDEX, CONS, PARAM)                                                                        \
  {                                                                                                                 \
        const char *paramEncoding = ext_trimADTJunkFromTypeEncoding(@encode(ADT_CURRENT_CONS_ALIAS_T(CONS, INDEX)); \
        \
        /* use isEqual: for objects (including class objects) */ \
        if (*paramEncoding == *@encode(id) || *paramEncoding == *@encode(Class)) { \
            id aObj = *(__unsafe_unretained id *)&a.ADT_CURRENT_CONS_PAYLOAD_T(CONS, INDEX); \
            id bObj = *(__unsafe_unretained id *)&b.ADT_CURRENT_CONS_PAYLOAD_T(CONS, INDEX); \
            \
            if (!aObj) { \
                if (bObj) { \
                    return NO; \
                } \
            } else if (![aObj isEqual:bObj]) { \
                return NO; \
            } \
        } else { \
            /* prefer == to memcmp() here, since ADT values may have garbage padding
             * bits that would cause false negatives with the latter */ \
            if (a.ADT_CURRENT_CONS_PAYLOAD_T(CONS, INDEX) != b.ADT_CURRENT_CONS_PAYLOAD_T(CONS, INDEX)) \
                return NO; \
        }                                                                                                           \
  }

/*
 * The structure tag for the ADT currently being defined. This takes advantage
 * of the fact that the main ADT() is considered to exist on one line, even if
 * it spans multiple lines in an editor.
 *
 * This does prevent multiple ADT() definitions from being provided on the same
 * line, however.
 */
#define ADT_CURRENT_T metamacro_concat(_ADT_, __LINE__)

/*
 * This generates an alias name that can be used to refer to the type of
 * parameter INDEX of constructor CONS (as a transparent union).
 *
 * The actual typedef, which is then referred to later, is generated with
 * ADT_typedef_iter().
 *
 * Because the type by this name is a transparent union, it can be used as
 * a function or method argument and appear to be the actual parameter type
 * given by the user.
 */
#define ADT_CURRENT_CONS_ALIAS_T(CONS, INDEX)                                  \
  metamacro_concat(metamacro_concat(ADT_CURRENT_T, _),                         \
                   metamacro_concat(CONS##_alias, INDEX))

/**
 * Creates an anonymous union that contains the user's parameter declaration as
 * a member, as well as an (other) internal union that is used to access the
 * data without needing to know the name given by the user.
 *
 * See ADT_payload_constructor() for more information.
 */
#define ADT_CURRENT_CONS_UNION_T(CONS, INDEX, PARAM)                           \
  /* create unions upon unions (oh my!) so that we can treat the user's        \
   * parameter as a typedef, without needing to split apart the type and name  \
   * inside it (which is probably not possible) */                             \
  union {                                                                      \
    union {                                                                    \
      PARAM;                                                                   \
    } ADT_CURRENT_CONS_PAYLOAD_T(CONS, INDEX);                                 \
                                                                               \
    PARAM;                                                                     \
  }

#define ADT_CURRENT_CONS_PAYLOAD_T(CONS, INDEX)                                \
  metamacro_concat(CONS##_payload_, INDEX)

/*
 * Expands to an NSString with a human-readable description of the current value
 * at INDEX, for constructor CONS, in the internal value structure of ADT. PARAM
 * is the parameter declaration originally given by the user.
 */
#define ADT_CURRENT_PARAMETER_DESCRIPTION(CONS, PARAM, ADT, INDEX)             \
  [NSString                                                                    \
      stringWithFormat:                                                        \
          @"%@ = %@",                                                          \
          /* this is the only place where we actually parse a      \
                         parameter declaration */ /* we do it here to remove   \
                                                     the type from the         \
                                                     description, keeping only \
                                                     the name */               \
          ext_parameterNameFromDeclaration(                                    \
              @ #PARAM), /* convert the parameter type into an Objective-C     \
                          * type encoding, which, along with a pointer to the  \
                          * data, can be used to generate a human-readable     \
                          * description of the actual value */                 \
          ext_stringFromTypedBytes(                                            \
              &(ADT).ADT_CURRENT_CONS_PAYLOAD_T(CONS, INDEX),                  \
              ext_trimADTJunkFromTypeEncoding(                                 \
                  @encode(ADT_CURRENT_CONS_ALIAS_T(CONS, INDEX))))]

const char *ext_trimADTJunkFromTypeEncoding(const char *encoding);
NSString *ext_parameterNameFromDeclaration(NSString *declaration);
