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

static void free_file(void *data);
static void ecdb_custom_filelist_cb_clicked(Ewl_Widget *w, void *ev,
                                       void *data);
static void ecdb_custom_filelist_cb_key_down(Ewl_Widget *w, void *ev,
                                       void *data);
static Ewl_Widget *ecdb_custom_filelist_cb_widget_fetch(unsigned int col,
                                                            void *pr_data);
static void ecdb_custom_filelist_cb_widget_assign(Ewl_Widget *w, void *data,
                                       unsigned int row, unsigned int col,
                                       void *pr_data);
static void *ecdb_custom_filelist_model_data_fetch(void *data, unsigned int row,
                                       unsigned int column);

static void ecdb_custom_filelist_model_filter(Ewl_Filelist_Directory *dir);
static unsigned int ecdb_custom_filelist_model_data_unref(void *data);
static Ewl_Filelist_Directory *ecdb_custom_filelist_directory_new
                                       (Ecdb_Source *src);
static void ecdb_custom_filelist_dnd_dropped_cb(Ewl_Widget *w, void *ev,
                                       void *data);

Ewl_Widget *
ecdb_custom_filelist_new(void)
{
   Ewl_Widget *ret;

   ret = ewl_filelist_new();
   if (!ret)
   {
      EINA_ERROR_PWARN("ecdb_custom_filelist_new: NULL return!\n");
      return NULL;
   }

   /* Ouch, this took forever to find */
   ewl_model_data_unref_set(EWL_FILELIST(ret)->model,
                                    ecdb_custom_filelist_model_data_unref);
   ewl_model_data_fetch_set(EWL_FILELIST(ret)->model,
                                    ecdb_custom_filelist_model_data_fetch);
   ewl_view_widget_constructor_set(EWL_FILELIST(ret)->view,
                                       ecdb_custom_filelist_cb_widget_fetch);
   ewl_view_widget_assign_set(EWL_FILELIST(ret)->view,
                                       ecdb_custom_filelist_cb_widget_assign);
   ewl_callback_append(ret, EWL_CALLBACK_DND_DATA_RECEIVED,
                                    ecdb_custom_filelist_dnd_dropped_cb, NULL);
   em->ewl_dnd_candidates = eina_list_append(em->ewl_dnd_candidates, ret);
   ewl_callback_del_type(EWL_FILELIST(ret)->controller, EWL_CALLBACK_CLICKED);
   ewl_callback_prepend(EWL_FILELIST(ret)->controller, EWL_CALLBACK_CLICKED,
                                    ecdb_custom_filelist_cb_clicked, ret);
   ewl_callback_append(EWL_FILELIST(ret)->controller, EWL_CALLBACK_KEY_DOWN,
                                    ecdb_custom_filelist_cb_key_down, ret);
   return ret;
}

static void
ecdb_custom_filelist_dnd_dropped_cb(Ewl_Widget *w, void *ev,
                     void *data__UNUSED__)
{
   int i;
   Ewl_Event_Dnd_Data_Received *dnd = ev;
   char **files = dnd->data;
   Ecdb_Source *parent, *child;
   Efreet_Uri *uri;

   /* Get the parent, find the path of the file(s) dropped
    * and add them as children to parent */
   parent = ewl_widget_data_get(w, "src");

   for (i = 0; i < dnd->len; i++)
   {
      uri = efreet_uri_decode(files[i]);
      if (!ecore_file_exists(uri->path))
      {
         efreet_uri_free(uri);
         continue;
      }
      child = ecdb_source_new();
      ecdb_source_data_set(child, uri->path);
      ecdb_source_child_append(parent, child);
      efreet_uri_free(uri);
   }

   ecdb_custom_filelist_directory_set(EWL_FILELIST(w), parent);

   /* This entire thing here is a hack... */
   ewl_filelist_model_data_sort(ewl_mvc_data_get
                                       (EWL_MVC(EWL_FILELIST(w)->controller)),
                                       0, EWL_SORT_DIRECTION_ASCENDING);
   ewl_mvc_dirty_set(EWL_MVC(EWL_FILELIST(w)->controller), TRUE);
   ewl_widget_configure(EWL_FILELIST(w)->controller);
   /* End hack stuff */

   // Send out this event so that we can get notified on size changes
   // and update the capacity measure
   ewl_callback_call_with_event_data(w, ECDB_FILELIST_SIZE_CHANGED, parent);
}

