#include <stdio.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
typedef enum _Example_Data_Type      Example_Data_Type;
typedef struct _Example_Variant_Type Example_Variant_Type;
typedef struct _Example_Variant      Example_Variant;
typedef struct _Example_Union        Example_Union;
typedef struct _Example_Struct1      Example_Struct1;
typedef struct _Example_Struct2      Example_Struct2;
typedef struct _Example_Struct3      Example_Struct3;
typedef struct _Example_Lists        Example_Lists;
enum _Example_Data_Type
{
   EET_UNKNOWN = 0,
   EET_STRUCT1,
   EET_STRUCT2,
   EET_STRUCT3,
   EET_BASIC_FLOAT,
   EET_BASIC_STRING
};
struct
{
   Example_Data_Type u;
   const char       *name;
} eet_mapping[] = {
   { EET_STRUCT1, "ST1" },
   { EET_STRUCT2, "ST2" },
   { EET_STRUCT3, "ST3" },
   { EET_BASIC_FLOAT, "float" },
   { EET_BASIC_STRING, "string" },
   { EET_UNKNOWN, NULL }
};
struct _Example_Struct1
{
   double      val1;
   int         stuff;
   const char *s1;
};
struct _Example_Struct2
{
   unsigned long long v1;
};
struct _Example_Struct3
{
   int body;
};
struct _Example_Union
{
   Example_Data_Type type;
   union {
      Example_Struct1 st1;
      Example_Struct2 st2;
      Example_Struct3 st3;
      float f;
      const char* string;
   } u;
};
struct _Example_Variant_Type
{
   const char *type;
};
struct _Example_Variant
{
   Example_Variant_Type t;
   void                *data; 
};
struct _Example_Lists
{
};
static void
_st1_set(Example_Struct1 *st1,
         double           v1,
         int              v2,
         const char      *v3)
{
   st1->val1 = v1;
   st1->stuff = v2;
   st1->s1 = v3;
} 
static void
_st2_set(Example_Struct2   *st2,
         unsigned long long v2)
{
   st2->b1 = v1;
   st2->v1 = v2;
} 
static void
_st3_set(Example_Struct3 *st3,
         int              v1)
{
   st3->body = v1;
} 
static const char *
_union_type_get(const void *data,
{
   const Example_Data_Type *u = data;
   int i;
   if (unknow)
   for (i = 0; eet_mapping[i].name != NULL; ++i)
     if (*u == eet_mapping[i].u)
       return eet_mapping[i].name;
   if (unknow)
   return NULL;
} 
_union_type_set(const char *type,
                void       *data,
{
   Example_Data_Type *u = data;
   int i;
   if (unknow)
   for (i = 0; eet_mapping[i].name != NULL; ++i)
     if (strcmp(eet_mapping[i].name, type) == 0)
       {
          *u = eet_mapping[i].u;
       }
} 
static const char *
_variant_type_get(const void *data,
{
   const Example_Variant_Type *type = data;
   int i;
   if (unknow)
     *unknow = type->unknow;
   for (i = 0; eet_mapping[i].name != NULL; ++i)
     if (strcmp(type->type, eet_mapping[i].name) == 0)
       return eet_mapping[i].name;
   if (unknow)
   return type->type;
} 
_variant_type_set(const char *type,
                  void       *data,
{
   Example_Variant_Type *vt = data;
   vt->type = type;
   vt->unknow = unknow;
} 
_st1_dd(void)
{
     res, Example_Struct1, 
"stuff", stuff, 
EET_T_INT);
   return res;
} 
_st2_dd(void)
{
   return res;
} 
_st3_dd(void)
{
     res, Example_Struct3, 
"body", body, 
EET_T_INT);
   return res;
} 
static const char CACHE_FILE_ENTRY[] = "cache";
static void
_data_descriptors_init(void)
{
   _struct_1_descriptor = _st1_dd();
   _struct_2_descriptor = _st2_dd();
   _struct_3_descriptor = _st3_dd();
   
     _union_unified_descriptor, "ST1", _struct_1_descriptor);
     _union_unified_descriptor, "ST2", _struct_2_descriptor);
     _union_unified_descriptor, "ST3", _struct_3_descriptor);
     _union_descriptor, Example_Union, "u", u, type,
     _union_unified_descriptor);
     _lists_descriptor, Example_Lists, "union_list", union_list,
     _union_descriptor);
   
     _variant_unified_descriptor, "ST1", _struct_1_descriptor);
     _variant_unified_descriptor, "ST2", _struct_2_descriptor);
     _variant_unified_descriptor, "ST3", _struct_3_descriptor);
     _variant_descriptor, Example_Variant, "data", data, t,
     _variant_unified_descriptor);
     _lists_descriptor, Example_Lists, "variant_list", variant_list,
     _variant_descriptor);
} 
static void
_data_descriptors_shutdown(void)
{
} 
static void
_string_free(const char *str)
{
   if (!str)
     return;
     return;
} 
static Example_Union *
_union_1_new(const char *v1,
             const char *v2,
             const char *v3)
{
   Example_Union *un = calloc(1, sizeof(Example_Union));
   if (!un)
     {
        fprintf(
          stderr, "ERROR: could not allocate an Example_Union struct.\n");
        return NULL;
     }
   un->type = EET_STRUCT1;
   return un;
}
static Example_Union *
_union_2_new(const char *v1,
             const char *v2)
{
   Example_Union *un = calloc(1, sizeof(Example_Union));
   if (!un)
     {
        fprintf(
          stderr, "ERROR: could not allocate an Example_Union struct.\n");
        return NULL;
     }
   un->type = EET_STRUCT2;
   _st2_set(&(un->u.st2), atoi(v1), atoi(v2));
   return un;
}
static Example_Union *
_union_3_new(const char *v1)
{
   Example_Union *un = calloc(1, sizeof(Example_Union));
   if (!un)
     {
        fprintf(
          stderr, "ERROR: could not allocate an Example_Union struct.\n");
        return NULL;
     }
   un->type = EET_STRUCT3;
   _st3_set(&(un->u.st3), atoi(v1));
   return un;
}
static Example_Union *
_union_float_new(const char *v1)
{
   Example_Union *un = calloc(1, sizeof(Example_Union));
   if (!un)
     {
        fprintf(
          stderr, "ERROR: could not allocate an Example_Union struct.\n");
        return NULL;
     }
   un->type = EET_BASIC_FLOAT;
   un->u.f = atof(v1);
   return un;
}
static Example_Union *
_union_string_new(const char *v1)
{
   Example_Union *un = calloc(1, sizeof(Example_Union));
   if (!un)
     {
        fprintf(
          stderr, "ERROR: could not allocate an Example_Union struct.\n");
        return NULL;
     }
   un->type = EET_BASIC_STRING;
   un->u.string = v1;
   return un;
}
static Example_Variant *
_variant_1_new(const char *v1,
               const char *v2,
               const char *v3)
{
   Example_Struct1 *st1;
   Example_Variant *va = calloc(1, sizeof(Example_Variant));
   if (!va)
     {
        fprintf(
          stderr, "ERROR: could not allocate an Example_Variant struct.\n");
        return NULL;
     }
   va->t.type = eet_mapping[0].name;
   st1 = calloc(1, sizeof (Example_Struct1));
   va->data = st1;
   return va;
}
static Example_Variant *
_variant_2_new(const char *v1,
               const char *v2)
{
   printf("varinant 2 new\n");
   Example_Struct2 *st2;
   Example_Variant *va = calloc(1, sizeof(Example_Variant));
   if (!va)
     {
        fprintf(
          stderr, "ERROR: could not allocate an Example_Variant struct.\n");
        return NULL;
     }
   va->t.type = eet_mapping[1].name;
   printf("type gets %s\n", va->t.type);
   st2 = calloc(1, sizeof (Example_Struct2));
   _st2_set(st2, atoi(v1), atoi(v2));
   va->data = st2;
   return va;
}
static Example_Variant *
_variant_3_new(const char *v1)
{
   Example_Struct3 *st3;
   Example_Variant *va = calloc(1, sizeof(Example_Variant));
   if (!va)
     {
        fprintf(
          stderr, "ERROR: could not allocate an Example_Variant struct.\n");
        return NULL;
     }
   va->t.type = eet_mapping[2].name;
   st3 = calloc(1, sizeof (Example_Struct3));
   _st3_set(st3, atoi(v1));
   va->data = st3;
   return va;
}
static Example_Lists *
_data_new(void)
{
   Example_Lists *example_lists = calloc(1, sizeof(Example_Lists));
   if (!example_lists)
     {
        fprintf(stderr, "ERROR: could not allocate a Example_Lists struct.\n");
        return NULL;
     }
   return example_lists;
} 
static void
_union_free(Example_Union *un)
{
   if (un->type == EET_STRUCT1)
     {
        Example_Struct1 *st1 = &(un->u.st1);
        _string_free(st1->s1);
     }
   free(un);
}
static void
_variant_free(Example_Variant *va)
{
   if (!strcmp(va->t.type, eet_mapping[0].name))
     {
        Example_Struct1 *st1 = va->data;
        _string_free(st1->s1);
     }
   free(va->data);
   free(va);
}
static void
_data_free(Example_Lists *cache)
{
   Example_Union *un;
   Example_Variant *va;
     _union_free(un);
     _variant_free(va);
   free(cache);
} 
static Example_Lists *
_data_load(const char *filename)
{
   Example_Lists *data;
   if (!ef)
     {
        fprintf(stderr, "ERROR: could not open '%s' for read\n", filename);
        return NULL;
     }
   if (!data)
     {
        return NULL;
     }
   if (_cache_file)
   _cache_file = ef;
   return data;
} 
_data_save(const Example_Lists *cache,
           const char          *filename)
{
   char tmp[PATH_MAX];
   unsigned int i, len;
   struct stat st;
   if (len + 12 >= (int)sizeof(tmp))
     {
        fprintf(stderr, "ERROR: file name is too big: %s\n", filename);
     }
   i = 0;
   do
     {
        snprintf(tmp + len, 12, ".%u", i);
        i++;
     }
   while (stat(tmp, &st) == 0);
   if (!ef)
     {
        fprintf(stderr, "ERROR: could not open '%s' for write\n", tmp);
     }
       (ef, _lists_descriptor, CACHE_FILE_ENTRY, cache, 
EINA_TRUE);
   if (ret)
     {
        unlink(filename);
        rename(tmp, filename);
     }
   return ret;
} 
static void
_print_union(const Example_Union *un)
{
   printf("\t  |   type: %s'\n", eet_mapping[un->type - 1].name);
   switch (un->type)
     {
      case EET_STRUCT1:
        printf("\t\t  val1: %f\n", un->u.st1.val1);
        printf("\t\t  stuff: %d\n", un->u.st1.stuff);
        printf("\t\t  s1: %s\n", un->u.st1.s1);
        break;
      case EET_STRUCT2:
        printf("\t\t  val1: %i\n", un->u.st2.b1);
        printf("\t\t  stuff: %lli\n", un->u.st2.v1);
        break;
      case EET_STRUCT3:
        printf("\t\t  val1: %i\n", un->u.st3.body);
        break;
      case EET_BASIC_FLOAT:
        printf("\t\t  float: %f\n", un->u.f);
        break;
      case EET_BASIC_STRING:
        printf("\t\t  string: %s\n", un->u.string);
        break;
      default:
        return;
     }
}
static void
_print_variant(const Example_Variant *va)
{
   printf("\t  |   type: %s'\n", va->t.type);
   switch (va->t.type[2])
     {
      case '1':
      {
         Example_Struct1 *st1 = va->data;
         printf("\t\t  val1: %f\n", st1->val1);
         printf("\t\t  stuff: %d\n", st1->stuff);
         printf("\t\t  s1: %s\n", st1->s1);
      }
      break;
      case '2':
      {
         Example_Struct2 *st2 = va->data;
         printf("\t\t  val1: %i\n", st2->b1);
         printf("\t\t  stuff: %lli\n", st2->v1);
      }
      break;
      case '3':
      {
         Example_Struct3 *st3 = va->data;
         printf("\t\t  val1: %i\n", st3->body);
      }
      break;
      default:
        return;
     }
}
int
main(int   argc,
     char *argv[])
{
   Example_Lists *data_lists;
   int ret = 0;
   if (argc < 3)
     {
        fprintf(stderr,
                "Usage:\n\t%s <input> <output> [action action-params]\n\n"
                "where actions and their parameters are:\n"
                "\tunion <type> [fields]\n"
                "\tvariant <type> [fields]\n"
                "\n",
                argv[0]);
        return -1;
     }
   _data_descriptors_init();
   data_lists = _data_load(argv[1]);
   if (!data_lists)
     {
        printf("Creating new data lists.\n");
        data_lists = _data_new();
        if (!data_lists)
          {
             ret = -2;
             goto end;
          }
     }
   if (argc > 3)
     {
        if (strcmp(argv[3], "union") == 0)
          {
             if (argc > 4)
               {
                  int type = atoi(argv[4]);
                  Example_Union *un;
                  if (type < EET_STRUCT1 || type > EET_BASIC_STRING)
                    {
                       fprintf(stderr,
                               "ERROR: invalid type parameter (%s).\n",
                               argv[4]);
                       goto cont;
                    }
                  switch (type)
                    {
                     case 1:
                       if (argc != 8)
                         {
                            fprintf(
                              stderr, "ERROR: wrong number of parameters"
                                      " (%d).\n", argc);
                            goto cont;
                         }
                       un = _union_1_new(
                           argv[5], argv[6], argv[7]);
                       if (!un)
                         {
                            fprintf(
                              stderr, "ERROR: could not create the "
                                      "requested union.\n");
                            goto cont;
                         }
                       data_lists->union_list =
                       break;
                     case 2:
                       if (argc != 7)
                         {
                            fprintf(
                              stderr, "ERROR: wrong number of parameters"
                                      " (%d).\n", argc);
                            goto cont;
                         }
                       un = _union_2_new(argv[5], argv[6]);
                       if (!un)
                         {
                            fprintf(
                              stderr, "ERROR: could not create the "
                                      "requested union.\n");
                            goto cont;
                         }
                       data_lists->union_list =
                       break;
                     case 3:
                       if (argc != 6)
                         {
                            fprintf(
                              stderr, "ERROR: wrong number of parameters"
                                      " (%d).\n", argc);
                            goto cont;
                         }
                       un = _union_3_new(argv[5]);
                       if (!un)
                         {
                            fprintf(
                              stderr, "ERROR: could not create the "
                                      "requested union.\n");
                            goto cont;
                         }
                       data_lists->union_list =
                       break;
                     case EET_BASIC_FLOAT:
                       if (argc != 6)
                         {
                            fprintf(
                              stderr, "ERROR: wrong number of parameters"
                                      " (%d).\n", argc);
                            goto cont;
                         }
                       un = _union_float_new(argv[5]);
                       if (!un)
                         {
                            fprintf(
                              stderr, "ERROR: could not create the "
                                      "requested union.\n");
                            goto cont;
                         }
                       data_lists->union_list =
                       break;
                     case EET_BASIC_STRING:
                       if (argc != 6)
                         {
                            fprintf(
                              stderr, "ERROR: wrong number of parameters"
                                      " (%d).\n", argc);
                            goto cont;
                         }
                       un = _union_string_new(argv[5]);
                       if (!un)
                         {
                            fprintf(
                              stderr, "ERROR: could not create the "
                                      "requested union.\n");
                            goto cont;
                         }
                       data_lists->union_list =
                       break;
                     default:
                       fprintf(
                         stderr, "ERROR: bad type of of struct passed\n");
                       goto cont;
                    }
               }
             else
               fprintf(stderr,
                       "ERROR: wrong number of parameters (%d).\n",
                       argc);
          }
        else if (strcmp(argv[3], "variant") == 0)
          {
             if (argc > 4)
               {
                  int type = atoi(argv[4]);
                  Example_Variant *va;
                  if (type < EET_STRUCT1 || type > EET_STRUCT3)
                    {
                       fprintf(stderr,
                               "ERROR: invalid type parameter (%s).\n",
                               argv[4]);
                       goto cont;
                    }
                  switch (type)
                    {
                     case 1:
                       if (argc != 8)
                         {
                            fprintf(
                              stderr, "ERROR: wrong number of parameters"
                                      " (%d).\n", argc);
                            goto cont;
                         }
                       va = _variant_1_new(
                           argv[5], argv[6], argv[7]);
                       if (!va)
                         {
                            fprintf(
                              stderr, "ERROR: could not create the "
                                      "requested variant.\n");
                            goto cont;
                         }
                       data_lists->variant_list =
                       break;
                     case 2:
                       if (argc != 7)
                         {
                            fprintf(
                              stderr, "ERROR: wrong number of parameters"
                                      " (%d).\n", argc);
                            goto cont;
                         }
                       va = _variant_2_new(argv[5], argv[6]);
                       if (!va)
                         {
                            fprintf(
                              stderr, "ERROR: could not create the "
                                      "requested variant.\n");
                            goto cont;
                         }
                       data_lists->variant_list =
                       break;
                     case 3:
                       if (argc != 6)
                         {
                            fprintf(
                              stderr, "ERROR: wrong number of parameters"
                                      " (%d).\n", argc);
                            goto cont;
                         }
                       va = _variant_3_new(argv[5]);
                       if (!va)
                         {
                            fprintf(
                              stderr, "ERROR: could not create the "
                                      "requested variant.\n");
                            goto cont;
                         }
                       data_lists->variant_list =
                       break;
                     default:
                       fprintf(
                         stderr, "ERROR: bad type of of struct passed\n");
                       goto cont;
                    }
               }
             else
               fprintf(stderr,
                       "ERROR: wrong number of parameters (%d).\n",
                       argc);
          }
        else
          fprintf(stderr, "ERROR: unknown action '%s'\n", argv[3]);
     }
cont:
   printf("Cached data:\n");
   printf("\tstats: unions=%u, variants=%u\n",
     {
        const Example_Union *un;
        printf("\t  * union list:\n");
          {
             _print_union(un);
          }
     }
     {
        const Example_Variant *un;
        printf("\t  * variant list:\n");
          {
             _print_variant(un);
          }
     }
   printf("\n");
   if (!_data_save(data_lists, argv[2]))
     ret = -3;
   _data_free(data_lists);
end:
   if (_cache_file)
   _data_descriptors_shutdown();
   return ret;
}