#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <Ecore_Getopt.h>

#include "edje_cc.h"
#include "edje_private.h"

#define EDJE_PICK_HELP_STRING \
  "\nEdje Pick - the \"edj\" merging tool.\n\
===================================\n\n\
Use Edje Pick to compose a single edj file \
by selecting groups from edj files.\n\n\
Use Edje Pick as follows:\n\
Include files with \'--include\' file-name\n\
Follow the included file-name by names of selected groups from this file.\n\
To select a group use: \'--group\' group-name.\n\n\
You must specify merged file name with \'--output\' file-name.\n\
Use '--verbose' switch to get detailed log.\n\n\
To produce 3rd file \'out.edj\' that composed  of:\n\
Group \'test\' from \'ex1.edj\' and \'test2\', \'test3\', from \'ex2.edj\'\n\
edje_pick -i ex1.edj -g test -i ex2.edj -g test2 -g test3 -o out.edj\n\n\
By using \'--append\' whole file content is selected.\n\
No need to specify selected groups with this switch.\n\
Note that selected group with will override group with the same name\n\
from appended-file when using \'--append\' switch.\n\n\
Example, the command:\n\
edje_pick -a theme1.edj -i default.edj -g elm/button/base/default \
-g elm/check/base/default -o hybrid.edj\n\n\
will produce a 3rd theme file \'hybrid.edj\',\n\
composed of all theme1.edj components.\n\
Replacing the button and check with widgets taken from default theme.\n\
(Given that theme1.edj button, check group-name are as in default.edj)\n"

struct _Edje_Pick_Id
{
   int       old_id;
   int       new_id;
   Eina_Bool used;
};
typedef struct _Edje_Pick_Id Edje_Pick_Id;

struct _Edje_Pick_File_Params
{
   const char *name;
   Eina_List  *groups;
   Edje_File  *edf;    /* Keeps all file data after reading  */
   Eina_Bool   append; /* Take everything from this file */

   /* We hold list of IDs for each file */
   Eina_List  *scriptlist;
   Eina_List  *luascriptlist;
   Eina_List  *imagelist;
   Eina_List  *imagesetlist; /* List of IDs (Edje_Pick_Data) for image sets */
   Eina_List  *vectorlist;
   Eina_List  *samplelist;
   Eina_List  *tonelist;
   Eina_List  *vibrationlist;
   Eina_List  *fontlist;
};
typedef struct _Edje_Pick_File_Params Edje_Pick_File_Params;

struct _Edje_Pick_Data
{
   const char  *filename; /* Image, Sample File Name       */
   void        *entry; /* used to build output file dir FIXME: REMOVE THIS */
   void        *data; /* Data as taken from input file */

   int          size;
   Edje_Pick_Id id;
};
typedef struct _Edje_Pick_Data Edje_Pick_Data;

struct _Edje_Pick_Tone
{
   Edje_Sound_Tone *tone;
   Eina_Bool        used;
};
typedef struct _Edje_Pick_Tone Edje_Pick_Tone;

struct _Edje_Pick_Font
{
   Edje_Font *f;

   void      *data; /* Font data as take from source edj file */
   int        size; /* data size */
   Eina_Bool  used;
};
typedef struct _Edje_Pick_Font Edje_Pick_Font;

struct _Edje_Pick
{
   Eina_Bool              v; /* Verbose */
   Edje_Pick_File_Params *current_file;
};
typedef struct _Edje_Pick Edje_Pick;

static Edje_Pick context = { EINA_FALSE, NULL };

#define VERBOSE(COMMAND) if (context.v) { COMMAND; }

enum _Edje_Pick_Status
{
   EDJE_PICK_NO_ERROR,
   EDJE_PICK_OUT_FILENAME_MISSING,
   EDJE_PICK_FAILED_OPEN_INP,
   EDJE_PICK_FAILED_READ_INP,
   EDJE_PICK_GROUP_NOT_FOUND,
   EDJE_PICK_IMAGE_NOT_FOUND,
   EDJE_PICK_SAMPLE_NOT_FOUND,
   EDJE_PICK_INCLUDE_MISSING,
   EDJE_PICK_GROUP_MISSING,
   EDJE_PICK_PARSE_FAILED,
   EDJE_PICK_HELP_SHOWN,
   EDJE_PICK_DUP_GROUP
};
typedef enum _Edje_Pick_Status Edje_Pick_Status;

static void
_edje_pick_args_show(Eina_List *ifs, char *out)
{  /* Print command-line arguments after parsing phase */
  Edje_Pick_File_Params *p;
  Eina_List *l;
  char *g;

  EINA_LOG_INFO("Got args for <%d> input files.\n", eina_list_count(ifs));

  EINA_LIST_FOREACH(ifs, l, p)
    {
       Eina_List *ll;

       if (p->append)
         printf("\nFile name: %s\n\tGroups: ALL (append mode)\n", p->name);
       else
         {
            printf("\nFile name: %s\n\tGroups:\n", p->name);
            EINA_LIST_FOREACH(p->groups, ll, g)
              printf("\t\t%s\n", g);
         }
    }

  EINA_LOG_INFO("\nOutput file name was <%s>\n", out);
}

static void
_edje_pick_data_free(Eina_List *l)
{
   Edje_Pick_Data *ep;

   EINA_LIST_FREE(l, ep)
     {
        if (ep->filename) eina_stringshare_del(ep->filename);
        free(ep->data);
        free(ep);
     }
}

static void
_edje_pick_out_file_free(Edje_File *out_file)
{
   if (out_file)
     {
        /* Free output file memory allocation */
        if (out_file->ef)
          eet_close(out_file->ef);

        if (out_file->external_dir)
          {
             if (out_file->external_dir->entries)
               free(out_file->external_dir->entries);

             free(out_file->external_dir);
          }

        if (out_file->image_dir)
          {
             if (out_file->image_dir->entries)
               free(out_file->image_dir->entries);

             free(out_file->image_dir);
          }

        if (out_file->sound_dir)
          {
             if (out_file->sound_dir->samples)
               free(out_file->sound_dir->samples);

             if (out_file->sound_dir->tones)
               free(out_file->sound_dir->tones);

             free(out_file->sound_dir);
          }
        if (out_file->vibration_dir)
          {
             if (out_file->vibration_dir->samples)
               free(out_file->vibration_dir->samples);

             free(out_file->vibration_dir);
          }

        eina_list_free(out_file->color_classes);
        eina_hash_free_cb_set(out_file->collection, free);
        eina_hash_free(out_file->collection);
        eina_stringshare_del(out_file->compiler);

        free(out_file);
     }
}

static int
_edje_pick_cleanup(Eina_List *ifs, Edje_File *out_file, Edje_Pick_Status s)
{
   Edje_Pick_File_Params *p;
   Edje_Pick_Font *ft;
   void *n;

   _edje_pick_out_file_free(out_file);

   EINA_LIST_FREE(ifs, p)
     {
        EINA_LIST_FREE(p->groups, n)
          eina_stringshare_del(n);

        _edje_pick_data_free(p->scriptlist);
        p->scriptlist = NULL;

        _edje_pick_data_free(p->luascriptlist);
        p->luascriptlist = NULL;

        _edje_pick_data_free(p->imagelist);
        p->imagelist = NULL;

        _edje_pick_data_free(p->vectorlist);
        p->vectorlist = NULL;

        _edje_pick_data_free(p->imagesetlist);
        p->imagesetlist = NULL;

        _edje_pick_data_free(p->samplelist);

        EINA_LIST_FREE(p->tonelist, n)
          free(n);

        if (p->edf)
          _edje_cache_file_unref(p->edf);

        EINA_LIST_FREE(p->fontlist, ft)
          {
             Edje_Font *st = ft->f;

             eina_stringshare_del(st->name);
             eina_stringshare_del(st->file);
             free(ft->data);
             free(st);
             free(ft);
          }

        free(p);
     }

   switch (s)
     {
      case EDJE_PICK_OUT_FILENAME_MISSING:
        EINA_LOG_ERR("Output file name missing.\n");
        break;

      case EDJE_PICK_FAILED_OPEN_INP:
        EINA_LOG_ERR("Failed to open input file.\n");
        break;

      case EDJE_PICK_FAILED_READ_INP:
        EINA_LOG_ERR("Failed to read input file.\n");
        break;

      case EDJE_PICK_DUP_GROUP:
        EINA_LOG_ERR("Can't fetch groups with identical name from various files.\n");
        break;

      case EDJE_PICK_INCLUDE_MISSING:
        EINA_LOG_ERR("Cannot select groups when no input file included.\n");
        break;

      case EDJE_PICK_GROUP_MISSING:
        EINA_LOG_ERR("Group name missing for include file.\n");
        break;

      case EDJE_PICK_PARSE_FAILED:
        EINA_LOG_ERR("Command parsing failed.\n");
        break;

      default:
        return s;
     }

   _edje_edd_shutdown();
   eet_shutdown();
   return s;
}

