/*
 * Data types and API prototypes that are exported by libvoice
 */
#ifndef __LIBVOICE_H
#define __LIBVOICE_H

#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#include <syslog.h>

#include <libpicoevent.h>
#include "voice-types.h"

#define MIN_CALLER_ID_LEN  14  	// Minimum string length to be valid.
#define MAX_CALLER_ID_LEN  (CLID_MAX_DATE + CLID_MAX_NUMBER + CLID_MAX_NAME + 3 + 5)	// Max length of caller id string
#define CLID_TIME_DELIM    8	// String index where time record ends.
#define CLID_NUMB_REC      (CLID_TIME_DELIM + 2)  // String index where number starts.

#define MAX_KEYPAD_DIGITS  100 // Max number of simulated keypad presses we accept
#define MAX_CODECS         20
#define MAX_CODEC_NAMELEN  20

#define DEBUG_LOOPBACK       0 // Debug - feed received audio back to transmitter

enum VOICE_EVENT
{
	VOICE_EVT_START,
	VOICE_EVT_DTMF0,                   /* DTMF Tone 0 */
	VOICE_EVT_DTMF1,                   /* DTMF Tone 1 */
	VOICE_EVT_DTMF2,                   /* DTMF Tone 2 */
	VOICE_EVT_DTMF3,                   /* DTMF Tone 3 */
	VOICE_EVT_DTMF4,                   /* DTMF Tone 4 */
	VOICE_EVT_DTMF5,                   /* DTMF Tone 5 */
	VOICE_EVT_DTMF6,                   /* DTMF Tone 6 */
	VOICE_EVT_DTMF7,                   /* DTMF Tone 7 */
	VOICE_EVT_DTMF8,                   /* DTMF Tone 8 */
	VOICE_EVT_DTMF9,                   /* DTMF Tone 9 */
	VOICE_EVT_DTMFS,                   /* DTMF Tone * */
	VOICE_EVT_DTMFH,                   /* DTMF Tone # */
	VOICE_EVT_DTMFA,                   /* DTMF Tone A */
	VOICE_EVT_DTMFB,                   /* DTMF Tone B */
	VOICE_EVT_DTMFC,                   /* DTMF Tone C */
	VOICE_EVT_DTMFD,                   /* DTMF Tone D */
	VOICE_EVT_OFFHOOK,                 /* Off-hook event */
	VOICE_EVT_ONHOOK,                  /* On-hook event */
	VOICE_EVT_FLASH,                   /* Hook-flash event */
	// The following events are not handled at present
	VOICE_EVT_RING_ON,                 /* Ring signal on detected */
	VOICE_EVT_RING_OFF,                /* Ring signal off detected */
	VOICE_EVT_RING_END,                /* Ring signal end detected */
	VOICE_EVT_ENCODER_SWITCH,          /* Encoder switch event */
	VOICE_EVT_DECODER_SWITCH,          /* Decoder switch event */
	VOICE_EVT_DIALTONE,                /* Dial Tone Detection event */
	VOICE_EVT_DIALTONE_END,            /* Dial Tone End event */
	VOICE_EVT_INGRESS_DTMF,            /* Ingress DTMF generated */
	VOICE_EVT_CUSTOMTONE,              /* Custom tone detect event */
	VOICE_EVT_LT_RESULT,               /* SLIC/SLAC line testing result */
	VOICE_EVT_END
};

enum DECT_EVENT {
	DECT_EVT_SWITCH,
	DECT_EVT_JOIN,
	DECT_EVT_RELEASE,
	DECT_EVT_END
};