void
ecdb_custom_filelist_directory_set(Ewl_Filelist *fl, Ecdb_Source *src)
{
   if (src)
   {
      Ewl_Filelist_Directory *data;
      Ewl_Event_Action_Response ev_data;

      data = ewl_mvc_data_get(EWL_MVC(fl->controller));
      if (data)
      {
         ecdb_custom_filelist_model_data_unref(data);
      }

      data = ecdb_custom_filelist_directory_new(src);
      ewl_mvc_data_set(EWL_MVC(fl->controller), data);
      ev_data.response = EWL_FILELIST_EVENT_DIR_CHANGE;
      ewl_callback_call_with_event_data(EWL_WIDGET(fl),
                                       EWL_CALLBACK_VALUE_CHANGED, &ev_data);
      
      /* Set the source as needed for file operations */
      ewl_widget_data_set(EWL_WIDGET(fl), "src", src);
   }
}

static Ewl_Filelist_Directory *
ecdb_custom_filelist_directory_new(Ecdb_Source *parent)
{
   Ecdb_Source *src;
   Ewl_Filelist_Directory *dir;
   Ewl_Filelist_File *file;

   struct stat st;
   int nf = 0, nd = 0, i = 0;
   Ecore_List *files, *dirs;

   files = ecore_list_new();
   dirs = ecore_list_new();
   ecore_list_free_cb_set(files, free_file);
   ecore_list_free_cb_set(dirs, free_file);

   if (!parent)
   {
      return NULL;
   }

   while ((src = parent->children[i]))
   {
      file = calloc(1, sizeof(Ewl_Filelist_File));
      file->name = eina_stringshare_add(src->dst);

      stat(src->dst, &st);
      file->size = st.st_size;
      file->modtime = st.st_mtime;
      file->mode = st.st_mode;
      file->groupname = st.st_gid;
      file->username = st.st_uid;
      file->is_dir = src->dir;
      file->readable = ecore_file_can_read(src->dst);
      file->writeable = ecore_file_can_write(src->dst);

      if (src->dir)
      {
         ecore_list_append(dirs, file);
         nd++;
      }
      else
      {
         ecore_list_append(files, file);
         nf++;
      }

      i++;
   }

   dir = calloc(1, sizeof(Ewl_Filelist_Directory));
   dir->rfiles = files;
   dir->rdirs = dirs;
   dir->files = ecore_list_new();
   dir->dirs = ecore_list_new();
   dir->num_dirs = nd;
   dir->num_files = nf;

   ecdb_custom_filelist_model_filter(dir);

   return dir;
}

static void
free_file(void *data)
{
   Ewl_Filelist_File *file;

   file = data;
   eina_stringshare_del(file->name);
   FREE(file);
}

static void
ecdb_custom_filelist_model_filter(Ewl_Filelist_Directory *dir)
{
   Ewl_Filelist_File *file;
   int nd, nf;
   
   if (!dir->files || !dir->dirs)
      return;
   ecore_list_clear(dir->files);
   ecore_list_clear(dir->dirs);
   nd = nf = 0;

   if (!dir->show_dot)
   {
      ecore_list_first_goto(dir->rfiles);
      while ((file = ecore_list_next(dir->rfiles)))
      {
         if ((file->name) && (ecore_file_file_get(file->name)[0] != '.'))
         {
            ecore_list_append(dir->files, file);
            nf++;
         }
      }

      ecore_list_first_goto(dir->rdirs);
      while ((file = ecore_list_next(dir->rdirs)))
      {
         if ((file->name) && (ecore_file_file_get(file->name)[0] != '.'))
         {
            ecore_list_append(dir->dirs, file);
            nd++;
         }
      }
   }
   else
   {
      ecore_list_first_goto(dir->rfiles);
      while ((file = ecore_list_next(dir->rfiles)))
      {
         ecore_list_append(dir->files, file);
         nf++;
      }

      ecore_list_first_goto(dir->rdirs);
      while ((file = ecore_list_next(dir->rdirs)))
      {
         ecore_list_append(dir->dirs, file);
         nd++;
      }
   }

   dir->num_dirs = nd;
   dir->num_files = nf;
}

static unsigned int
ecdb_custom_filelist_model_data_unref(void *data)
{
   Ewl_Filelist_Directory *dir;

   dir = data;
   ecore_list_destroy(dir->files);
   ecore_list_destroy(dir->dirs);
   ecore_list_destroy(dir->rfiles);
   ecore_list_destroy(dir->rdirs);
   FREE(dir);
   return TRUE;
}