/* Look for group name in all input files that are not d1 */
static int
_group_name_in_other_file(Eina_List *inp_files, void *d1, void *d2)
{
   Edje_Pick_File_Params *inp_file = d1;
   char *group = d2; /* Group name to search */
   Eina_List *f;
   Edje_Pick_File_Params *current_file;

   EINA_LIST_FOREACH(inp_files, f, current_file)
     if (inp_file != current_file)
       if (eina_list_search_unsorted(current_file->groups,
                                     (Eina_Compare_Cb)strcmp,
                                     group))
         return 1;

   return 0;  /* Not found */
}

static int
_edje_pick_command_line_parse(int argc, char **argv,
                              Eina_List **ifs, char **ofn)
{  /* On return ifs is Input Files List, ofn is Output File Name */
  Eina_List *gpf = NULL;  /* List including counters of groups-per-file */
  Eina_List *a_files = NULL;
  Eina_List *i_files = NULL;
  Eina_List *l;
  Eina_List *ll;
  Eina_List *cg;
  Eina_List *groups = NULL;
  char *output_filename = NULL;
  Edje_Pick_File_Params *current_inp = NULL;
  Eina_List *files = NULL;   /* List of input files */
  int *c = NULL;
  char *str = NULL;
  int k;
  Eina_Bool show_help = EINA_FALSE;

  /* Define args syntax */
#define IS_GROUP(x)   ((!strcmp(x, "-g")) || (!strcmp(x, "--group")))
#define IS_INCLUDE(x) ((!strcmp(x, "-i")) || (!strcmp(x, "--include")))
#define IS_HELP(x)    ((!strcmp(x, "-h")) || (!strcmp(x, "--help")))
  static const Ecore_Getopt optdesc = {
     "edje_pick",
     NULL,
     "0.0",
     "(C) 2012 Enlightenment",
     "Public domain?",
     "Edje Pick - the \"edj\" merging tool.",

     EINA_TRUE,
     {
        ECORE_GETOPT_STORE_TRUE('v', "verbose", "Verbose"),
        ECORE_GETOPT_STORE('o', "output", "Output File",
                           ECORE_GETOPT_TYPE_STR),
        ECORE_GETOPT_APPEND_METAVAR('a', "append", "Append File",
                                    "STRING", ECORE_GETOPT_TYPE_STR),
        ECORE_GETOPT_APPEND_METAVAR('i', "include", "Include File",
                                    "STRING", ECORE_GETOPT_TYPE_STR),
        ECORE_GETOPT_APPEND_METAVAR('g', "group", "Add Group",
                                    "STRING", ECORE_GETOPT_TYPE_STR),
        ECORE_GETOPT_HELP('h', "help"),
        ECORE_GETOPT_SENTINEL
     }
  };

  Ecore_Getopt_Value values[] = {
     ECORE_GETOPT_VALUE_BOOL(context.v),
     ECORE_GETOPT_VALUE_STR(output_filename),
     ECORE_GETOPT_VALUE_LIST(a_files),
     ECORE_GETOPT_VALUE_LIST(i_files),
     ECORE_GETOPT_VALUE_LIST(groups),
     ECORE_GETOPT_VALUE_NONE
  };

  /* START - Read command line args */
  c = NULL;
  for (k = 1; k < argc; k++)
    {   /* Run through args, count how many groups per file */
      if (IS_GROUP(argv[k]))
        {
           if (!c)
             return _edje_pick_cleanup(files, NULL,
                                       EDJE_PICK_INCLUDE_MISSING);

           (*c)++;
           continue;
        }

      if (IS_INCLUDE(argv[k]))
        {
           c = calloc(1, sizeof(int));
           gpf = eina_list_append(gpf, c);
           continue;
        }

      show_help |= IS_HELP(argv[k]);
    }

  if (show_help)
    puts(EDJE_PICK_HELP_STRING);

  if (ecore_getopt_parse(&optdesc, values, argc, argv) < 0)
    {
       EINA_LIST_FREE(gpf, c)
         free(c);

       return _edje_pick_cleanup(files, NULL, EDJE_PICK_PARSE_FAILED);
    }

  if (show_help)
    {
       EINA_LIST_FREE(gpf, c)
         free(c);

       ecore_getopt_list_free(i_files);
       ecore_getopt_list_free(groups);
       return _edje_pick_cleanup(files, NULL, EDJE_PICK_HELP_SHOWN);
    }

  if (context.v) /* Changed to INFO if verbose */
    eina_log_level_set(EINA_LOG_LEVEL_INFO);

  EINA_LIST_FOREACH(a_files, l, str)
    {
       current_inp = calloc(1, sizeof(*current_inp));
       current_inp->append = EINA_TRUE;
       current_inp->name = eina_stringshare_add(str);
       files = eina_list_append(files, current_inp);
    }
  ecore_getopt_list_free(a_files);

  ll = gpf;
  cg = groups;
  EINA_LIST_FOREACH(i_files, l, str)
    {   /* Now match groups from groups-list with included files */
      current_inp = calloc(1, sizeof(*current_inp));
      current_inp->name = eina_stringshare_add(str);
      files = eina_list_append(files, current_inp);
      c = eina_list_data_get(ll);
      if (c)
        {
           while (*c)
             {
                char *g_name;
                if (!cg)
                  {
                     EINA_LIST_FREE(gpf, c)
                       free(c);

                     ecore_getopt_list_free(i_files);
                     ecore_getopt_list_free(groups);
                     return _edje_pick_cleanup(files, NULL,
                                               EDJE_PICK_GROUP_MISSING);
                  }

                g_name = eina_list_data_get(cg);
                if (_group_name_in_other_file(files, current_inp, g_name))
                  return _edje_pick_cleanup(files, NULL, EDJE_PICK_DUP_GROUP);

                if (!eina_list_search_unsorted(current_inp->groups,
                                               (Eina_Compare_Cb)strcmp, g_name))
                  current_inp->groups = eina_list_append(
                      current_inp->groups, eina_stringshare_add(g_name));

                cg = eina_list_next(cg);
                (*c)--;
             }
        }
      ll = eina_list_next(ll);
    }

  EINA_LIST_FREE(gpf, c)
    free(c);

  ecore_getopt_list_free(i_files);
  ecore_getopt_list_free(groups);

  if (!output_filename)
    return _edje_pick_cleanup(files, NULL, EDJE_PICK_OUT_FILENAME_MISSING);
  /* END   - Read command line args */

  /* Set output params, return OK */
  *ifs = files;
  *ofn = output_filename;
  return EDJE_PICK_NO_ERROR;
}

static void
_edje_pick_external_dir_update(Edje_File *o, Edje_File *edf)
{
   if (edf->external_dir && edf->external_dir->entries_count)
     {
        /* Add external-dir entries */
        unsigned int total = 0;
        unsigned int base = 0;
        Edje_External_Directory_Entry *entries;

        if (o->external_dir)
          base = total = o->external_dir->entries_count;
        else
          o->external_dir = calloc(1, sizeof(*(o->external_dir)));

        total += edf->external_dir->entries_count;

        entries = realloc(o->external_dir->entries,
                          total * sizeof(Edje_External_Directory_Entry));
        if (!entries)
          {
             EINA_LOG_ERR("Out of memory in realloc()");
             return;
          }
        o->external_dir->entries = entries;

        memcpy(&o->external_dir->entries[base], edf->external_dir->entries,
               edf->external_dir->entries_count *
               sizeof(Edje_External_Directory_Entry));

        o->external_dir->entries_count = total;
     }
}

