/* EIO - EFL data type library
 * Copyright (C) 2016 Enlightenment Developers:
 *           Lauro Moura <lauromoura@expertisesolutions.com.br>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library;
 * if not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif


#include <Eo.h>
#include "Ecore.h"
#include "Eio.h"
#include "eio_sentry_private.h"


static const Efl_Event_Description*
_translate_event(int input_event)
{
   if (input_event == EIO_MONITOR_FILE_CREATED)
     return EIO_SENTRY_EVENT_FILE_CREATED;
   else if (input_event == EIO_MONITOR_FILE_DELETED)
     return EIO_SENTRY_EVENT_FILE_DELETED;
   else if (input_event == EIO_MONITOR_FILE_MODIFIED)
     return EIO_SENTRY_EVENT_FILE_MODIFIED;
   else if (input_event == EIO_MONITOR_FILE_CLOSED)
     return EIO_SENTRY_EVENT_FILE_CLOSED;
   else if (input_event == EIO_MONITOR_DIRECTORY_CREATED)
     return EIO_SENTRY_EVENT_DIRECTORY_CREATED;
   else if (input_event == EIO_MONITOR_DIRECTORY_DELETED)
     return EIO_SENTRY_EVENT_DIRECTORY_DELETED;
   else if (input_event == EIO_MONITOR_DIRECTORY_MODIFIED)
     return EIO_SENTRY_EVENT_DIRECTORY_MODIFIED;
   else if (input_event == EIO_MONITOR_DIRECTORY_CLOSED)
     return EIO_SENTRY_EVENT_DIRECTORY_CLOSED;
   else if (input_event == EIO_MONITOR_SELF_RENAME)
     return EIO_SENTRY_EVENT_SELF_RENAME;
   else if (input_event == EIO_MONITOR_SELF_DELETED)
     return EIO_SENTRY_EVENT_SELF_DELETED;
   else if (input_event == EIO_MONITOR_ERROR)
     return EIO_SENTRY_EVENT_ERROR;
   else
     return NULL;
}

static unsigned char
_handle_event(void *data, int type, void *event)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_PASS_ON);
   EINA_SAFETY_ON_NULL_RETURN_VAL(event, ECORE_CALLBACK_PASS_ON);

   const Efl_Event_Description* translated_event = _translate_event(type);
   Eio_Sentry_Data *pd = (Eio_Sentry_Data *)data;
   Eio_Monitor_Event *monitor_event = (Eio_Monitor_Event *)event;

   Eio_Sentry_Event *event_info = malloc(sizeof(Eio_Sentry_Event));
   EINA_SAFETY_ON_NULL_RETURN_VAL(event_info, ECORE_CALLBACK_PASS_ON);

   event_info->source = eio_monitor_path_get(monitor_event->monitor);
   event_info->trigger = monitor_event->filename;

   efl_event_callback_call(pd->object, translated_event, event_info);

   // If event was error, we must delete the monitor.
   if (type == EIO_MONITOR_ERROR)
     eina_hash_del(pd->targets, event_info->source, NULL);

   free(event_info);

   return ECORE_CALLBACK_PASS_ON;
}

static void
_initialize_handlers(Eio_Sentry_Data *pd)
{
   Ecore_Event_Handler *h;
   EINA_SAFETY_ON_NULL_RETURN(pd);
   pd->handlers = eina_array_new(11);

   h = ecore_event_handler_add(EIO_MONITOR_FILE_CREATED, _handle_event, pd);
   eina_array_push(pd->handlers, h);
   h = ecore_event_handler_add(EIO_MONITOR_FILE_DELETED, _handle_event, pd);
   eina_array_push(pd->handlers, h);
   h = ecore_event_handler_add(EIO_MONITOR_FILE_MODIFIED, _handle_event, pd);
   eina_array_push(pd->handlers, h);
   h = ecore_event_handler_add(EIO_MONITOR_FILE_CLOSED, _handle_event, pd);
   eina_array_push(pd->handlers, h);

   h = ecore_event_handler_add(EIO_MONITOR_DIRECTORY_CREATED, _handle_event, pd);
   eina_array_push(pd->handlers, h);
   h = ecore_event_handler_add(EIO_MONITOR_DIRECTORY_DELETED, _handle_event, pd);
   eina_array_push(pd->handlers, h);
   h = ecore_event_handler_add(EIO_MONITOR_DIRECTORY_MODIFIED, _handle_event, pd);
   eina_array_push(pd->handlers, h);
   h = ecore_event_handler_add(EIO_MONITOR_DIRECTORY_CLOSED, _handle_event, pd);
   eina_array_push(pd->handlers, h);

   h = ecore_event_handler_add(EIO_MONITOR_SELF_RENAME, _handle_event, pd);
   eina_array_push(pd->handlers, h);
   h = ecore_event_handler_add(EIO_MONITOR_SELF_DELETED, _handle_event, pd);
   eina_array_push(pd->handlers, h);
   h = ecore_event_handler_add(EIO_MONITOR_ERROR, _handle_event, pd);
   eina_array_push(pd->handlers, h);
}

Eina_Bool
_eio_sentry_add(Eo *obj EINA_UNUSED, Eio_Sentry_Data *pd, const char *path)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(path, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINA_FALSE);

   if (!pd->handlers)
     _initialize_handlers(pd);

   if (eina_hash_find(pd->targets, path))
     return EINA_TRUE;

   Eio_Monitor *monitor = eio_monitor_add(path);

   if (!monitor)
     {
        EINA_LOG_ERR("Failed to create monitor.");
        return EINA_FALSE;
     }

   if (!eina_hash_add(pd->targets, path, monitor))
     {
        EINA_LOG_ERR("Failed to register monitor.");
        eio_monitor_del(monitor);
        return EINA_FALSE;
     }

   return EINA_TRUE;
}

void
_eio_sentry_remove(Eo *obj EINA_UNUSED, Eio_Sentry_Data *pd, const char *path)
{
   EINA_SAFETY_ON_NULL_RETURN(path);
   EINA_SAFETY_ON_NULL_RETURN(pd);

   eina_hash_del(pd->targets, path, NULL);
}

Eina_Bool
_eio_sentry_fallback_check(const Eo *obj EINA_UNUSED, Eio_Sentry_Data *pd, const char *path)
{
   Eio_Monitor *monitor;

   EINA_SAFETY_ON_NULL_RETURN_VAL(path, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINA_FALSE);

   monitor = eina_hash_find(pd->targets, path);
   EINA_SAFETY_ON_NULL_RETURN_VAL(monitor, EINA_FALSE);
   return eio_monitor_fallback_check(monitor);
}

Efl_Object * _eio_sentry_efl_object_constructor(Eo *obj, Eio_Sentry_Data *pd)
{
   obj = efl_constructor(efl_super(obj, EIO_SENTRY_CLASS));

   pd->object = obj;
   pd->targets = eina_hash_string_small_new((Eina_Free_Cb)&eio_monitor_del);

   return obj;
}

void _eio_sentry_efl_object_destructor(Eo *obj, Eio_Sentry_Data *pd)
{
   eina_hash_free(pd->targets);
   if (pd->handlers)
     {
        while (eina_array_count(pd->handlers))
          ecore_event_handler_del(eina_array_pop(pd->handlers));
        eina_array_free(pd->handlers);
     }

   efl_destructor(efl_super(obj, EIO_SENTRY_CLASS));
}

#include "eio_sentry.eo.c"