enum VOICE_SIGNAL {
	VOICE_SIG_DIAL,
	VOICE_SIG_RINGBACK,
	VOICE_SIG_STUTTER,
	VOICE_SIG_UNOBTAINABLE,
	VOICE_SIG_CALL_WAITING,
	VOICE_SIG_BUSY,
	VOICE_SIG_RINGING,
	VOICE_SIG_CALLID_RINGING,
	VOICE_SIG_CALLID,
	VOICE_SIG_NETBUSY,
	VOICE_SIG_DTMF0,
	VOICE_SIG_DTMF1,
	VOICE_SIG_DTMF2,
	VOICE_SIG_DTMF3,
	VOICE_SIG_DTMF4,
	VOICE_SIG_DTMF5,
	VOICE_SIG_DTMF6,
	VOICE_SIG_DTMF7,
	VOICE_SIG_DTMF8,
	VOICE_SIG_DTMF9,
	VOICE_SIG_DTMFH,
	VOICE_SIG_DTMFS,
	VOICE_SIG_DTMFA,
	VOICE_SIG_DTMFB,
	VOICE_SIG_DTMFC,
	VOICE_SIG_DTMFD,
	VOICE_SIG_INGRESS_DTMF, // Simulate phone keypad button pressing
	VOICE_SIG_ANSWER,
	VOICE_SIG_K_BREAK,
	VOICE_SIG_LAST
};

enum pcm_id {
	PCM_0,
	PCM_1
};

enum VOICE_LINE_TYPE {
	VOICE_LINE_UNKNOWN,  // Invalid type
	VOICE_LINE_FXS,
	VOICE_LINE_DECT
};

struct terminal_info_t {
	enum VOICE_LINE_TYPE voice_ports[8];
	int32_t num_voice_ports;
	int32_t num_fxo;
	int32_t num_fxs;
	int32_t num_dect;
};

// Line configuration
struct line_config_t {
	uint16_t echo_cancel;
	uint16_t silence;       // silence suppression
	uint16_t comfort_noise;
	int32_t txgain;         // Gain applies to voice and tones, in contrast to EPZCNXPARAM volume which applies to voice only
	int32_t rxgain;
	int32_t hookflash_min;
	int32_t hookflash_max;
	char *name;             // Human readable line name
	int32_t config_loaded;  // 1 if configuration is loaded successfully
};

struct line_t {
	enum VOICE_LINE_TYPE type;        // Line type
	struct line_config_t line_conf;   // Line configuration
	uint16_t simulated_hook;          // True if line use simulated hook events
	uint16_t simulated_busy;          // True if line act as always off-hook and thus always busy
	uint32_t simulated_busy_peer_id;  // ID of remote who gets the above busy message
	uint16_t signaled_call_waiting;   // True if Call waiting has already been signaled to DECT
	uint16_t conference_started;      // True if conference has just been setup by DECT handset
	int pcm_callid[2];	  // -1: Invalid, 0: Obtaining, >0: Established
	pe_list_t *pending_digits;        // List of keypad digits waiting to be sent

	void *priv;                       // Platform dependent data
};

struct connection_t {
	int line;          // Logical line number which starts with 0
	int connection_id; // Arbitrary number from Asterisk

	void *priv;        // Platform dependent data
};

struct voice_event_t {
	const char *name;
	enum VOICE_EVENT event;
};

struct dect_event_t {
	const char *name;
	enum DECT_EVENT event;
};

struct voice_signal_t {
	const char *name;
	enum VOICE_SIGNAL signal;
};

struct rtp_stats_t {
	uint16_t local_burst_density;
	uint16_t remote_burst_density;
	uint16_t local_burst_duration;
	uint16_t remote_burst_duration;
	uint16_t local_gap_density;
	uint16_t remote_gap_density;
	uint16_t local_gap_duration;
	uint16_t remote_gap_duration;
	uint16_t local_jb_rate;
	uint16_t remote_jb_rate;
	uint16_t local_jb_max;
	uint16_t remote_jb_max;
	uint16_t local_jb_nominal;
	uint16_t remote_jb_nominal;
	uint16_t local_jb_abs_max;
	uint16_t remote_jb_abs_max;
	uint32_t discarded;
	uint32_t lost;
	uint32_t rxpkts;
	uint32_t txpkts;
	uint16_t jb_avg;
	uint32_t jitter;
	uint16_t local_loss_rate;
	uint16_t remote_loss_rate;
	uint32_t max_jitter;
	uint16_t jb_overruns;
	uint16_t jb_underruns;
};