static Edje_File *
_edje_pick_output_prepare(Edje_File *o, Edje_File *edf, char *name)
{
   /* Allocate and prepare header memory buffer */
   if (!o)
     {
        o = calloc(1, sizeof(Edje_File));
        o->compiler = eina_stringshare_add("edje_cc");
        o->version = edf->version;
        o->minor = edf->minor;
        o->feature_ver = edf->feature_ver;
        o->base_scale = edf->base_scale;
        o->collection = eina_hash_string_small_new(NULL);
        o->data = eina_hash_string_small_new(NULL);
        o->fonts = eina_hash_string_small_new(NULL);

        /* Open output file */
        o->ef = eet_open(name, EET_FILE_MODE_WRITE);
     }
   else
     {
        if (o->version != edf->version)
          {
             EINA_LOG_WARN("Warning: Merging files of various version.\n");
             if (o->version < edf->version)
               o->version = edf->version;
          }

        if (o->minor != edf->minor)
          {
             EINA_LOG_WARN("Warning: Merging files of various minor.\n");
             if (o->minor < edf->minor)
               o->minor = edf->minor;
          }

        if (o->feature_ver != edf->feature_ver)
          {
             EINA_LOG_WARN("Warning: Merging files of various feature_ver.\n");
             if (o->feature_ver < edf->feature_ver)
               o->feature_ver = edf->feature_ver;
          }
        if (NEQ(o->base_scale, edf->base_scale))
          {
             EINA_LOG_ERR("Error: Merging files of various base scale. Base scale of the files should be same.\n");
             free(o);
             return NULL;
          }
     }

   _edje_pick_external_dir_update(o, edf);
   return o;
}

static void
_edje_pick_header_alias_parent_add(Edje_File *out_file, Edje_File *edf, Edje_Part_Collection_Directory_Entry *ce, Edje_Part_Collection_Directory_Entry *ce_out)
{
   Edje_Part_Collection_Directory_Entry *ce_cor, *ce_new, *ce_f;
   Eina_Iterator *i;

   if (!ce->group_alias) return;

   i = eina_hash_iterator_data_new(edf->collection);
   EINA_ITERATOR_FOREACH(i, ce_cor)
     {
        /* find the alias parent group */
        if ((ce->id == ce_cor->id) && (!ce_cor->group_alias))
          {
             ce_f = eina_hash_find(out_file->collection, ce_cor->entry);
             if (!ce_f)
               {
                  ce_new = malloc(sizeof(*ce_new));
                  memcpy(ce_new, ce_cor, sizeof(*ce_new));
                  ce_new->id = ce_out->id;
                  eina_hash_direct_add(out_file->collection, ce_new->entry, ce_new);
               }
             else
               {
                  ce_out->id = ce_f->id;
               }
          }
     }
   eina_iterator_free(i);
}

static Eina_List *
_edje_pick_header_dependencies_check(Edje_File *out_file, Edje_File *edf, Eina_List *groups, Edje_Part_Collection_Directory_Entry *ce, int *current_id)
{
   char buf[1024];
   Edje_Part_Collection *edc;
   Edje_Part *part;
   unsigned int i, j;
   Edje_Pack_Element *item;
   Eina_List *dep_list = NULL, *dep_list_inner = NULL;

   edje_cache_emp_alloc(ce);

   snprintf(buf, sizeof(buf), "edje/collections/%i", ce->id);
   EINA_LOG_INFO("Trying to read group <%s>\n", buf);
   edc = eet_data_read(edf->ef, _edje_edd_edje_part_collection, buf);
   if (!edc)
     {
        EINA_LOG_ERR("Failed to read group <%s> id <%d>\n", ce->entry, ce->id);
        goto exit;
     }
#define GROUP_CHECK_AND_ADD(NAME)                                                                             \
  if (NAME)                                                                                                   \
    {                                                                                                         \
       Edje_Part_Collection_Directory_Entry *ce_cor;                                                          \
       if (eina_list_search_unsorted(groups, (void *)strcmp, NAME)) goto exit;                                \
       ce_cor = eina_hash_find(edf->collection, NAME);                                                        \
       if (!eina_hash_find(out_file->collection, ce_cor->entry))                                              \
         {                                                                                                    \
            dep_list_inner = _edje_pick_header_dependencies_check(out_file, edf, groups, ce_cor, current_id); \
            dep_list = eina_list_merge(dep_list, dep_list_inner);                                             \
         }                                                                                                    \
       if ((ce_cor) && (!eina_hash_find(out_file->collection, NAME)))                                         \
         {                                                                                                    \
            Edje_Part_Collection_Directory_Entry *ce_out;                                                     \
            ce_out = malloc(sizeof(*ce_out));                                                                 \
            memcpy(ce_out, ce_cor, sizeof(*ce_out));                                                          \
            ce_out->id = *current_id;                                                                         \
            if (ce_out->group_alias)                                                                          \
              _edje_pick_header_alias_parent_add(out_file, edf, ce, ce_out);                                  \
            EINA_LOG_INFO("Changing ID of group <%d> to <%d>\n", ce->id, ce_out->id);                         \
            eina_hash_direct_add(out_file->collection, ce_out->entry, ce_out);                                \
            (*current_id)++;                                                                                  \
            dep_list = eina_list_append(dep_list, eina_stringshare_add(ce_out->entry));                       \
         }                                                                                                    \
    }

   for (i = 0; i < edc->parts_count; i++)
     {
        part = edc->parts[i];
        GROUP_CHECK_AND_ADD(part->source);
        GROUP_CHECK_AND_ADD(part->source2);
        GROUP_CHECK_AND_ADD(part->source3);
        GROUP_CHECK_AND_ADD(part->source4);
        GROUP_CHECK_AND_ADD(part->source5);
        GROUP_CHECK_AND_ADD(part->source6);
        for (j = 0; j < part->items_count; j++)
          {
             item = part->items[j];
             GROUP_CHECK_AND_ADD(item->source);
          }
     }

exit:
   edje_cache_emp_free(ce);
   return dep_list;
#undef GROUP_CHECK_AND_ADD
}

static int
_edje_pick_data_update(Edje_File *out_file, Edje_File *edf)
{
   Eina_Bool status = EDJE_PICK_NO_ERROR;
   Eina_Iterator *i;
   Eina_Stringshare *key, *value;

   i = eina_hash_iterator_key_new(edf->data);
   EINA_ITERATOR_FOREACH(i, key)
     {
        if (eina_hash_find(out_file->data, key)) continue;
        value = eina_hash_find(edf->data, key);
        eina_hash_direct_add(out_file->data, key, value);
     }

   return status;
}