static void *
ecdb_custom_filelist_model_data_fetch(void *data, unsigned int row,
                  unsigned int column)
{
   Ewl_Filelist_Directory *fld;
   Ewl_Filelist_File *file;
   int i;
   void *ret;

   fld = data;

   /* Check if in dirs or files list */
   if (row < fld->num_dirs)
   {
      file = ecore_list_index_goto(fld->dirs, row);
   }
   else
   {
      i = row - fld->num_dirs;
      file = ecore_list_index_goto(fld->files, i);
   }

   if (column == 1) ret = ewl_filelist_size_get(file->size);
   else if (column == 2) ret = ewl_filelist_perms_get(file->mode);
   else if (column == 3) ret = ewl_filelist_username_get(file->username);
   else if (column == 4) ret = ewl_filelist_groupname_get(file->groupname);
   else if (column == 5) ret = ewl_filelist_modtime_get(file->modtime);
   else ret = strdup(file->name);

   /* ret needs to be freed by the view or with model_data_free_set */
   return ret;
}

static void
ecdb_custom_filelist_cb_key_down(Ewl_Widget *w, void *ev, void *data)
{
   Ewl_Event_Key_Down *kd;
   Ewl_Filelist *fl;
   char *file;
   int i;
   Ecdb_Source *parent, *child;

   kd = ev;
   fl = data;

   if (!ewl_mvc_selected_count_get(EWL_MVC(w)))
   {
      return;
   }

   if (!strcmp(kd->base.keyname, "Delete"))
   {
      file = ecdb_custom_filelist_selected_file_get(fl);
      parent = ewl_widget_data_get(EWL_WIDGET(fl), "src");
      
      for (i = 0; (child = parent->children[i]); i++)
      {
         if (!strcmp(child->dst, file))
         {
            ecdb_source_child_remove(parent, child);
            ecdb_source_destroy(child);
            ecdb_custom_filelist_directory_set(fl, parent);
            ewl_callback_call_with_event_data(EWL_WIDGET(fl),
                                          ECDB_FILELIST_SIZE_CHANGED, parent);
            break;
         }
      }
   }
}

static void
ecdb_custom_filelist_cb_clicked(Ewl_Widget *w, void *ev, void *data)
{
   Ewl_Event_Mouse_Down *md;
   char *file;
   int i = 0;
   Ewl_Filelist *fl;
   Ecdb_Source *parent, *child;

   md = ev;
   fl = data;

   if ((!ewl_mvc_selected_count_get(EWL_MVC(w))) || (md->clicks != 2))
   {
      ewl_filelist_selected_files_change_notify(fl);
      return;
   }

   file = ecdb_custom_filelist_selected_file_get(fl);

   parent = ewl_widget_data_get(EWL_WIDGET(fl), "src");
   i = 0;
   while ((child = parent->children[i]))
   {
      if ((child->dir) && (!strcmp(child->dst, file)))
      {
         ecdb_custom_filelist_directory_set(fl, child);
         FREE(file);
         return;
      }
      i++;
   }
   ewl_filelist_selected_files_change_notify(fl);
   FREE(file);
}

char *
ecdb_custom_filelist_selected_file_get(Ewl_Filelist *fl)
{
   Ewl_Filelist_Directory *data;
   Ewl_Filelist_File *file;
   Ewl_Selection_Idx *idx;
   int i;

   idx = ewl_mvc_selected_get(EWL_MVC(fl->controller));
   data = EWL_SELECTION(idx)->data;
   if (idx->row < data->num_dirs)
   {
      file = ecore_list_index_goto(data->dirs, idx->row);
   }
   else
   {
      i = (idx->row - data->num_dirs);
      file = ecore_list_index_goto(data->files, i);
   }
   
   FREE(idx);
   return strdup(file->name);
}

static Ewl_Widget *ecdb_custom_filelist_cb_widget_fetch(
                                             unsigned int col __UNUSED__,
                                                      void *pr_data __UNUSED__)
{
   Ewl_Widget *ret;

   ret = ewl_icon_simple_new();
   ewl_icon_constrain_set(EWL_ICON(ret), EWL_ICON_SIZE_MEDIUM);
   ewl_box_orientation_set(EWL_BOX(ret), EWL_ORIENTATION_HORIZONTAL);
   ewl_object_alignment_set(EWL_OBJECT(ret), EWL_FLAG_ALIGN_LEFT);

   return ret;
}

static void ecdb_custom_filelist_cb_widget_assign(Ewl_Widget *w, void *data,
                                       unsigned int row __UNUSED__,
                                       unsigned int col, void *pr_data)
{
   const char *img = NULL, *stock, *filename;

   if (col == 0)
   {
      stock = ewl_filelist_stock_icon_get(data);
      img = ewl_icon_theme_icon_path_get(stock, EWL_ICON_SIZE_MEDIUM);
      if (img)
      {
         ewl_icon_image_set(EWL_ICON(w), img, NULL);
      }
      filename = ecore_file_file_get(data);
      ewl_icon_label_set(EWL_ICON(w), filename);
   }
   else
   {
      ewl_icon_label_set(EWL_ICON(w), data);
   }

   FREE(data);
}

