#ifndef __EVENTS_INCLUDED_H__
#define __EVENTS_INCLUDED_H__

#ifdef	__cplusplus
extern "C" {
#endif

#include "stdlib/acl_define.h"
#include <time.h>
#include "stdlib/acl_ring.h"
#include "stdlib/acl_vstream.h"
#include "stdlib/acl_fifo.h"
#include "stdlib/acl_avl.h"
#include "thread/acl_thread.h"
#include "event/acl_events.h"

#include "events_dog.h"
#include "fdmap.h"
#include "events_define.h"
#include "events_epoll.h"
#include "events_devpoll.h"

/*
 * I/O events. We pre-allocate one data structure per file descriptor. XXX
 * For now use FD_SETSIZE as defined along with the fd-set type.
 */

#if defined(_WIN32) || defined(_WIN64)
typedef struct IOCP_EVENT IOCP_EVENT;
#endif

#ifndef DELAY_MIN
#define DELAY_MIN	1000
#endif

struct ACL_EVENT_FDTABLE {
	ACL_VSTREAM *stream;
	ACL_EVENT_NOTIFY_RDWR r_callback;
	ACL_EVENT_NOTIFY_RDWR w_callback;
	void *r_context;
	void *w_context;
	acl_int64 r_ttl;
	acl_int64 w_ttl;
	acl_int64 r_timeout;
	acl_int64 w_timeout;
	int   event_type;
	int   listener;			/* if the fd is a listening fd */
	int   flag;
#define EVENT_FDTABLE_FLAG_READ         (1 << 1)
#define EVENT_FDTABLE_FLAG_WRITE        (1 << 2)
#define EVENT_FDTABLE_FLAG_EXPT         (1 << 3)
#define EVENT_FDTABLE_FLAG_ADD_READ     (1 << 4)
#define EVENT_FDTABLE_FLAG_ADD_WRITE    (1 << 5)
#define EVENT_FDTABLE_FLAG_DEL_READ     (1 << 6)
#define EVENT_FDTABLE_FLAG_DEL_WRITE    (1 << 7)
#define EVENT_FDTABLE_FLAG_DELAY_OPER   (1 << 8)
#define EVENT_FDTABLE_FLAG_IOCP         (1 << 9)
#define	EVENT_FDTABLE_FLAG_FIRE		(1 << 10)

	int   fdidx;
	int   fdidx_ready;
	ACL_RING delay_entry;

#if defined(_WIN32) || defined(_WIN64)
	HANDLE h_iocp;
	IOCP_EVENT *event_read;
	IOCP_EVENT *event_write;
#endif
};

typedef struct EVENT_TIMERS EVENT_TIMERS;

struct	ACL_EVENT {
	/* ¼Ʊʶ */
	char  name[128];

	/**
	 * ¼:
	 * ACL_EVENT_SELECT, ACL_EVENT_KERNEL,
	 * ACL_EVENT_POLL, ACL_EVENT_WMSG
	 */
	int   event_mode;
	/* ¼Ƿڶ̻߳ʹ */
	int   use_thread;

	/* ¼汻Ƕ׵ */
	int   nested;
	/* ǰʱ(΢뼶) */
	acl_int64 present;
	acl_int64 last_check;
	acl_int64 check_inter;
	acl_int64 last_debug;
	/* ¼ȴʱ() */
	int   delay_sec;
	/* ¼ȴʱ(΢) */
	int   delay_usec;
	/* ѭǰݿɶָ */
	int   read_ready;

	/* ʱ */
	EVENT_TIMERS *timers;
	/* ҪĶʱ */
	ACL_RING timers_ready;

#if defined(_WIN32) || defined(_WIN64)
	ACL_RING timer_head;
#endif

	/* ׽ */
	int   fdsize;
	/* ǰ׽ָ */
	int   fdcnt;
	/* һȫʱ±λ */
	int   fdpos;
	/* ¼ѭʱ׼õ׽ָ */
	int   ready_cnt;
	/* ׽¼ */
	ACL_EVENT_FDTABLE **fdtabs;
	/* ׼õ׽¼ */
	ACL_EVENT_FDTABLE **ready;
	/* ׽ֵ */
	ACL_SOCKET   maxfd;

	/* ¼ѭ */
	void (*loop_fn)(ACL_EVENT *ev);
	/* Щ¼Ҫǰгʼ */
	void (*init_fn)(ACL_EVENT *ev);
	/* ͷ¼ */
	void (*free_fn)(ACL_EVENT *ev);
	/* ڶ߳ʹ¼ʱҪ˽ӿڼʱ¼ */
	void (*add_dog_fn)(ACL_EVENT *ev);

	/* ʼĳ׽ֵĿɶ״̬ */
	void (*enable_read_fn)(ACL_EVENT *, ACL_VSTREAM *, int,
		ACL_EVENT_NOTIFY_RDWR, void *);
	/* ʼĳ׽ֵĿд״̬ */
	void (*enable_write_fn)(ACL_EVENT *, ACL_VSTREAM *, int,
		ACL_EVENT_NOTIFY_RDWR, void *);
	/* ʼؼ׽ӿڵĿɶ״̬ */
	void (*enable_listen_fn)(ACL_EVENT *, ACL_VSTREAM *, int,
		ACL_EVENT_NOTIFY_RDWR, void *);

	/* ֹͣĳ׽ӿڵĿɶ״̬ */
	void (*disable_read_fn)(ACL_EVENT *, ACL_VSTREAM *);
	/* ֹͣĳ׽ӿڵĿд״̬ */
	void (*disable_write_fn)(ACL_EVENT *, ACL_VSTREAM *);
	/* ֹͣĳ׽ӿڵĶд״̬ */
	void (*disable_readwrite_fn)(ACL_EVENT *, ACL_VSTREAM *);

	/* ׽ӿڵĿɶ״̬Ƿ񱻼 */
	int  (*isrset_fn)(ACL_EVENT *, ACL_VSTREAM *);
	/* ׽ӿڵĿд״̬Ƿ񱻼 */
	int  (*iswset_fn)(ACL_EVENT *, ACL_VSTREAM *);
	/* ׽ӿڵ쳣״̬Ƿ񱻼 */
	int  (*isxset_fn)(ACL_EVENT *, ACL_VSTREAM *);

	/* Ӷʱ */
	acl_int64 (*timer_request)(ACL_EVENT *, ACL_EVENT_NOTIFY_TIME,
		void *, acl_int64, int);
	/* ȡʱ */
	acl_int64 (*timer_cancel)(ACL_EVENT *ev,
		ACL_EVENT_NOTIFY_TIME, void *);
	/* öʱǷΪѭִ */
	void (*timer_keep)(ACL_EVENT *, ACL_EVENT_NOTIFY_TIME, void *, int);
	/* ʱǷѭִе */
	int  (*timer_ifkeep)(ACL_EVENT *, ACL_EVENT_NOTIFY_TIME, void *);

	/* ÿε¼صǰúָǿȵô˺ */
	void (*fire_begin)(ACL_EVENT *, void *);
	/* ÿε¼صúָǿô˺ */
	void (*fire_end)(ACL_EVENT *, void *);
	/* fire_begin/fire_finish ĵڶ */
	void *fire_ctx;
};

/* 뱣ּʱǳ */
/*
#ifdef	ACL_HAS_SPINLOCK
#define	EVENT_USE_SPINLOCK
#endif
*/

typedef struct EVENT_THR {
	ACL_EVENT event;

	/* just for thread */
#ifdef	EVENT_USE_SPINLOCK
	pthread_spinlock_t tm_mutex;
	pthread_spinlock_t tb_mutex;
#else
	acl_pthread_mutex_t tm_mutex;
	acl_pthread_mutex_t tb_mutex;
#endif
	EVENT_DOG *evdog;
	int     blocked;
} EVENT_THR;

/* local macro */
#define	FD_ENTRY_CLR(_fdp_) do {  \
	_fdp_->stream = NULL;  \
	_fdp_->r_callback = NULL;  \
	_fdp_->w_callback = NULL;  \
	_fdp_->r_context = NULL;  \
	_fdp_->w_context = NULL;  \
	_fdp_->r_ttl = 0; \
	_fdp_->w_ttl = 0; \
	_fdp_->r_timeout = 0; \
	_fdp_->w_timeout = 0; \
	_fdp_->listener = 0; \
	_fdp_->event_type = 0; \
	_fdp_->flag = 0; \
	_fdp_->fdidx = -1; \
	_fdp_->fdidx_ready = -1; \
	_fdp_->fifo_info = NULL; \
} while (0);