static int
_edje_pick_header_make(Edje_File *out_file, Edje_File *edf, Eina_List *ifs)
{
   static int current_group_id = 0;
   Edje_Part_Collection_Directory_Entry *ce;
   Eina_Bool status = EDJE_PICK_NO_ERROR;
   Eina_List *l, *alist = NULL;
   char *name1 = NULL;
   Eina_List *deps = NULL, *dep_list = NULL;
   Eina_Iterator *i;

   /* Build file header */
   if (context.current_file->append)
     {
        i = eina_hash_iterator_key_new(edf->collection);
        EINA_ITERATOR_FOREACH(i, name1)  /* Run through all keys */
          {
             Edje_Part_Collection_Directory_Entry *ce_out;

             /* Use ALL groups from this file */
             /* Test that no duplicate-group name for files in append mode */
             /* Done here because we don't read EDC before parse cmd line  */
             /* We SKIP group of file in append-mode if we got this group  */
             /* from file in include mode.                                 */
             if (_group_name_in_other_file(ifs, context.current_file, name1))
               continue;  /* Skip group of file in append mode */

             ce = eina_hash_find(edf->collection, name1);
             ce_out = malloc(sizeof(*ce_out));
             memcpy(ce_out, ce, sizeof(*ce_out));
             if (ce_out->group_alias)
               alist = eina_list_append(alist, eina_stringshare_add(name1));

             ce_out->id = current_group_id;
             current_group_id++;

             eina_hash_direct_add(out_file->collection, ce_out->entry, ce_out);

             /* Add this group to groups to handle for this file */
             context.current_file->groups = eina_list_append(
                 context.current_file->groups, eina_stringshare_add(name1));
          }

        eina_iterator_free(i);
        EINA_LIST_FOREACH(alist, l, name1)
          {
             Edje_Part_Collection_Directory_Entry *ce_cor;

             ce = eina_hash_find(edf->collection, name1);
             i = eina_hash_iterator_data_new(edf->collection);
             EINA_ITERATOR_FOREACH(i, ce_cor)
               {
                  if ((ce->id == ce_cor->id) && (!ce_cor->group_alias))
                    break;
               }
             ce = eina_hash_find(out_file->collection, ce_cor->entry);
             ce_cor = eina_hash_find(out_file->collection, name1);
             ce_cor->id = ce->id;
             eina_iterator_free(i);
          }
        EINA_LIST_FREE(alist, name1)
          eina_stringshare_del(name1);
     }
   else
     {
        EINA_LIST_FOREACH(context.current_file->groups, l, name1)
          {
             /* Verify group found then add to ouput file header */
             ce = eina_hash_find(edf->collection, name1);

             if (!ce)
               {
                  EINA_LOG_ERR("Group <%s> was not found in <%s> file.\n",
                               name1, context.current_file->name);
                  status = EDJE_PICK_GROUP_NOT_FOUND;
               }
             else
               {
                  Edje_Part_Collection_Directory_Entry *ce_out;

                  /* Add this groups to hash, with filname pefix for entries */
                  ce_out = malloc(sizeof(*ce_out));

                  memcpy(ce_out, ce, sizeof(*ce_out));
                  ce_out->id = current_group_id;
                  if (ce_out->group_alias)
                    _edje_pick_header_alias_parent_add(out_file, edf, ce, ce_out);
                  EINA_LOG_INFO("Changing ID of group <%d> to <%d>\n", ce->id, ce_out->id);
                  current_group_id++;

                  eina_hash_direct_add(out_file->collection, ce_out->entry,
                                       ce_out);
                  dep_list = _edje_pick_header_dependencies_check(out_file, edf, context.current_file->groups, ce, &current_group_id);
                  if (!deps) deps = dep_list;
                  else deps = eina_list_merge(deps, dep_list);
               }
          }
        EINA_LIST_FREE(deps, name1)
          context.current_file->groups = eina_list_append(context.current_file->groups, name1);
     }

   return status;
}

static int
_old_id_cmp(const void *d1, const void *d2)
{
   /* Find currect ID struct */
   return ((Edje_Pick_Data *)d1)->id.old_id - ((intptr_t)d2);
}

static int
_new_id_cmp(const void *d1, const void *d2)
{
   /* Find currect ID struct */
   return ((Edje_Pick_Data *)d1)->id.new_id - ((intptr_t)d2);
}

static int
_edje_pick_new_id_get(Eina_List *id_list, int id, Eina_Bool set_used)
{
   if (id >= 0)
     {
        Edje_Pick_Data *p_id = eina_list_search_unsorted(id_list,
                                                         _old_id_cmp,
                                                         (void *)(intptr_t)id);

        if (p_id)
          {
             if (set_used)
               p_id->id.used = EINA_TRUE;

             return p_id->id.new_id;
          }
     }

   return id;
}

static int
_edje_pick_image_new_id_get(Eina_List *id_list, int id)
{
   if (id >= 0)
     {
        Edje_Pick_Data *p_id = eina_list_search_unsorted(id_list,
                                                         _old_id_cmp,
                                                         (void *)(intptr_t)id);
        if (p_id) return p_id->id.new_id;
     }

   return -1;
}

static int
_edje_pick_image_old_id_get(Eina_List *id_list, int id)
{
   if (id >= 0)
     {
        Edje_Pick_Data *p_id = eina_list_search_unsorted(id_list,
                                                         _new_id_cmp,
                                                         (void *)(intptr_t)id);
        if (p_id) return p_id->id.old_id;
     }

   return -1;
}

static int
_edje_pick_images_copy(Edje_File *edf, Edje_File *o)
{
   int old_id;
   Eina_Bool status = EDJE_PICK_NO_ERROR;
   Eina_List *l, *l1;
   Edje_Pick_Data *image, *set, *vector;

   if (edf->image_dir)
     {
        if (edf->image_dir->entries) /* Copy image dir entries of current file */
          {
             Edje_Image_Directory_Entry *entries = NULL;

             EINA_LIST_FOREACH(context.current_file->imagelist, l, image)
               {
                  if (image->id.used)
                    {
                       o->image_dir->entries_count += 1;

                       /* alloc mem first time  or  re-allocate again (bigger array) */
                       entries = realloc(o->image_dir->entries,
                                         o->image_dir->entries_count *
                                         sizeof(Edje_Image_Directory_Entry));
                       if (!entries)
                         {
                            EINA_LOG_ERR("Out of memory in realloc()");
                            return EDJE_PICK_IMAGE_NOT_FOUND;
                         }
                       o->image_dir->entries = entries;

                       old_id = _edje_pick_image_old_id_get(
                           context.current_file->imagelist, o->image_dir->entries_count - 1);
                       /* Concatenate current file entries to re-allocated array */
                       memcpy(&o->image_dir->entries[o->image_dir->entries_count - 1],
                              &edf->image_dir->entries[old_id],
                              sizeof(Edje_Image_Directory_Entry));
                       o->image_dir->entries[o->image_dir->entries_count - 1].id = image->id.new_id;
                    }
               }
          }

        if (edf->image_dir->sets) /* Copy image dir sets of current file */
          {
             Edje_Image_Directory_Set *sets;

             EINA_LIST_FOREACH(context.current_file->imagesetlist, l, set)
               {
                  if (set->id.used)
                    {
                       Edje_Image_Directory_Set_Entry *e;

                       o->image_dir->sets_count += 1;
                       /* alloc mem first time  or  re-allocate again (bigger array) */
                       sets = realloc(o->image_dir->sets,
                                      o->image_dir->sets_count *
                                      sizeof(Edje_Image_Directory_Set));
                       if (!sets)
                         {
                            EINA_LOG_ERR("Out of memory in realloc()");
                            return EDJE_PICK_IMAGE_NOT_FOUND;
                         }
                       o->image_dir->sets = sets;

                       old_id = _edje_pick_image_old_id_get(
                           context.current_file->imagesetlist, o->image_dir->sets_count - 1);
                       /* Concatenate current file sets to re-allocated array */
                       memcpy(&o->image_dir->sets[o->image_dir->sets_count - 1],
                              &edf->image_dir->sets[old_id],
                              sizeof(Edje_Image_Directory_Set));
                       o->image_dir->sets[o->image_dir->sets_count - 1].id = set->id.new_id;

                       EINA_LIST_FOREACH(o->image_dir->sets[o->image_dir->sets_count - 1].entries, l1, e)
                         e->id = _edje_pick_image_new_id_get(
                             context.current_file->imagelist,
                             e->id);
                    }
               }
          }

        if (edf->image_dir->vectors) /* Copy vector dir entries of current file */
          {
             Edje_Vector_Directory_Entry *vectors = NULL;

             EINA_LIST_FOREACH(context.current_file->vectorlist, l, vector)
               {
                  if (vector->id.used)
                    {
                       o->image_dir->vectors_count += 1;

                       /* alloc mem first time  or  re-allocate again (bigger array) */
                       vectors = realloc(o->image_dir->vectors,
                                         o->image_dir->vectors_count *
                                         sizeof(Edje_Vector_Directory_Entry));
                       if (!vectors)
                         {
                            EINA_LOG_ERR("Out of memory in realloc()");
                            return EDJE_PICK_IMAGE_NOT_FOUND;
                         }
                       o->image_dir->vectors = vectors;

                       old_id = _edje_pick_image_old_id_get(
                           context.current_file->vectorlist, o->image_dir->vectors_count - 1);
                       /* Concatenate current file vectors to re-allocated array */
                       memcpy(&o->image_dir->vectors[o->image_dir->vectors_count - 1],
                              &edf->image_dir->vectors[old_id],
                              sizeof(Edje_Vector_Directory_Entry));
                       o->image_dir->vectors[o->image_dir->vectors_count - 1].id = vector->id.new_id;
                    }
               }
          }
     }

   return status;
}

