/* vim: set sw=3 ts=3 sts=3 expandtab: */
#include "ecdb.h"

int ecdb_source_init(Ecdb_Source *src);
void ecdb_source_add_directory_recursive(Ecdb_Source *parent);

Ecdb_Source *
ecdb_source_new(void)
{
   Ecdb_Source *src;

   src = calloc(1, sizeof(Ecdb_Source));
   if (!src)
   {
      return NULL;
   }
   if (!ecdb_source_init(src))
   {
      FREE(src);
      return NULL;
   }

   return src;
}

int
ecdb_source_init(Ecdb_Source *src)
{
   src->dir = FALSE;
   src->num_children = 0;
   src->size = 0;
   src->children = calloc(1, sizeof(Ecdb_Source));
   if (!src->children)
   {
      return FALSE;
   }
   src->children[src->num_children] = NULL;
   return TRUE;
}

void
ecdb_source_destroy(Ecdb_Source *src)
{
   int i;
   Ecdb_Source *child;

   if (!src)
   {
      EINA_ERROR_PWARN("srs NULL!\n");
      return;
   }

   /* free the non-recursive stuff */
   if (src->dst)
   {
      eina_stringshare_del(src->dst);
      src->dst = NULL;
   }

   /* Not sure what to do here.  The nodes should already be dealt with
    * in normal operation.  Maybe have another function that specifically
    * deals with this in error cases.  Anyways, commented out for now
    */
   //if (src->node) iso_node_unref(src->node);
   if (src->children)
   {
      for (i = 0; src->children[i]; i++)
      {
         child = src->children[i];
         ecdb_source_destroy(child);
      }
      FREE(src->children);
   }
   FREE(src);
}

void
ecdb_source_data_set(Ecdb_Source *src, const char *dst)
{
   if ((!src) || (!ecore_file_exists(dst)))
   {
      return;
   }

   src->dst = eina_stringshare_add(dst);
   src->size = ecore_file_size(dst);

   /* Add the files recursively here */
   if (ecore_file_is_dir(src->dst))
   {
      src->dir = !!TRUE;
      ecdb_source_add_directory_recursive(src);
   }
}

void
ecdb_source_add_directory_recursive(Ecdb_Source *parent)
{
   Eina_List *files;
   Ecdb_Source *child;
   char path[PATH_MAX];
   char *src;

   if (!parent)
   {
      EINA_ERROR_PWARN("parent NULL!\n");
      return;
   }

   files = ecore_file_ls(parent->dst);

   EINA_LIST_FREE(files, src)
   {
      child = ecdb_source_new();
      snprintf(path, PATH_MAX, "%s/%s", parent->dst, src);
      ecdb_source_data_set(child, path);
      ecdb_source_child_append(parent, child);
      FREE(src);
   }
}

void
ecdb_source_child_append(Ecdb_Source *src, Ecdb_Source *child)
{
   long long orig, diff;
   Ecdb_Source *p;

   if (!src)
   {
      EINA_ERROR_PWARN("src NULL!\n");
      return;
   }

   if (!child)
   {
      EINA_ERROR_PWARN("child NULL!\n");
      return;
   }

   if (src == child)
   {
      EINA_ERROR_PWARN("Trying to make a parent of itself!\n");
      return;
   }

   orig = src->size;
   src->num_children++;
   /* This is really inefficient when adding directories with lots and lots 
    * of child files.  Its good enough not to be a hugely critical thing,
    * but before release I need to test against using a list, or before
    * adding a directory count the list and malloc the entire size.  Also
    * need to worry about multi-file drag and drop.
    */
   src->children = realloc(src->children, sizeof(Ecdb_Source) * 
                  (src->num_children + 1));
   src->children[src->num_children - 1] = child;
   src->children[src->num_children] = NULL;
   src->size += child->size;
   child->parent = src;

   diff = src->size - orig;
   p = src;
   while (p->parent)
   {
      p = p->parent;
      p->size += diff;
   }
}

/* Basically here we can remove all occurences (who knows why we'd get
 * multiple, but whatever), or just the first.  For now remove the first, and
 * see how that goes
 */
void
ecdb_source_child_remove(Ecdb_Source *src, Ecdb_Source *child)
{
   Ecdb_Source **temp;
   Ecdb_Source *s;
   int i, cidx, f;

   if (!src)
   {
      EINA_ERROR_PWARN("src NULL!\n");
      return;
   }

   if (!child)
   {
      EINA_ERROR_PWARN("child NULL!\n");
      return;
   }

   if (src == child)
   {
      EINA_ERROR_PWARN("Trying to remove oneself\n");
      return;
   }

   temp = calloc(src->num_children, sizeof(Ecdb_Source));
   temp[src->num_children - 1] = NULL;

   cidx = f = 0;
   for (i = 0; src->children[i]; i++)
   {
      if ((src->children[i] == child) && (!f))
      {
         f++;
         continue;
      }
      temp[cidx] = src->children[i];
      cidx++;
   }

   FREE(src->children);
   src->children = temp;
   src->num_children--;

   // Propogate the size changes back up to the root
   s = src;
   do {
      s->size -= child->size;

      if (s->parent)
         s = s->parent;
      else
         break;
   } while (1);

   child->parent = NULL;
}