/* in events.c */
void event_check_fds(ACL_EVENT *ev);
int  event_prepare(ACL_EVENT *ev);
void event_fire(ACL_EVENT *ev);
int  event_thr_prepare(ACL_EVENT *ev);
void event_thr_fire(ACL_EVENT *ev);

/* in events_alloc.c */
ACL_EVENT *event_alloc(size_t size);

/* in events_select.c */
ACL_EVENT *event_new_select(void);

/* in events_select_thr.c */
ACL_EVENT *event_new_select_thr(void);

/* in events_poll.c */
ACL_EVENT *event_new_poll(int fdsize);

/* in events_poll_thr.c */
ACL_EVENT *event_poll_alloc_thr(int fdsize);

/* in events_kernel.c */
ACL_EVENT *event_new_kernel(int fdsize);

/* in events_kernel_thr.c */
#ifdef	ACL_EVENTS_KERNEL_STYLE
ACL_EVENT *event_new_kernel_thr(int fdsize);
#endif

#if (ACL_EVENTS_KERNEL_STYLE == ACL_EVENTS_STYLE_EPOLL)
ACL_EVENT *event_epoll_alloc_thr(int fdsize);
#endif

# define LOCK_INIT(mutex_in) do { \
	acl_pthread_mutexattr_t attr; \
	acl_pthread_mutex_t *mutex_ptr = (mutex_in); \
	acl_pthread_mutexattr_init(&attr); \
	acl_pthread_mutexattr_settype(&attr, ACL_PTHREAD_MUTEX_RECURSIVE); \
	acl_pthread_mutex_init(mutex_ptr, &attr); \
	acl_pthread_mutexattr_destroy(&attr); \
} while (0)