static int
_edje_pick_sounds_add(Edje_File *edf)
{
   char buf[1024];
   int size, k;
   void *data;
   Eina_Bool status = EDJE_PICK_NO_ERROR;
   static int current_sample_id = 0;

   if (edf->sound_dir)  /* Copy Sounds */
     {
        for (k = 0; k < (int)edf->sound_dir->samples_count; k++)
          {
             Edje_Sound_Sample *sample = &edf->sound_dir->samples[k];

             snprintf(buf, sizeof(buf), "edje/sounds/%i", sample->id);
             VERBOSE(EINA_LOG_INFO("Trying to read <%s>\n", sample->snd_src));

             data = eet_read(edf->ef, buf, &size);
             if (size)
               {
                  Edje_Pick_Data *smpl = malloc(sizeof(*smpl));
                  smpl->filename = eina_stringshare_add(sample->snd_src);
                  smpl->data = data;
                  smpl->size = size;
                  smpl->entry = (void *)sample;  /* for output file sound dir */
                  smpl->id.old_id = sample->id;
                  sample->id = smpl->id.new_id = current_sample_id;
                  smpl->id.used = EINA_FALSE;

                  VERBOSE(EINA_LOG_INFO("Read <%s> sample data <%p> size <%d>\n",
                                        buf, smpl->data, smpl->size));

                  current_sample_id++;
                  context.current_file->samplelist =
                    eina_list_append(context.current_file->samplelist, smpl);
               }
             else
               {
                  EINA_LOG_ERR("Sample <%s> was not found in <%s> file.\n",
                               sample->name, context.current_file->name);
                  status = EDJE_PICK_SAMPLE_NOT_FOUND;
                  /* Should that really be freed? Or is some other handling needed? */
                  free(data);
               }
          }

        for (k = 0; k < (int)edf->sound_dir->tones_count; k++)
          {
             /* Save all tones as well */
             Edje_Pick_Tone *t = malloc(sizeof(*t));

             t->tone = &edf->sound_dir->tones[k];
             /* Update ID to new ID */
             t->tone->id = _edje_pick_new_id_get(context.current_file->samplelist,   /* From samplelist */
                                                 t->tone->id, EINA_FALSE);

             t->used = EINA_FALSE;
             context.current_file->tonelist = eina_list_append(context.current_file->tonelist, t);
          }
     }

   return status;
}

static int
_edje_pick_vibrations_add(Edje_File *edf)
{
   char buf[1024];
   int size, k;
   void *data;
   Eina_Bool status = EDJE_PICK_NO_ERROR;
   static int current_sample_id = 0;

   if (edf->vibration_dir)  /* Copy Sounds */
     {
        for (k = 0; k < (int)edf->vibration_dir->samples_count; k++)
          {
             Edje_Vibration_Sample *sample = &edf->vibration_dir->samples[k];

             snprintf(buf, sizeof(buf), "edje/vibrations/%i", sample->id);
             VERBOSE(EINA_LOG_INFO("Trying to read <%s>\n", sample->name));

             data = eet_read(edf->ef, buf, &size);
             if (size)
               {
                  Edje_Pick_Data *smpl = malloc(sizeof(*smpl));
                  smpl->filename = eina_stringshare_add(sample->name);
                  smpl->data = data;
                  smpl->size = size;
                  smpl->entry = (void *)sample;  /* for output file vibration dir */
                  smpl->id.old_id = sample->id;
                  sample->id = smpl->id.new_id = current_sample_id;
                  smpl->id.used = EINA_FALSE;

                  VERBOSE(EINA_LOG_INFO("Read <%s> sample data <%p> size <%d>\n",
                                        buf, smpl->data, smpl->size));

                  current_sample_id++;
                  context.current_file->vibrationlist =
                    eina_list_append(context.current_file->vibrationlist, smpl);
               }
             else
               {
                  EINA_LOG_ERR("Sample <%s> was not found in <%s> file.\n",
                               sample->name, context.current_file->name);
                  status = EDJE_PICK_SAMPLE_NOT_FOUND;
                  /* Should that really be freed? Or is some other handling needed? */
                  free(data);
               }
          }
     }

   return status;
}

static int
_font_cmp(const void *d1, const void *d2)
{
   const Edje_Font *f1 = d1;
   const Edje_Font *f2 = d2;

   /* Same font if (d1->name == d2->name) AND (d1->file == d2->file) */
   return strcmp(f1->name, f2->name) |
          strcmp(f1->file, f2->file);
}

static int
_Edje_Pick_Fonts_add(Edje_File *out_file, Edje_File *edf)
{
   Eina_Iterator *it;
   Edje_Font *f;
   char buf[1024];

   if (!edf->fonts) return -1;

   it = eina_hash_iterator_data_new(edf->fonts);
   if (!it) return -1;

   EINA_ITERATOR_FOREACH(it, f)
     {
        if (!eina_list_search_unsorted(context.current_file->fontlist, _font_cmp, f))
          continue;
        /* Add only fonts that are NOT regestered in our list */
        Edje_Pick_Font *ft = malloc(sizeof(*ft));
        Edje_Font *st = malloc(sizeof(*st));
        ft->size = 0;

        st->name = (char *)eina_stringshare_add(f->name);
        st->file = (char *)eina_stringshare_add(f->file);

        snprintf(buf, sizeof(buf), "edje/fonts/%s", f->name);
        VERBOSE(EINA_LOG_INFO("Trying to read <%s>\n", f->name));
        ft->data = eet_read(edf->ef, buf, &ft->size);
        if (!ft->size)
          {
             eina_stringshare_del(st->name);
             eina_stringshare_del(st->file);
             free(st);
             free(ft);
             VERBOSE(EINA_LOG_INFO("Enable to read <%s>. Skip.\n", f->name));
             continue;
          }

        ft->f = st;
        ft->used = EINA_TRUE;  /* TODO: Fix this later */
        context.current_file->fontlist = eina_list_append(context.current_file->fontlist, ft);
        eina_hash_direct_add(out_file->fonts, st->name, st);
     }

   eina_iterator_free(it);

   return EDJE_PICK_NO_ERROR;
}

static int
_edje_pick_scripts_add(Edje_File *edf, int id, int new_id)
{
   int size;
   void *data;
   char buf[1024];

   /* Copy Script */
   snprintf(buf, sizeof(buf), "edje/scripts/embryo/compiled/%i", id);
   data = eet_read(edf->ef, buf, &size);
   if (size)
     {
        Edje_Pick_Data *s = calloc(1, sizeof(*s));

        s->data = data;
        s->size = size;
        s->id.old_id = id;
        s->id.new_id = new_id;
        s->id.used = EINA_TRUE;

        VERBOSE(EINA_LOG_INFO("Read embryo script <%s> data <%p> size <%d>\n",
                              buf, s->data, s->size));
        context.current_file->scriptlist = eina_list_append(context.current_file->scriptlist, s);
     }
   else
     {
        /* Should that really be freed? Or is some other handling needed? */
        free(data);
     }

   return EDJE_PICK_NO_ERROR;
}

static int
_edje_pick_lua_scripts_add(Edje_File *edf, int id, int new_id)
{
   int size;
   void *data;
   char buf[1024];

   /* Copy Script */
   snprintf(buf, sizeof(buf), "edje/scripts/lua/%i", id);
   data = eet_read(edf->ef, buf, &size);
   if (size)
     {
        Edje_Pick_Data *s = calloc(1, sizeof(*s));

        s->data = data;
        s->size = size;
        s->id.old_id = id;
        s->id.new_id = new_id;
        s->id.used = EINA_TRUE;

        VERBOSE(EINA_LOG_INFO("Read lua script <%s> data <%p> size <%d>\n",
                              buf, s->data, s->size));
        context.current_file->luascriptlist = eina_list_append(context.current_file->luascriptlist, s);
     }
   else
     {
        /* Should that really be freed? Or is some other handling needed? */
        free(data);
     }

   return EDJE_PICK_NO_ERROR;
}

