/*
 * File:   ro_session_hash.h
 * Author: Jason Penton
 *
 * Created on 07 April 2011, 4:12 PM
 */

#ifndef RO_SESSION_HASH_H
#define RO_SESSION_HASH_H

#include "ro_timer.h"
#include "../../core/mem/shm_mem.h"
#include "../../core/rand/kam_rand.h"
#include "ims_charging_stats.h"
#include <stdlib.h>


/* ro session flags */
#define RO_SESSION_FLAG_NEW (1 << 0)	  /*!< new ro session */
#define RO_SESSION_FLAG_INSERTED (1 << 1) /*!< session has been written to DB */
#define RO_SESSION_FLAG_CHANGED (1 << 2)  /*!< ro session has been updated */
#define RO_SESSION_FLAG_DELETED (1 << 3)  /*!< ro session has been deleted */

#define MAX_PANI_LEN 100

extern struct ims_charging_counters_h ims_charging_cnts_h;

enum ro_session_event_type
{
	pending,
	answered,
	no_more_credit,
	delayed_delete,
	unknown_error,
};

struct diameter_avp_value
{
	str mac;
};

//used to pass data into dialog callbacks

struct impu_data
{
	str identity;
	str contact;
};

extern struct impu_data impu_data_t;

struct ro_session
{
	volatile int ref;
	int direction;
	struct ro_session *next;
	struct ro_session *prev;
	str ro_session_id;
	str callid;
	str asserted_identity;
	str called_asserted_identity;
	str incoming_trunk_id;
	str outgoing_trunk_id;
	str pani;
	str app_provided_party;
	unsigned int hop_by_hop;
	struct ro_tl ro_tl;
	unsigned int reserved_secs;
	unsigned int valid_for;
	unsigned int dlg_h_entry;
	unsigned int dlg_h_id;
	unsigned int h_entry;
	unsigned int h_id;
	time_t start_time;
	time_t last_event_timestamp;
	time_t last_event_timestamp_backup;
	enum ro_session_event_type event_type;
	int auth_appid;
	int auth_session_type;
	int active;
	unsigned int flags;
	str mac;
	int rating_group;
	int service_identifier;
	unsigned int is_final_allocation;
	long billed;
	unsigned int ccr_sent;
	str origin_host;
	int ro_timer_buffer;
};

/*! entries in the main ro_session table */
struct ro_session_entry
{
	struct ro_session *first; /*!< dialog list */
	struct ro_session *last;  /*!< optimisation, end of the dialog list */
	unsigned int next_id;	  /*!< next id */
	unsigned int lock_idx;	  /*!< lock index */
};

/*! main ro_session table */
struct ro_session_table
{
	unsigned int size;				  /*!< size of the dialog table */
	struct ro_session_entry *entries; /*!< dialog hash table */
	unsigned int locks_no;			  /*!< number of locks */
	gen_lock_set_t *locks;			  /*!< lock table */
};


/*! global ro_session table */
extern struct ro_session_table *ro_session_table;


/*!
 * \brief Set a ro_session lock
 * \param _table ro_session table
 * \param _entry locked entry
 */
#define ro_session_lock(_table, _entry)                    \
	{                                                      \
		LM_DBG("LOCKING %d\n", (_entry)->lock_idx);        \
		lock_set_get((_table)->locks, (_entry)->lock_idx); \
		LM_DBG("LOCKED %d\n", (_entry)->lock_idx);         \
	}


/*!
 * \brief Release a ro_session lock
 * \param _table ro_session table
 * \param _entry locked entry
 */
#define ro_session_unlock(_table, _entry)                      \
	{                                                          \
		LM_DBG("UNLOCKING %d\n", (_entry)->lock_idx);          \
		lock_set_release((_table)->locks, (_entry)->lock_idx); \
		LM_DBG("UNLOCKED %d\n", (_entry)->lock_idx);           \
	}

/*!
 * \brief Reference an ro_session without locking
 * \param _ro_session Ro Session
 * \param _cnt increment for the reference counter
 */
#define ref_ro_session_unsafe(_session, _cnt)                           \
	do {                                                                \
		(_session)->ref += (_cnt);                                      \
		LM_DBG("ref ro_session %p with %d -> %d (tl=%p)\n", (_session), \
				(_cnt), (_session)->ref, &(_session)->ro_tl);           \
	} while(0)