# define LOCK_DESTROY(mutex_in) do { \
	acl_pthread_mutex_t *mutex_ptr = (mutex_in); \
	(void) acl_pthread_mutex_destroy(mutex_ptr); \
} while (0)

# define THREAD_LOCK(mutex_in) do { \
	acl_pthread_mutex_t *mutex_ptr = (mutex_in); \
	int   status = acl_pthread_mutex_lock(mutex_ptr); \
	if (status != 0) \
		acl_msg_fatal("%s(%d): lock error(%s)", \
			__FILE__, __LINE__, strerror(status)); \
} while (0)

# define THREAD_UNLOCK(mutex_in) do { \
	acl_pthread_mutex_t *mutex_ptr = (mutex_in); \
	int   status = acl_pthread_mutex_unlock(mutex_ptr); \
	if (status != 0) \
		acl_msg_fatal("%s(%d): unlock error(%s)", \
			__FILE__, __LINE__, strerror(status)); \
} while (0)

#define SET_TIME(x) {  \
	struct timeval _tv;  \
	gettimeofday(&_tv, NULL);  \
	(x) = ((acl_int64) _tv.tv_sec) * 1000000 + ((acl_int64) _tv.tv_usec);  \
}

/* in events_timer.c */
void event_timer_create(ACL_EVENT *eventp);
void event_timer_free(ACL_EVENT *eventp);
acl_int64 event_timer_when(ACL_EVENT *eventp);
acl_int64 event_timer_request(ACL_EVENT *ev, ACL_EVENT_NOTIFY_TIME callback,
	void *context, acl_int64 delay, int keep);
acl_int64 event_timer_cancel(ACL_EVENT *ev, ACL_EVENT_NOTIFY_TIME callback,
	void *context);
void event_timer_keep(ACL_EVENT *ev, ACL_EVENT_NOTIFY_TIME callback,
	void *context, int keep);
int  event_timer_ifkeep(ACL_EVENT *ev, ACL_EVENT_NOTIFY_TIME callback,
	void *context);
void event_timer_trigger(ACL_EVENT *ev);

/* in events_timer_thr.c */
acl_int64 event_timer_request_thr(ACL_EVENT *ev,
	ACL_EVENT_NOTIFY_TIME callback, void *context,
	acl_int64 delay, int keep);
acl_int64 event_timer_cancel_thr(ACL_EVENT *ev,
	ACL_EVENT_NOTIFY_TIME callback, void *context);

void event_timer_keep_thr(ACL_EVENT *ev, ACL_EVENT_NOTIFY_TIME callback,
	void *context, int keep);
int  event_timer_ifkeep_thr(ACL_EVENT *ev, ACL_EVENT_NOTIFY_TIME callback,
	void *context);

void event_timer_trigger_thr(EVENT_THR *event);

#ifdef	__cplusplus
}
#endif

#endif