struct codec_capability {
	int num_codecs;
	struct {
		char uciName[MAX_CODEC_NAMELEN];
		char codec[MAX_CODEC_NAMELEN];
		int ptimeMin;
		int ptimeMax;
		int ptimeDefault;
		int ptimeIncrement;
		float bitRate;
		int rtpPayload;
	} codecs[MAX_CODECS];
};

#define ENABLE_VOICE_DEBUG    0 // Enable/disable voice debug
#if ENABLE_VOICE_DEBUG
// log to file
#define ENDPT_DBG(fmt, ...) do { \
		FILE *fp = fopen("/tmp/voicemngr.log", "a"); \
		if (fp) { \
			struct timeval tv_now; \
			struct tm tm_now; \
			gettimeofday(&tv_now, NULL); \
			localtime_r(&tv_now.tv_sec, &tm_now); \
			fprintf(fp, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s()@%s:%d: " fmt, \
				1900 + tm_now.tm_year, tm_now.tm_mon + 1, tm_now.tm_mday, \
				tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec, (int)(tv_now.tv_usec / 1000), \
				__func__, __FILE__, __LINE__, ##__VA_ARGS__); \
			fclose(fp); \
		} \
		printf(fmt, ##__VA_ARGS__); \
	} while (0)
#else
#define ENDPT_DBG(format, ...)  voice_syslog(LOG_DEBUG, "%s:%d %s: " format, \
		__FILE__, __LINE__, __func__, ##__VA_ARGS__)
#endif
#define CHECK_POINT() ENDPT_DBG("Check point at %s@%s:%d\n", __func__, __FILE__, __LINE__)

#define ENDPT_INFO(...) voice_syslog(LOG_INFO, __VA_ARGS__)
#define ENDPT_WARN(...) voice_syslog(LOG_WARNING, __VA_ARGS__)
#define ENDPT_ERR(...)  voice_syslog(LOG_ERR, __VA_ARGS__)

extern struct terminal_info_t terminal_info;
extern struct line_t *lines;
extern struct connection_t *connections;
extern int max_num_connections;
extern const struct voice_event_t event_map[];
extern const struct dect_event_t dect_event_map[];
extern const struct voice_signal_t signal_map[];

extern void (*voice_cb_event_report)(int line, const char *event, int data);
extern void (*voice_cb_egress_media)(const struct media_packet_t *packet, int size);


// Connection API
int voice_connection_init(void);
int voice_connection_deinit(void);
int voice_connection_create(int line, int connection);
int voice_connection_close(int line, int connection);
int voice_connection_close_all(void);
int voice_connection_conference_start(int line, int connection);
int voice_connection_conference_stop(int line, int connection);
int voice_connection_find(int line, int connection);
int voice_connection_parm_update(int line, int connection, struct config_update_struct *data);
// Line API
int voice_line_preinit(void);
int voice_line_init(int has_dect);
int voice_line_deinit(void);
int voice_line_is_ready(int line);
int voice_line_is_offhook(int line);
int voice_line_get_connection_count(int line);
int voice_line_signal(int line, int connection, enum VOICE_SIGNAL signal, int start, void *data);
int voice_line_simulate_hook(int line, enum VOICE_EVENT event);

// Misc
void voice_syslog(int level, const char *fmt, ...);
int voice_engine_shutdown(void);
int voice_get_min_tx_gain(void);
int voice_get_max_tx_gain(void);
int voice_get_min_rx_gain(void);
int voice_get_max_rx_gain(void);
int voice_get_terminal_info(const struct terminal_info_t *voice_port_cfg, struct terminal_info_t *terminal_info);
void voice_hook_simulation_maintain(int line);
int voice_get_rtp_stats(int line, int connection, int reset, struct rtp_stats_t *rtp_stats);
int voice_get_codec_capability(struct codec_capability *pcodecs);
int voice_set_country(const char *country_code);
int voice_register_cb_event_report(void (*voice_cb_event_report)(int line, const char *event, int data));
int voice_register_cb_egress_media(void (*cb_egress_media)(const struct media_packet_t *packet, int size));
int voice_write_media_packet(const struct media_packet_t *packet);

#endif