static int
_edje_pick_image_entry_add(int id, Edje_File *edf, Edje_File *o)
{
   char buf[1024];
   int size;
   void *data;
   static int current_img_id = -1;

   int new_id = _edje_pick_image_new_id_get(context.current_file->imagelist, id);
   if (new_id >= 0) return new_id;

   if (edf->image_dir)
     {
        if (!o->image_dir) /* First time only */
          o->image_dir = calloc(1, sizeof(*(o->image_dir)));

        if (edf->image_dir->entries) /* Copy Images */
          {
             Edje_Image_Directory_Entry *img = &edf->image_dir->entries[id];

             snprintf(buf, sizeof(buf), "edje/images/%i", img->id);
             VERBOSE(EINA_LOG_INFO("Trying to read <%s>\n", img->entry));
             data = eet_read(edf->ef, buf, &size);
             if (size) /* Advance image ID and register this in imagelist */
               {
                  current_img_id++;
                  Edje_Pick_Data *image = malloc(sizeof(*image));

                  image->filename = eina_stringshare_add(img->entry);
                  image->data = data;
                  image->size = size;
                  image->entry = (void *)img;   /* for output file image dir */
                  image->id.old_id = img->id;
                  img->id = image->id.new_id = current_img_id;
                  image->id.used = EINA_TRUE;

                  VERBOSE(EINA_LOG_INFO("Read image <%s> data <%p> size <%d>, new_id : %d\n",
                                        buf, image->data, image->size, image->id.new_id));

                  context.current_file->imagelist = eina_list_append(
                      context.current_file->imagelist, image);
               }
             else
               {
                  if (img->entry)
                    {
                       EINA_LOG_ERR("Image <%s> was not found in <%s> file.\n",
                                    img->entry, context.current_file->name);
                    }
                  else
                    {
                       EINA_LOG_ERR("Image entry <%s> was not found in <%s> file.\n", buf, context.current_file->name);
                    }
                  /* Should that really be freed? Or is some other handling needed? */
                  free(data);
               }
          }
     }

   return current_img_id;
}

static int
_edje_pick_image_set_add(int id, Edje_File *edf, Edje_File *o)
{
   static int current_set_id = -1;
   Edje_Image_Directory_Set_Entry *ent;
   Eina_List *l;

   int new_id = _edje_pick_image_new_id_get(context.current_file->imagesetlist, id);
   if (new_id >= 0) return new_id;

   if (edf->image_dir)
     {
        if (!o->image_dir) /* First time only */
          o->image_dir = calloc(1, sizeof(*(o->image_dir)));

        if (edf->image_dir->sets) /* Fix IDs in sets to new assigned IDs of entries */
          {
             current_set_id++;
             Edje_Pick_Data *set = calloc(1, sizeof(*set));
             set->id.old_id = edf->image_dir->sets[id].id;
             set->id.new_id = current_set_id;
             set->id.used = EINA_TRUE;

             /* Save IDs in set-list, used in Desc update later */
             context.current_file->imagesetlist = eina_list_append(
                 context.current_file->imagesetlist, set);
             EINA_LIST_FOREACH(edf->image_dir->sets[id].entries, l, ent)
               {
                  _edje_pick_image_entry_add(ent->id, edf, o);
               }
          }
     }

   return current_set_id;
}

static int
_edje_pick_vector_entry_add(int id, Edje_File *edf, Edje_File *o)
{
   char buf[1024];
   int size;
   void *data;
   static int current_vg_id = -1;

   int new_id = _edje_pick_image_new_id_get(context.current_file->vectorlist, id);
   if (new_id >= 0) return new_id;

   if (edf->image_dir)
     {
        if (!o->image_dir) /* First time only */
          o->image_dir = calloc(1, sizeof(*(o->image_dir)));

        if (edf->image_dir->vectors) /* Copy Vectors */
          {
             Edje_Vector_Directory_Entry *vg = &edf->image_dir->vectors[id];

             snprintf(buf, sizeof(buf), "edje/vectors/%i", vg->id);
             VERBOSE(EINA_LOG_INFO("Trying to read <%s>\n", vg->entry));
             data = eet_read(edf->ef, buf, &size);
             if (size) /* Advance vector ID and register this in vectorlist */
               {
                  current_vg_id++;
                  Edje_Pick_Data *vector = malloc(sizeof(*vector));

                  vector->filename = eina_stringshare_add(vg->entry);
                  vector->data = data;
                  vector->size = size;
                  vector->entry = (void *)vg;   /* for output file vector dir */
                  vector->id.old_id = vg->id;
                  vg->id = vector->id.new_id = current_vg_id;
                  vector->id.used = EINA_TRUE;

                  VERBOSE(EINA_LOG_INFO("Read vector <%s> data <%p> size <%d>, new_id : %d\n",
                                        buf, vector->data, vector->size, vector->id.new_id));

                  context.current_file->vectorlist = eina_list_append(
                      context.current_file->vectorlist, vector);
               }
             else
               {
                  if (vg->entry)
                    {
                       EINA_LOG_ERR("Vector <%s> was not found in <%s> file.\n",
                                    vg->entry, context.current_file->name);
                    }
                  else
                    {
                       EINA_LOG_ERR("Vector entry <%s> was not found in <%s> file.\n", buf, context.current_file->name);
                    }
                  /* Should that really be freed? Or is some other handling needed? */
                  free(data);
               }
          }
     }

   return current_vg_id;
}

static void
_edje_pick_images_desc_update(Edje_Part_Description_Image *desc, Edje_File *edf, Edje_File *o)
{
   /* Update all IDs of images in descs */
   if (desc)
     {
        unsigned int k;
        int new_id = (desc->image.set) ?
          _edje_pick_image_set_add(desc->image.id, edf, o) :
          _edje_pick_image_entry_add(desc->image.id, edf, o);

        desc->image.id = new_id;

        for (k = 0; k < desc->image.tweens_count; k++)
          {
             new_id = (desc->image.set) ?
               _edje_pick_image_set_add(desc->image.tweens[k]->id, edf, o) :
               _edje_pick_image_entry_add(desc->image.tweens[k]->id, edf, o);

             desc->image.tweens[k]->id = new_id;
          }
     }
}

static void
_edje_pick_vectors_desc_update(Edje_Part_Description_Vector *desc, Edje_File *edf, Edje_File *o)
{
   /* Update all IDs of vectors in descs */
   if (desc)
     {
        int new_id = _edje_pick_vector_entry_add(desc->vg.id, edf, o);

        desc->vg.id = new_id;
     }
}

static void
_edje_pick_styles_desc_update(Edje_Part_Description_Text *desc, Edje_File *edf, Edje_File *o)
{
   Eina_List *l;
   Edje_Style *stl;
   const char *style;

   if (desc)
     {
        style = desc->text.style.str;

        if (style)
          {
             EINA_LIST_FOREACH(edf->styles, l, stl)
               if ((stl->name) && (!strcmp(stl->name, style)))
                 o->styles = eina_list_append(o->styles, stl);
          }
     }
}

static void
_edje_pick_color_class_desc_update(Edje_Part_Description_Common *desc, Edje_File *edf, Edje_File *o)
{
   Eina_List *l;
   Edje_Color_Class *cc;
   const char *color_class;

   if (desc)
     {
        color_class = desc->color_class;

        if (color_class)
          {
             EINA_LIST_FOREACH(edf->color_classes, l, cc)
               if ((cc->name) && (!strcmp(cc->name, color_class)))
                 o->color_classes = eina_list_append(o->color_classes, cc);
          }
     }
}

