/*
 * Copyright (c) 2010, Sangoma Technologies 
 * David Yat Sin <davidy@sangoma.com>
 * Moises Silva <moy@sangoma.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of the original author; nor the names of any contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef __FTMOD_SNG_ISDN_H__
#define __FTMOD_SNG_ISDN_H__

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <unistd.h>
#include <ctype.h>

#include "private/ftdm_core.h"

#include <sng_isdn.h>

#define MAX_SPANS_PER_NFAS_LINK	  8 /* TODO: Confirm this value */
#define NUM_E1_CHANNELS_PER_SPAN 32
#define NUM_T1_CHANNELS_PER_SPAN 24
#define NUM_BRI_CHANNELS_PER_SPAN 2

/* Should never have DEBUG_MODE defined when used in production */
#if 0
#undef DEBUG_MODE
#define FORCE_SEFGAULT
#else
#define DEBUG_MODE
#define FORCE_SEGFAULT	*(int *) 0 = 0;
#endif

/* TODO: rename all *_cc_* to *_an_*  */

typedef struct sngisdn_glare_data {
	int16_t		suId;
    uint32_t	suInstId;
    uint32_t	spInstId;
	int16_t		dChan;
    ConEvnt		setup;
	uint8_t		ces;
}sngisdn_glare_data_t;


/* Channel specific data */
typedef struct sngisdn_chan_data {
    ftdm_channel_t			*ftdmchan;
    uint32_t				flags;
	uint8_t 				ces;		/* not used for now */
	uint8_t					dchan_id;
	uint32_t				suInstId;	/* instance ID generated locally */
	uint32_t				spInstId;	/* instance ID generated by stack */

	uint8_t                 globalFlg;
	sngisdn_glare_data_t	glare;
} sngisdn_chan_data_t;

/* Span specific data */
typedef struct sngisdn_span_data {
	ftdm_span_t	*ftdm_span;
	uint8_t		link_id;
	uint8_t 	switchtype;
	uint8_t		signalling;			/* SNGISDN_SIGNALING_CPE or SNGISDN_SIGNALING_NET */
	uint8_t 	cc_id;
	uint8_t 	dchan_id;
	uint8_t 	span_id;
	uint8_t		tei;
	uint8_t		keep_link_up;
	uint8_t		trace_flags;
} sngisdn_span_data_t;

/* dchan_data can have more than 1 span when running NFAS */
typedef struct sngisdn_dchan_data {
	uint8_t 			num_spans;
	sngisdn_span_data_t	*spans[MAX_L1_LINKS+1];
	uint16_t			num_chans;
	/* worst case for number of channel is when using NFAS, and NFAS is only used on T1,
		so we can use MAX_SPANS_PER_NFAS_LINK*NUM_T1_CHANNELS_PER_SPAN instead of
		MAX_SPANS_PER_NFAS_LINK*NUM_E1_CHANNELS_PER_SPAN
	*/
	/* Never seen NFAS on E1 yet, so use NUM_T1_CHANNELS_PER_SPAN */
	/* b-channels are arranged by physical id's not logical */
	sngisdn_chan_data_t *channels[MAX_SPANS_PER_NFAS_LINK*NUM_T1_CHANNELS_PER_SPAN];
}sngisdn_dchan_data_t;

typedef struct sngisdn_cc {
	/* TODO: use flags instead of config_done and activation_done */
	uint8_t				config_done;
	uint8_t				activation_done;
	uint8_t				switchtype;
	ftdm_trunk_type_t	trunktype;
	uint32_t			last_suInstId;
	ftdm_mutex_t		*request_mutex;
	sngisdn_chan_data_t	*active_spInstIds[MAX_INSTID];
	sngisdn_chan_data_t	*active_suInstIds[MAX_INSTID];
}sngisdn_cc_t;

/* Global sngisdn data */
typedef struct ftdm_sngisdn_data {
	uint8_t	gen_config_done;
	uint8_t num_cc;						/* 1 ent per switchtype */
	struct sngisdn_cc ccs[MAX_VARIANTS+1];
	uint8_t	num_dchan;
	sngisdn_dchan_data_t dchans[MAX_L1_LINKS+1];
}ftdm_sngisdn_data_t;

