/* Elements derived from irssi (http://irssi.org) */

typedef struct plocal_t {
	pqueue_t	*msgqueue;
	pqueue_t	*log_msgs;
	int		signal_fd;
	PerlInterpreter	*my_perl;
	interpreter_t	*interpreter;
} plocal_t;

typedef struct {
	void *major;
	void *minor;
} ptr2_t;

#define new_pv(a) \
	(newSVpv((a) == NULL ? "" : (a), (a) == NULL ? 0 : strlen(a)))

#define is_hvref(o) \
	((o) && SvROK(o) && SvRV(o) && (SvTYPE(SvRV(o)) == SVt_PVHV))

#define is_avref(o) \
	((o) && SvROK(o) && SvRV(o) && (SvTYPE(SvRV(o)) == SVt_PVAV))

#define hvref(o) \
	(is_hvref(o) ? (HV *)SvRV(o) : NULL)

#define avref(o) \
	(is_avref(o) ? (AV *)SvRV(o) : NULL)
	
#define arisa_boot(x) { \
 	  extern void boot_Arisa__##x(pTHX_ CV *cv); \
	  arisa_callXS(boot_Arisa__##x, cv, mark); \
	}

#define perl_apply_settings(func,opt) 					\
	do {								\
		STRLEN n_a;						\
		int i;							\
		if(((items-1) % 2) == 0) {				\
			for(i = 1; i < items; i += 2) {			\
				char *name = (char *)SvPV(ST(i), n_a);	\
				char *value = (char *)SvPV(ST(i+1), n_a);\
				func(opt,name,value);			\
			}						\
		}							\
	} while(0)

#define perl_read_settings(func,opt,hv)					\
	do {								\
		pqueue_t settings;					\
		pqueue_init(&settings,0);				\
		func(opt,&settings);					\
		hv_store_values(hv,&settings);				\
	} while(0)

#define RET_REF(v) 							\
	do {								\
		if(v != NULL)						\
			XPUSHs(sv_2mortal(newRV_noinc((SV*)v)));	\
		else							\
			XPUSHs(&PL_sv_undef);				\
	} while(0)
		

#define RET_STRARR(strs,no_strs) 					\
	do {								\
		int i;							\
		for(i = 0; i < no_strs; ++i)				\
			XPUSHs(sv_2mortal(new_pv(strs[i])));		\
	} while(0)

#define LOAD_RET_STRARR(strs,no_strs)					\
	do {								\
		if(items > 1)						\
			av_to_strarr(&(strs),&(no_strs),ST(1));		\
		RET_STRARR(strs,no_strs);				\
	} while(0)

void	arisa_callXS(void (*subaddr)(pTHX_ CV* cv), CV *cv, SV **mark);

void	init_perl(plocal_t *l);
void	deinit_perl(plocal_t *l);

void	perl_handle_msg(plocal_t *l, se_msg_t *msg);
void	perl_handle_log_msg(plocal_t *l, char *msg);
void	perl_tick(plocal_t *l, struct timeval *now);

SV	*create_ptr_object(const char *package, void *ptr);
SV	*create_ptr2_object(const char *package, ptr2_t *ptr);
SV	*create_ptr2_object_p(const char *package, void *major, void *minor);
void	*fetch_object_ptr(SV *o);
void	fetch_object_ptr2(SV *o, ptr2_t *ptr);

void	hv_store_values(HV *hv, pqueue_t *values);
AV	*strarr_to_av(char **strs, int no_strs);
void	av_to_strarr(char ***strs, int *no_strs, SV *sv);
HV	*pack_to_hv(pack_t *p);
HV	*chat_to_hv(chat_t *c);
HV	*send_to_hv(send_t *s);
HV	*access_entry_to_hv(access_entry_t *ent);
HV	*ignore_entry_to_hv(ignore_entry_t *ent);