void
ecdb_source_add_children_rec(Ecdb_Source *parent, IsoImage *image)
{
   IsoDir *cd = NULL;
   Ecdb_Source *cs;
   int i = 0;

   if (!parent)
   {
      EINA_ERROR_PWARN("parent NULL!\n");
      return;
   }

   if (!image)
   {
      EINA_ERROR_PWARN("image NULL!\n");
      return;
   }

   while ((cs = parent->children[i]))
   {
      if (cs->dir)
      {
         iso_tree_add_new_dir(ISO_DIR(parent->node),
                                          ecore_file_file_get(cs->dst), &cd);
         cs->node = ISO_NODE(cd);
         /* If the source has children, find the node from above
          * (if applicable), and recursively add to it */
         if (cs->num_children > 0)
         {
            ecdb_source_add_children_rec(cs, image);
         }

      }
      
      /* file */
      else
      {
         iso_tree_add_node(image, ISO_DIR(parent->node),
                     cs->dst, NULL);
      }
      i++;
   }
}

/* proj->files should only have children */
BurnSource *
ecdb_image_project(Ecdb_Burn_Project *bp)
{
   IsoImage *image;
   Ecdb_Source *c;
   IsoWriteOpts *opts;
   BurnSource *data_src, *fifo_src;
   Ecdb_Data_Project *proj;

   if (!bp)
   {
      EINA_ERROR_PWARN("bp NULL!\n");
      return NULL;
   }

   if ((!bp->files) || (!bp->files->num_children))
   {
      return NULL;
   }

   /* To handle already-suplied image files */
   if ((bp->files->num_children == 1) && (ECDB_PROJECT(bp)->type ==
                                                         ECDB_IMAGE_PROJECT))
   {
      efreet_mime_init();
      c = bp->files->children[0];
      if ((!ecore_file_is_dir(c->dst)) &&
            (!strcmp(efreet_mime_type_get(c->dst), "application/x-cd-image")))
      {
         data_src = burn_file_source_new(c->dst, NULL);
         goto FIFO_CREATE;
      }
      else
      {
         EINA_ERROR_PWARN("Supplied file is not an image!\n");
         efreet_mime_shutdown();
         return NULL;
      }
      efreet_mime_shutdown();
   }
   
   if (ECDB_PROJECT(bp)->type == ECDB_DATA_PROJECT)
   {
      proj = ECDB_DATA(bp);
   }
   else
   {
      EINA_ERROR_PWARN("Incorrect project type!\n");
      return NULL;
   }

   /* Otherwise we have a bunch of files */
   if (!iso_image_new(proj->volume_id, &image))
   {
      EINA_ERROR_PWARN("Failed to create image!\n");
      return NULL;
   }

   /* Set all ids */
   if (proj->publisher_id)
   {
      iso_image_set_publisher_id(image, proj->publisher_id);
   }
   if (proj->data_preparer_id)
   {
      iso_image_set_data_preparer_id(image, proj->data_preparer_id);
   }
   if (proj->system_id)
   {
      iso_image_set_system_id(image, proj->system_id);
   }
   if (proj->application_id)
   {
      iso_image_set_application_id(image, proj->application_id);
   }
   if (proj->copywrite_id)
   {
      iso_image_set_copyright_file_id(image, proj->copywrite_id);
   }
   if (proj->abstract_id)
   {
      iso_image_set_abstract_file_id(image, proj->abstract_id);
   }
   if (proj->biblio_id)
   {
      iso_image_set_biblio_file_id(image, proj->biblio_id);
   }

   if (!iso_write_opts_new(&opts, 2))
   {
      EINA_ERROR_PWARN("Failed to create writing options!\n");
      iso_image_unref(image);
      return NULL;
   }

   /* And some write opts stuff */
   iso_write_opts_set_iso_level(opts, proj->iso_level);
   iso_write_opts_set_joliet(opts, proj->use_joliet);
   iso_write_opts_set_rockridge(opts, proj->use_rockridge);
   iso_write_opts_set_iso1999(opts, proj->iso1990);
   iso_write_opts_set_appendable(opts, bp->multi);
   
   iso_tree_set_follow_symlinks(image, proj->follow_symlinks);
   iso_tree_set_ignore_hidden(image, proj->ignore_hidden);
   iso_tree_set_ignore_special(image, proj->ignore_special);

   /* actually fill image with some files now */
   bp->files->node = ISO_NODE(iso_image_get_root(image));
   ecdb_source_add_children_rec(bp->files, image);
   
   /* Make the burn source here */
   iso_image_create_burn_source(image, opts, &data_src);
   iso_write_opts_free(opts);
   iso_image_unref(image);

   /* And, convert to fifo */
FIFO_CREATE:
   fifo_src = burn_fifo_source_new(data_src, bp->fifo_chunksize,
                                                         bp->fifo_chunks, 0);
   burn_source_free(data_src);
   return fifo_src;
}