typedef enum {
	FLAG_RESET_RX           = (1 << 0),
	FLAG_RESET_TX           = (1 << 1),
	FLAG_REMOTE_REL         = (1 << 2),
	FLAG_LOCAL_REL          = (1 << 3),
	FLAG_REMOTE_ABORT       = (1 << 4),
	FLAG_LOCAL_ABORT       = (1 << 4),
	FLAG_GLARE              = (1 << 5),
	FLAG_INFID_RESUME       = (1 << 17),
	FLAG_INFID_PAUSED       = (1 << 18),
	FLAG_CKT_MN_BLOCK_RX    = (1 << 19),
	FLAG_CKT_MN_BLOCK_TX    = (1 << 20),
	FLAG_CKT_MN_UNBLK_RX    = (1 << 21),
	FLAG_CKT_MN_UNBLK_TX    = (1 << 22),
	FLAG_GRP_HW_BLOCK_RX    = (1 << 23),
	FLAG_GRP_HW_BLOCK_TX    = (1 << 24),
	FLAG_GRP_MN_BLOCK_RX    = (1 << 25),
	FLAG_GRP_MN_BLOCK_TX    = (1 << 28),
	FLAG_GRP_HW_UNBLK_RX    = (1 << 27),
	FLAG_GRP_HW_UNBLK_TX    = (1 << 28),
	FLAG_GRP_MN_UNBLK_RX    = (1 << 29),
	FLAG_GRP_MN_UNBLK_TX    = (1 << 30)
} sngisdn_flag_t;


typedef enum {
	SNGISDN_SWITCH_INVALID = 0,	/* invalid */
	SNGISDN_SWITCH_NI2 ,	/* national isdn-2 */
	SNGISDN_SWITCH_5ESS,	/* att 5ess */
	SNGISDN_SWITCH_4ESS,	/* att 4ess */
	SNGISDN_SWITCH_DMS100,	/* nt dms100 */
	SNGISDN_SWITCH_EUROISDN,/* etsi */
	SNGISDN_SWITCH_QSIG,	/* etsi qsig */
	SNGISDN_SWITCH_INSNET,	/* int - net */
} sngisdn_switchtype_t;

typedef enum {
	SNGISDN_SIGNALING_INVALID = 0,		/* invalid */
	SNGISDN_SIGNALING_CPE ,				/* customer side emulation */
	SNGISDN_SIGNALING_NET,				/* network side emulation */
} sngisdn_signalingtype_t;

typedef enum {
	SNGISDN_TRACE_DISABLE = 0,
	SNGISDN_TRACE_Q921 = 1,
	SNGISDN_TRACE_Q931 = 2,
} sngisdn_tracetype_t;


#define sngisdn_set_flag(obj, flag)   ((obj)->flags |= (flag))
#define sngisdn_clear_flag(obj, flag) ((obj)->flags &= ~(flag))
#define sngisdn_test_flag(obj, flag)  ((obj)->flags & flag)

#define sngisdn_set_trace_flag(obj, flag)   ((obj)->trace_flags |= (flag))
#define sngisdn_clear_trace_flag(obj, flag) ((obj)->trace_flags &= ~(flag))
#define sngisdn_test_trace_flag(obj, flag)  ((obj)->trace_flags & flag)

/* TODO implement these 2 functions */
#define ISDN_FUNC_TRACE_ENTER(a)
#define ISDN_FUNC_TRACE_EXIT(a)

/* Configuration functions */
ftdm_status_t ftmod_isdn_parse_cfg(ftdm_conf_parameter_t *ftdm_parameters, ftdm_span_t *span);

/* Support functions */
uint32_t get_unique_suInstId(uint8_t cc_id);
void clear_call_data(sngisdn_chan_data_t *sngisdn_info);
void stack_hdr_init(Header *hdr);
void stack_pst_init(Pst *pst);
ftdm_status_t get_ftdmchan_by_spInstId(uint8_t cc_id, uint32_t spInstId, sngisdn_chan_data_t **sngisdn_data);
ftdm_status_t get_ftdmchan_by_suInstId(uint8_t cc_id, uint32_t suInstId, sngisdn_chan_data_t **sngisdn_data);

ftdm_status_t check_for_state_change(ftdm_channel_t *ftdmchan);