static void
_edje_pick_resources_process(Edje_Part_Collection *edc, Edje_File *edf, Edje_File *o)
{
   /* Find what imagees are used, update IDs, mark as USED */
   /* Find what styles and color classes are used */
   unsigned int i, k;

   for (i = 0; i < edc->parts_count; i++)
     {
        /* Scan all parts, locate what resources used */
        Edje_Part *part = edc->parts[i];

        /* Update all color class in ALL descs of this part */
        _edje_pick_color_class_desc_update((Edje_Part_Description_Common *)part->default_desc, edf, o);

        for (k = 0; k < part->other.desc_count; k++)
          _edje_pick_color_class_desc_update((Edje_Part_Description_Common *)part->other.desc[k], edf, o);

        if (part->type == EDJE_PART_TYPE_IMAGE)
          {
             /* Update IDs of all images in ALL descs of this part */
             _edje_pick_images_desc_update((Edje_Part_Description_Image *)part->default_desc, edf, o);

             for (k = 0; k < part->other.desc_count; k++)
               _edje_pick_images_desc_update((Edje_Part_Description_Image *)part->other.desc[k], edf, o);
          }
        else if (part->type == EDJE_PART_TYPE_VECTOR)
          {
             /* Update IDs of all vectors in ALL descs of this part */
             _edje_pick_vectors_desc_update((Edje_Part_Description_Vector *)part->default_desc, edf, o);

             for (k = 0; k < part->other.desc_count; k++)
               _edje_pick_vectors_desc_update((Edje_Part_Description_Vector *)part->other.desc[k], edf, o);
          }
        else if (part->type == EDJE_PART_TYPE_TEXT || part->type == EDJE_PART_TYPE_TEXTBLOCK)
          {
             /* Update all styles in ALL descs of this part */
             _edje_pick_styles_desc_update((Edje_Part_Description_Text *)part->default_desc, edf, o);

             for (k = 0; k < part->other.desc_count; k++)
               _edje_pick_styles_desc_update((Edje_Part_Description_Text *)part->other.desc[k], edf, o);
          }
     }
}

static int
_sample_cmp(const void *d1, const void *d2)
{
   /* Locate sample by name */
   if (d2)
     {
        Edje_Sound_Sample *sample = ((Edje_Pick_Data *)d1)->entry;

        return strcmp(sample->name, d2);
     }

   return 1;
}

static int
_tone_cmp(const void *d1, const void *d2)
{
   /* Locate tone by name */
   if (d2)
     {
        Edje_Sound_Tone *tone = ((Edje_Pick_Tone *)d1)->tone;

        return strcmp(tone->name, d2);
     }

   return 1;
}

static void
_edje_pick_program_update(Edje_Program *prog)
{
   Edje_Pick_Data *p;
   Edje_Pick_Tone *t;

   /* Scan for used samples, update samples IDs */
   p = eina_list_search_unsorted(context.current_file->samplelist,
                                 (Eina_Compare_Cb)_sample_cmp,
                                 prog->sample_name);

   /* Sample is used by program, should be saved */
   if (p)
     p->id.used = EINA_TRUE;

   /* handle tones as well */
   t = eina_list_search_unsorted(context.current_file->tonelist,
                                 (Eina_Compare_Cb)_tone_cmp,
                                 prog->tone_name);

   /* Tone is used by program, should be saved */
   if (t)
     t->used = EINA_TRUE;
}

static int
_edje_pick_programs_process(Edje_Part_Collection *edc)
{
   /* This wil mark which samples are used and should be saved */
   unsigned int i;

   for (i = 0; i < edc->programs.fnmatch_count; i++)
     _edje_pick_program_update(edc->programs.fnmatch[i]);

   for (i = 0; i < edc->programs.strcmp_count; i++)
     _edje_pick_program_update(edc->programs.strcmp[i]);

   for (i = 0; i < edc->programs.strncmp_count; i++)
     _edje_pick_program_update(edc->programs.strncmp[i]);

   for (i = 0; i < edc->programs.strrncmp_count; i++)
     _edje_pick_program_update(edc->programs.strrncmp[i]);

   for (i = 0; i < edc->programs.nocmp_count; i++)
     _edje_pick_program_update(edc->programs.nocmp[i]);

   return EDJE_PICK_NO_ERROR;
}

static int
_edje_pick_collection_process(Edje_Part_Collection *edc, Edje_File *edf, Edje_File *o)
{
   /* Update all IDs, NAMES in current collection */
   Edje_Part_Collection_Directory_Entry *ce;

   ce = eina_hash_find(o->collection, edc->part);
   edc->id = ce->id;

   _edje_pick_resources_process(edc, edf, o);
   _edje_pick_programs_process(edc);

   return EDJE_PICK_NO_ERROR;
}

static void
_edje_pick_sound_dir_compose(Eina_List *samples, Eina_List *tones, Edje_File *o)
{  /* Compose sound_dir array from all used samples, tones */
  if (samples)
    {
       Edje_Sound_Sample *sample;
       Edje_Sound_Sample *p;
       Eina_List *l;

       o->sound_dir = calloc(1, sizeof(*(o->sound_dir)));
       o->sound_dir->samples = malloc(eina_list_count(samples) *
                                      sizeof(Edje_Sound_Sample));

       p = o->sound_dir->samples;
       EINA_LIST_FOREACH(samples, l, sample)
         {
            memcpy(p, sample, sizeof(Edje_Sound_Sample));
            p++;
         }

       o->sound_dir->samples_count = eina_list_count(samples);

       if (tones)
         {
            Edje_Sound_Tone *tone;
            Edje_Sound_Tone *t;

            o->sound_dir->tones = malloc(eina_list_count(tones) *
                                         sizeof(Edje_Sound_Tone));

            t = o->sound_dir->tones;
            EINA_LIST_FOREACH(tones, l, tone)
              {
                 memcpy(t, tone, sizeof(Edje_Sound_Tone));
                 t++;
              }

            o->sound_dir->tones_count = eina_list_count(tones);
         }
    }
}

static void
_edje_pick_vibration_dir_compose(Eina_List *vibrations, Edje_File *o)
{  /* Compose vibrationdir array from all used vibrations */
  if (vibrations)
    {
       Edje_Vibration_Sample *sample;
       Edje_Vibration_Sample *p;
       Eina_List *l;

       o->vibration_dir = calloc(1, sizeof(*(o->vibration_dir)));
       o->vibration_dir->samples = malloc(eina_list_count(vibrations) *
                                          sizeof(Edje_Vibration_Sample));

       p = o->vibration_dir->samples;
       EINA_LIST_FOREACH(vibrations, l, sample)
         {
            memcpy(p, sample, sizeof(Edje_Vibration_Sample));
            p++;
         }

       o->vibration_dir->samples_count = eina_list_count(vibrations);
    }
}

