/*
   Unix SMB/CIFS implementation.

   common events code for immediate events

   Copyright (C) Stefan Metzmacher 2009

     ** NOTE! The following LGPL license applies to the tevent
     ** library. This does NOT imply that all of Samba is released
     ** under the LGPL

   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 3 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/>.
*/

#include "replace.h"
#include "tevent.h"
#include "tevent_internal.h"
#include "tevent_util.h"

static void tevent_common_immediate_cancel(struct tevent_immediate *im)
{
	if (!im->event_ctx) {
		return;
	}

	tevent_debug(im->event_ctx, TEVENT_DEBUG_TRACE,
		     "Cancel immediate event %p \"%s\"\n",
		     im, im->handler_name);

	/* let the backend free im->additional_data */
	if (im->cancel_fn) {
		im->cancel_fn(im);
	}

	DLIST_REMOVE(im->event_ctx->immediate_events, im);
	im->event_ctx		= NULL;
	im->handler		= NULL;
	im->private_data	= NULL;
	im->handler_name	= NULL;
	im->schedule_location	= NULL;
	im->cancel_fn		= NULL;
	im->additional_data	= NULL;

	talloc_set_destructor(im, NULL);
}

/*
  destroy an immediate event
*/
static int tevent_common_immediate_destructor(struct tevent_immediate *im)
{
	tevent_common_immediate_cancel(im);
	return 0;
}

/*
 * schedule an immediate event on
 */
void tevent_common_schedule_immediate(struct tevent_immediate *im,
				      struct tevent_context *ev,
				      tevent_immediate_handler_t handler,
				      void *private_data,
				      const char *handler_name,
				      const char *location)
{
	tevent_common_immediate_cancel(im);

	if (!handler) {
		return;
	}

	im->event_ctx		= ev;
	im->handler		= handler;
	im->private_data	= private_data;
	im->handler_name	= handler_name;
	im->schedule_location	= location;
	im->cancel_fn		= NULL;
	im->additional_data	= NULL;

	DLIST_ADD_END(ev->immediate_events, im, struct tevent_immediate *);
	talloc_set_destructor(im, tevent_common_immediate_destructor);

	tevent_debug(ev, TEVENT_DEBUG_TRACE,
		     "Schedule immediate event \"%s\": %p\n",
		     handler_name, im);
}

/*
  trigger the first immediate event and return true
  if no event was triggered return false
*/
bool tevent_common_loop_immediate(struct tevent_context *ev)
{
	struct tevent_immediate *im = ev->immediate_events;
	tevent_immediate_handler_t handler;
	void *private_data;

	if (!im) {
		return false;
	}

	tevent_debug(ev, TEVENT_DEBUG_TRACE,
		     "Run immediate event \"%s\": %p\n",
		     im->handler_name, im);

	/*
	 * remember the handler and then clear the event
	 * the handler might reschedule the event
	 */
	handler = im->handler;
	private_data = im->private_data;

	DLIST_REMOVE(im->event_ctx->immediate_events, im);
	im->event_ctx		= NULL;
	im->handler		= NULL;
	im->private_data	= NULL;
	im->handler_name	= NULL;
	im->schedule_location	= NULL;
	im->cancel_fn		= NULL;
	im->additional_data	= NULL;

	talloc_set_destructor(im, NULL);

	handler(ev, im, private_data);

	return true;
}