/* Outbound Call Control functions */
void sngisdn_snd_setup(ftdm_channel_t *ftdmchan);
void sngisdn_snd_proceed(ftdm_channel_t *ftdmchan);
void sngisdn_snd_progress(ftdm_channel_t *ftdmchan);
void sngisdn_snd_alert(ftdm_channel_t *ftdmchan);
void sngisdn_snd_connect(ftdm_channel_t *ftdmchan);
void sngisdn_snd_disconnect(ftdm_channel_t *ftdmchan);
void sngisdn_snd_release(ftdm_channel_t *ftdmchan);
void sngisdn_snd_reset(ftdm_channel_t *ftdmchan);
void sngisdn_snd_con_complete(ftdm_channel_t *ftdmchan);

/* Inbound Call Control functions */
void sngisdn_rcv_con_ind (signed short suId, uint32_t suInstId, uint32_t spInstId, ConEvnt *conEvnt, signed short dChan, uint8_t ces);
void sngisdn_rcv_con_cfm (signed short suId, uint32_t suInstId, uint32_t spInstId, CnStEvnt *cnStEvnt, signed short dChan, uint8_t ces);
void sngisdn_rcv_cnst_ind (signed short suId, uint32_t suInstId, uint32_t spInstId, CnStEvnt *cnStEvnt, uint8_t evntType, signed short dChan, uint8_t ces);
void sngisdn_rcv_disc_ind (signed short suId, uint32_t suInstId, uint32_t spInstId, DiscEvnt *discEvnt);
void sngisdn_rcv_rel_ind (signed short suId, uint32_t suInstId, uint32_t spInstId, RelEvnt *relEvnt);
void sngisdn_rcv_dat_ind (signed short suId, uint32_t suInstId, uint32_t spInstId, InfoEvnt *infoEvnt);
void sngisdn_rcv_sshl_ind (signed short suId, uint32_t suInstId, uint32_t spInstId, SsHlEvnt *ssHlEvnt, uint8_t action);
void sngisdn_rcv_sshl_cfm (signed short suId, uint32_t suInstId, uint32_t spInstId, SsHlEvnt *ssHlEvnt, uint8_t action);
void sngisdn_rcv_rmrt_ind (signed short suId, uint32_t suInstId, uint32_t spInstId, RmRtEvnt *rmRtEvnt, uint8_t action);
void sngisdn_rcv_rmrt_cfm (signed short suId, uint32_t suInstId, uint32_t spInstId, RmRtEvnt *rmRtEvnt, uint8_t action);
void sngisdn_rcv_flc_ind (signed short suId, uint32_t suInstId, uint32_t spInstId, StaEvnt *staEvnt);
void sngisdn_rcv_fac_ind (signed short suId, uint32_t suInstId, uint32_t spInstId, FacEvnt *facEvnt, uint8_t evntType, signed short dChan, uint8_t ces);
void sngisdn_rcv_sta_cfm ( signed short suId, uint32_t suInstId, uint32_t spInstId, StaEvnt *staEvnt);
void sngisdn_rcv_srv_ind ( signed short suId, Srv *srvEvnt, signed short dChan, uint8_t ces);
void sngisdn_rcv_srv_cfm ( signed short suId, Srv *srvEvnt, signed short dChan, uint8_t ces);
void sngisdn_rcv_rst_cfm ( signed short suId, Rst *rstEvnt, signed short dChan, uint8_t ces, uint8_t evtType);
void sngisdn_rcv_rst_ind ( signed short suId, Rst *rstEvnt, signed short dChan, uint8_t ces, uint8_t evtType);


void sngisdn_rcv_phy_ind(SuId suId, Reason reason);
void sngisdn_rcv_q921_ind(BdMngmt *status);
void sngisdn_rcv_q921_trace(BdMngmt *trc, Buffer *mBuf);
void sngisdn_rcv_q931_ind(InMngmt *status);
void sngisdn_rcv_q931_trace(InMngmt *trc, Buffer *mBuf);
void sngisdn_rcv_cc_ind(CcMngmt *status);
void sngisdn_rcv_sng_log(uint8_t level, char *fmt,...);

void handle_sng_log(uint8_t level, char *fmt,...);
void sngisdn_set_span_sig_status(ftdm_span_t *ftdmspan, ftdm_signaling_status_t status);

/* Stack management functions */
ftdm_status_t sng_isdn_stack_cfg(ftdm_span_t *span);
ftdm_status_t sng_isdn_stack_activate(ftdm_span_t *span);


#endif /* __FTMOD_SNG_ISDN_H__ */

