/* 
 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2005-2009, Anthony Minessale II <anthm@freeswitch.org>
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 *
 * The Initial Developer of the Original Code is
 * Anthony Minessale II <anthm@freeswitch.org>
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * 
 * Anthony Minessale II <anthm@freeswitch.org>
 * Andrew Thompson <andrew@hijacked.us>
 * Rob Charlton <rob.charlton@savageminds.com>
 *
 *
 * mod_erlang_event.h -- Erlang Event Handler derived from mod_event_socket
 *
 */

typedef enum {
	LFLAG_WAITING_FOR_PID = (1 << 0), /* waiting for a node to return a pid */
	LFLAG_OUTBOUND_INIT = (1 << 1), /* Erlang peer has been notified of this session */
	LFLAG_SESSION_ALIVE = (1 << 2),
} session_flag_t;

typedef enum {
	ERLANG_PID = 0,
	ERLANG_REG_PROCESS
} process_type;

typedef enum {
	ERLANG_STRING = 0,
	ERLANG_BINARY
} erlang_encoding_t;

struct erlang_process {
	process_type type;
	char *reg_name;
	erlang_pid pid;
};

struct session_elem {
	switch_core_session_t *session;
	switch_mutex_t *flag_mutex;
	uint32_t flags;
	struct erlang_process process;
	switch_queue_t *event_queue;
	struct session_elem *next;
};

typedef struct session_elem session_elem_t;

typedef enum {
	LFLAG_RUNNING = (1 << 0),
	LFLAG_EVENTS = (1 << 1),
	LFLAG_LOG = (1 << 2),
	LFLAG_MYEVENTS = (1 << 3),
	LFLAG_STATEFUL = (1 << 4)
} event_flag_t;

/* There is one listener for each Erlang node we are attached to - either
   inbound or outbound. For example, if the erlang node node1@server connects
   to freeswitch then a listener is created and handles commands sent from
   that node. If 5 calls are directed to the outbound erlang application
   via the dialplan, and are also set to talk to node1@server, then those
   5 call sessions will be "attached" to the same listener.
 */
struct listener {
	int sockfd;
	struct ei_cnode_s *ec;
	struct erlang_process log_process;
	struct erlang_process event_process;
	char *peer_nodename;
	switch_queue_t *event_queue;
	switch_queue_t *log_queue;
	switch_memory_pool_t *pool;
	switch_mutex_t *flag_mutex;
	switch_mutex_t *sock_mutex;
	char *ebuf;
	uint32_t flags;
	switch_log_level_t level;
	uint8_t event_list[SWITCH_EVENT_ALL + 1];
	switch_hash_t *event_hash;
	switch_hash_t *fetch_reply_hash;
	switch_hash_t *spawn_pid_hash;
	switch_thread_rwlock_t *rwlock;
	switch_mutex_t *session_mutex;
	session_elem_t *session_list;
	int lost_events;
	int lost_logs;
	time_t last_flush;
	uint32_t timeout;
	uint32_t id;
	char remote_ip[50];
	/*switch_port_t remote_port;*/
	struct listener *next;
};

typedef struct listener listener_t;

struct erlang_binding {
	switch_xml_section_t section;
	struct erlang_process process;
	listener_t *listener;
	struct erlang_binding *next;
};

struct api_command_struct {
	char *api_cmd;
	char *arg;
	listener_t *listener;
	char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
	uint8_t bg;
	erlang_pid pid;
	switch_memory_pool_t *pool;
};

struct globals_struct {
	switch_mutex_t *listener_mutex;
	switch_event_node_t *node;
	unsigned int reference0;
	unsigned int reference1;
	unsigned int reference2;
	switch_mutex_t *ref_mutex;
};
typedef struct globals_struct globals_t;

struct listen_list_struct {
	int sockfd;
	switch_mutex_t *sock_mutex;
	listener_t *listeners;
	uint8_t ready;
};
typedef struct listen_list_struct listen_list_t;

struct bindings_struct {
	struct erlang_binding *head;
	switch_xml_binding_t *search_binding;
};
typedef struct bindings_struct bindings_t;

#define MAX_ACL 100
struct prefs_struct {
	switch_mutex_t *mutex;
	char *ip;
	char *nodename;
	switch_bool_t shortname;
	uint16_t port;
	char *cookie;
	int done;
	int threads;
	char *acl[MAX_ACL];
	uint32_t acl_count;
	uint32_t id;
	erlang_encoding_t encoding;
};
typedef struct prefs_struct prefs_t;

/* shared globals */
#ifdef DEFINE_GLOBALS
globals_t globals;
listen_list_t listen_list;
bindings_t bindings;
prefs_t prefs;
#else
extern globals_t globals;
extern listen_list_t listen_list;
extern bindings_t bindings;
extern prefs_t prefs;
#endif

/* function prototypes */
/* handle_msg.c */
int handle_msg(listener_t *listener, erlang_msg *msg, ei_x_buff *buf, ei_x_buff *rbuf);

/* ei_helpers.c */
void ei_link(listener_t *listener, erlang_pid *from, erlang_pid *to);
void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event);
void ei_encode_switch_event_tag(ei_x_buff *ebuf, switch_event_t *event, char *tag);
int ei_pid_from_rpc(struct ei_cnode_s *ec, int sockfd, erlang_ref *ref, char *module, char *function);
int ei_spawn(struct ei_cnode_s *ec, int sockfd, erlang_ref *ref, char *module, char *function, int argc, char **argv);
void ei_init_ref(struct ei_cnode_s *ec, erlang_ref *ref);
void ei_x_print_reg_msg(ei_x_buff *buf, char *dest, int send);
void ei_x_print_msg(ei_x_buff *buf, erlang_pid *pid, int send);
int ei_sendto(ei_cnode *ec, int fd, struct erlang_process *process, ei_x_buff *buf);
void ei_hash_ref(erlang_ref *ref, char *output);
int ei_compare_pids(erlang_pid *pid1, erlang_pid *pid2);
int ei_decode_string_or_binary(char *buf, int *index, int maxlen, char *dst);
switch_status_t initialise_ei(struct ei_cnode_s *ec);
#define ei_encode_switch_event(_b, _e) ei_encode_switch_event_tag(_b, _e, "event")

/* crazy macro for toggling encoding type */
#define _ei_x_encode_string(buf, string) switch (prefs.encoding) { \
	case ERLANG_BINARY: \
		ei_x_encode_binary(buf, string, strlen(string)); \
		break; \
	default: \
		ei_x_encode_string(buf, string); \
		break; \
}

/* mod_erlang_event.c */
session_elem_t* attach_call_to_registered_process(listener_t* listener, char* reg_name, switch_core_session_t *session);
session_elem_t* attach_call_to_pid(listener_t* listener, erlang_pid* pid, switch_core_session_t *session);
session_elem_t* attach_call_to_spawned_process(listener_t* listener, char *module, char *function, switch_core_session_t *session);

/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
 */