/*!
 * \brief Unreference an ro_session without locking
 * \param _ro_session Ro Session
 * \param _cnt decrement for the reference counter
 */
#define unref_ro_session_unsafe(_ro_session, _cnt, _ro_session_entry)        \
	do {                                                                     \
		(_ro_session)->ref -= (_cnt);                                        \
		LM_DBG("unref ro_session %p with %d -> %d (tl=%p)\n", (_ro_session), \
				(_cnt), (_ro_session)->ref, &(_ro_session)->ro_tl);          \
		if((_ro_session)->ref < 0) {                                         \
			LM_CRIT("bogus ref for session id < 0 [%d]\n",                   \
					(_ro_session)->ref);                                     \
		}                                                                    \
		if((_ro_session)->ref <= 0) {                                        \
			unlink_unsafe_ro_session(_ro_session_entry, _ro_session);        \
			LM_DBG("ref <=0 for ro_session %p\n", _ro_session);              \
			put_ro_session_on_wait(_ro_session);                             \
		}                                                                    \
	} while(0)

/*!
 * \brief Unlink a ro_session from the list without locking
 * \see unref_ro_session_unsafe
 * \param ro_session_entry unlinked entry
 * \param ro_session unlinked ro_session
 */
static inline void unlink_unsafe_ro_session(
		struct ro_session_entry *ro_session_entry,
		struct ro_session *ro_session)
{
	if((ro_session->next == 0x00) && (ro_session->prev == 0x00)
			&& (ro_session != ro_session_entry->first)) {
		return;
	}

	if(ro_session->next)
		ro_session->next->prev = ro_session->prev;
	else
		ro_session_entry->last = ro_session->prev;
	if(ro_session->prev)
		ro_session->prev->next = ro_session->next;
	else
		ro_session_entry->first = ro_session->next;

	ro_session->next = ro_session->prev = 0;

	counter_add(ims_charging_cnts_h.active_ro_sessions, -1);

	return;
}

/*!
 * \brief Initialize the global ro_session table
 * \param size size of the table
 * \return 0 on success, -1 on failure
 */
int init_ro_session_table(unsigned int size);

/*!
 * \brief Destroy the ro_session dialog table
 */
void destroy_ro_session(struct ro_session *ro_session);


/*!
 * \brief Link a ro_session structure
 * \param ro_session Ro Session
 * \param n extra increments for the reference counter
 */
void link_ro_session(struct ro_session *ro_session, int n);

void remove_aaa_session(str *session_id);

struct ro_session *build_new_ro_session(int direction, int auth_appid,
		int auth_session_type, str *session_id, str *callid,
		str *asserted_identity, str *called_asserted_identity, str *mac,
		unsigned int dlg_h_entry, unsigned int dlg_h_id,
		unsigned int requested_secs, unsigned int validity_timeout,
		int active_rating_group, int active_service_identifier,
		str *incoming_trunk_id, str *outgoing_trunk_id, str *pani,
		str *app_provided_party, int ro_timer_buffer);

/*!
 * \brief Refefence a ro_session with locking
 * \see ref_ro_session_unsafe
 * \param ro_session Ro Session
 * \param cnt increment for the reference counter
 */
void ref_ro_session_helper(struct ro_session *ro_session, unsigned int cnt,
		unsigned int mustlock, char *fname, int fline);
#define ref_ro_session(ro_session, cnt, mustlock) \
	ref_ro_session_helper(ro_session, cnt, mustlock, __FILE__, __LINE__)

/*!
 * \brief Unreference a ro_session with locking
 * \see unref_ro_session_unsafe
 * \param ro_session Ro Session
 * \param cnt decrement for the reference counter
 */
void unref_ro_session_helper(struct ro_session *ro_session, unsigned int cnt,
		unsigned int mustlock, char *fname, int fline);
#define unref_ro_session(ro_session, cnt, mustlock) \
	unref_ro_session_helper(ro_session, cnt, mustlock, __FILE__, __LINE__)

struct ro_session *lookup_ro_session(
		unsigned int h_entry, str *callid, int direction, unsigned int *del);

struct ro_session *lookup_ro_session_by_session_id(str *session_id);

void free_impu_data(struct impu_data *impu_data);

int put_ro_session_on_wait(struct ro_session *session);


#endif /* RO_SESSION_HASH_H */