SV	*getSVarg(SV **sp, SV **mark, I32 ax, I32 items, const char *name);
#define	SVarg(x)	getSVarg(sp,mark,ax,items,(x))
int	getSvIVarg(SV **sp, SV **mark, I32 ax, I32 items, const char *name);
#define SvIVarg(x)	getSvIVarg(sp,mark,ax,items,(x))
unsigned int getSvUVarg(SV **sp, SV **mark, I32 ax, I32 items, const char *name);
#define SvUVarg(x)      getSvUVarg(sp,mark,ax,items,(x))
char	*getSvPVarg(SV **sp, SV **mark, I32 ax, I32 items, const char *name);
#define SvPVarg(x)	getSvPVarg(sp,mark,ax,items,(x))
#define SvPTRarg(x)	fetch_object_ptr(getSVarg(sp,mark,ax,items,(x)))


SV	*perl_access_add(SV **sp, SV **mark, I32 ax, I32 items, access_t *acc);
SV	*perl_access_check(SV **sp, SV **mark, I32 ax, I32 items, access_t *acc);
SV	*perl_access_del(SV **sp, SV **mark, I32 ax, I32 items, access_t *acc);
SV	*perl_access_mode(SV **sp, SV **mark, I32 ax, I32 items, access_t *acc);
#define ACCESS_EXPAND(lck,acc_ptr,cond,func)				\
	do { 								\
		access_t *acc = NULL; 					\
		LOCK((lck)); 						\
		if((cond))  						\
			acc = (acc_ptr); 				\
		UNLOCK((lck));						\
		if(acc != NULL)						\
			RETVAL = func(sp,mark,ax,items,acc); 		\
	} while(0)
#define ACCESS_ADD(l,ap,c)	ACCESS_EXPAND(l,ap,c,perl_access_add)
#define ACCESS_CHECK(l,ap,c)	ACCESS_EXPAND(l,ap,c,perl_access_check)
#define ACCESS_DEL(l,ap,c)	ACCESS_EXPAND(l,ap,c,perl_access_del)
#define ACCESS_MODE(l,ap,c)	ACCESS_EXPAND(l,ap,c,perl_access_mode)
#define ACCESS_LIST(lck,acc_ptr,cond)					\
	do {								\
		access_t *acc = NULL;					\
		int i;							\
		LOCK((lck));						\
		if((cond))						\
			acc = (acc_ptr);				\
		UNLOCK((lck));						\
		if(acc != NULL) {					\
			LOCK(acc);					\
			for(i = 0; i < acc->no_entries; ++i) {		\
				XPUSHs(sv_2mortal(newRV_noinc(		\
					(SV*)access_entry_to_hv(acc->entries[i])))); \
			}						\
			UNLOCK(acc);					\
		}							\
	} while(0)


SV	*perl_ignore_add(SV **sp, SV **mark, I32 ax, I32 items, ignore_list_t *l);
SV	*perl_ignore_del(SV **sp, SV **mark, I32 ax, I32 items, ignore_list_t *l);
HV	*perl_ignore_settings(SV **sp, SV **mark, I32 ax, I32 items, ignore_list_t *l);
SV	*perl_ignore_move(ignore_list_t *l, int src, int dst);
#define IGNORE_EXPAND(lck,lptr,func) 					\
	do { 								\
		LOCK((lck));						\
		RETVAL = func(sp,mark,ax,items,lptr); 			\
		UNLOCK((lck));						\
	} while(0)
#define IGNORE_LIST_ADD(l,p) 	IGNORE_EXPAND(l,p,perl_ignore_add)
#define IGNORE_LIST_DEL(l,p)	IGNORE_EXPAND(l,p,perl_ignore_del)
#define IGNORE_LIST_SETTINGS(l,p) IGNORE_EXPAND(l,p,perl_ignore_settings)
#define IGNORE_LIST_WIPE(l,p) \
	do { LOCK((l)); ignore_wipe(p); UNLOCK((l)); } while(0)
#define IGNORE_LIST_LIST(l,p) 						\
	do {								\
		int i;							\
		LOCK((l));						\
		LOCK((p));						\
		for(i = 0; i < (p)->no_entries; ++i) {			\
			XPUSHs(sv_2mortal(newRV_noinc(			\
				(SV*)ignore_entry_to_hv((p)->entries[i])))); \
		}							\
		UNLOCK((p));						\
		UNLOCK((l));						\
	} while(0)
#define IGNORE_LIST_MOVE(l,p,src,dst) 					\
	do { 								\
		LOCK((l)); 						\
		RETVAL = perl_ignore_move(p,src,dst); 			\
		UNLOCK((l)); 						\
	} while(0)