int
main(int argc, char **argv)
{
   char *name1, *output_filename = NULL;
   Eina_List *inp_files = NULL;
   int comp_mode = EET_COMPRESSION_DEFAULT;
   Edje_File *out_file = NULL;
   Eina_List *images = NULL;
   Eina_List *samples = NULL;
   Eina_List *tones = NULL;
   Eina_List *vibrations = NULL;

   Edje_Part_Collection *edc;
   Edje_Part_Collection_Directory_Entry *ce;
   Eet_File *ef;
   Eina_List *f, *l;
   char buf[1024];
   int k, bytes;

   ecore_app_no_system_modules();

   eina_init();
   eet_init();
   ecore_init();
   _edje_edd_init();
   eina_log_level_set(EINA_LOG_LEVEL_WARN);  /* Changed to INFO if verbose */

   k = _edje_pick_command_line_parse(argc, argv, &inp_files, &output_filename);
   if ( k != EDJE_PICK_NO_ERROR)
     return k;

   _edje_pick_args_show(inp_files, output_filename);

   /* START - Main loop scanning input files */
   EINA_LIST_FOREACH(inp_files, f, context.current_file)
     {
        Edje_File *edf;

        ef = eet_open(context.current_file->name, EET_FILE_MODE_READ);
        if (!ef)
          return _edje_pick_cleanup(inp_files, out_file,
                                    EDJE_PICK_FAILED_OPEN_INP);

        edf = eet_data_read(ef, _edje_edd_edje_file, "edje/file");
        if (!edf)
          return _edje_pick_cleanup(inp_files, out_file,
                                    EDJE_PICK_FAILED_READ_INP);

        context.current_file->edf = edf;
        edf->ef = ef;

        out_file = _edje_pick_output_prepare(out_file, edf, output_filename);
        if (!out_file)
          {
             _edje_cache_file_unref(edf);
             eet_close(ef);
             return _edje_pick_cleanup(inp_files, out_file, k);
          }

        k = _edje_pick_header_make(out_file, edf, inp_files);
        if (k != EDJE_PICK_NO_ERROR)
          {
             _edje_cache_file_unref(edf);
             eet_close(ef);
             return _edje_pick_cleanup(inp_files, out_file, k);
          }

        /* Build lists of all samples and fonts of input files    */
        _edje_pick_sounds_add(edf);  /* Add Sounds to samplelist          */
        _edje_pick_vibrations_add(edf);  /* Add Vibrations to samplelist  */

        _edje_pick_data_update(out_file, edf);

        /* Process Groups */
        EINA_LIST_FOREACH(context.current_file->groups, l, name1)
          {  /* Read group info */
            ce = eina_hash_find(edf->collection, name1);
            if (!ce || (ce->id < 0))
              {
                 EINA_LOG_ERR("Failed to find group <%s> id\n", name1);
                 _edje_cache_file_unref(edf);
                 return _edje_pick_cleanup(inp_files, out_file,
                                           EDJE_PICK_GROUP_NOT_FOUND);
              }

            VERBOSE(EINA_LOG_INFO("Copy group: <%s>\n", name1));

            edje_cache_emp_alloc(ce);

            snprintf(buf, sizeof(buf), "edje/collections/%i", ce->id);
            EINA_LOG_INFO("Trying to read group <%s>\n", buf);
            edc = eet_data_read(edf->ef, _edje_edd_edje_part_collection, buf);
            if (!edc)
              {
                 EINA_LOG_ERR("Failed to read group <%s> id <%d>\n", name1, ce->id);
                 _edje_cache_file_unref(edf);
                 return _edje_pick_cleanup(inp_files, out_file,
                                           EDJE_PICK_GROUP_NOT_FOUND);
              }

            /* Update IDs */
            _edje_pick_collection_process(edc, edf, out_file);

            /* Build lists of all scripts with new IDs */
            _edje_pick_scripts_add(edf, ce->id, edc->id);
            _edje_pick_lua_scripts_add(edf, ce->id, edc->id);

            {
               /* Write the group to output file using new id */
               snprintf(buf, sizeof(buf),
                        "edje/collections/%i", edc->id);
               bytes = eet_data_write(out_file->ef,
                                      _edje_edd_edje_part_collection,
                                      buf, edc, comp_mode);
               EINA_LOG_INFO("Wrote <%d> bytes for group <%s>\n", bytes, buf);
            }

            free(edc);
            edje_cache_emp_free(ce);
            eet_close(ef);
          }

        _edje_pick_images_copy(edf, out_file);  /* Add Images to imagelist */
        _edje_cache_file_unref(edf);
        _Edje_Pick_Fonts_add(out_file, edf);   /* Add fonts from file to fonts list */

        /* We SKIP writing source, just can't compose it */
        /* FIXME: use Edje_Edit code to generate source */
     } /* END   - Main loop scanning input files */

   /* Write rest of output */

   EINA_LIST_FOREACH(inp_files, f, context.current_file)
     {
        /* Write Scripts from ALL files */
        Edje_Pick_Data *s;
        Edje_Pick_Tone *tn;
        Edje_Pick_Font *fn;
        Eina_List *t;

        EINA_LIST_FOREACH(context.current_file->scriptlist, t, s)
          {
             /* Write Scripts */
             snprintf(buf, sizeof(buf),
                      "edje/scripts/embryo/compiled/%i", s->id.new_id);
             VERBOSE(EINA_LOG_INFO("wrote embryo scr <%s> data <%p> size <%d>\n",
                                   buf, s->data, s->size));
             eet_write(out_file->ef, buf, s->data, s->size, comp_mode);
          }

        EINA_LIST_FOREACH(context.current_file->luascriptlist, t, s)
          {
             /* Write Lua Scripts */
             snprintf(buf, sizeof(buf),
                      "edje/scripts/lua/%i", s->id.new_id);
             VERBOSE(EINA_LOG_INFO("wrote lua scr <%s> data <%p> size <%d>\n",
                                   buf, s->data, s->size));
             eet_write(out_file->ef, buf, s->data, s->size, comp_mode);
          }

        EINA_LIST_FOREACH(context.current_file->imagelist, t, s)
          {
             if (context.current_file->append || s->id.used)
               {
                  snprintf(buf, sizeof(buf), "edje/images/%i", s->id.new_id);
                  eet_write(out_file->ef, buf, s->data, s->size, EINA_TRUE);
                  VERBOSE(EINA_LOG_INFO("Wrote <%s> image data <%p> size <%d>\n", buf, s->data, s->size));
               }
          }

        EINA_LIST_FOREACH(context.current_file->vectorlist, t, s)
          {
             if (context.current_file->append || s->id.used)
               {
                  snprintf(buf, sizeof(buf), "edje/vectors/%i", s->id.new_id);
                  eet_write(out_file->ef, buf, s->data, s->size, EINA_TRUE);
                  VERBOSE(EINA_LOG_INFO("Wrote <%s> vector data <%p> size <%d>\n", buf, s->data, s->size));
               }
          }

        EINA_LIST_FOREACH(context.current_file->fontlist, t, fn)
          {
             if (context.current_file->append || fn->used)
               {
                  snprintf(buf, sizeof(buf), "edje/fonts/%s", fn->f->name);
                  eet_write(out_file->ef, buf, fn->data, fn->size, EINA_TRUE);
                  VERBOSE(EINA_LOG_INFO("Wrote <%s> fonts data <%p> size <%d>\n", buf, fn->data, fn->size));
               }
          }

        EINA_LIST_FOREACH(context.current_file->samplelist, l, s)
          {
             if (context.current_file->append || s->id.used) /* Write only used samples */
               {
                  samples = eina_list_append(samples, s->entry);

                  snprintf(buf, sizeof(buf), "edje/sounds/%i",
                           s->id.new_id);
                  eet_write(out_file->ef, buf,
                            s->data, s->size, EET_COMPRESSION_NONE);
                  VERBOSE(EINA_LOG_INFO("Wrote <%s> sample data <%p> size <%d>\n",
                                        buf, s->data, s->size));
               }
          }

        EINA_LIST_FOREACH(context.current_file->tonelist, l, tn)
          {
             if (context.current_file->append || tn->used)
               tones = eina_list_append(tones, tn->tone);
          }

        EINA_LIST_FOREACH(context.current_file->vibrationlist, l, s)
          {
             if (context.current_file->append || s->id.used) /* Write only used vibrations */
               {
                  vibrations = eina_list_append(vibrations, s->entry);

                  snprintf(buf, sizeof(buf), "edje/vibrations/%i",
                           s->id.new_id);
                  eet_write(out_file->ef, buf,
                            s->data, s->size, EINA_TRUE);
                  VERBOSE(EINA_LOG_INFO("Wrote <%s> vibration data <%p> size <%d>\n",
                                        buf, s->data, s->size));
               }
          }
     }

   _edje_pick_sound_dir_compose(samples, tones, out_file);
   _edje_pick_vibration_dir_compose(vibrations, out_file);

   /* Write file header after processing all groups */
   if (out_file)
     {
        bytes = eet_data_write(out_file->ef, _edje_edd_edje_file, "edje/file",
                               out_file, comp_mode);
        VERBOSE(EINA_LOG_INFO("Wrote <%d> bytes for file header.\n", bytes));
     }

   eina_list_free(images);
   eina_list_free(samples);
   eina_list_free(tones);

   if (output_filename)
     printf("Wrote <%s> output file.\n", output_filename);

   return _edje_pick_cleanup(inp_files, out_file, EDJE_PICK_NO_ERROR);
}

