/*
   Copyright (C) 2010 bg <bg_one@mail.ru>
*/
#ifndef CHAN_DONGLE_AT_CMD_QUEUE_H_INCLUDED
#define CHAN_DONGLE_AT_CMD_QUEUE_H_INCLUDED

#include <sys/time.h>			/* struct timeval */

#include <asterisk.h>
#include <asterisk/linkedlists.h>	/* AST_LIST_ENTRY */

#include "at_command.h"			/* at_cmd_t */
#include "at_response.h"		/* at_res_t */
#include "export.h"			/* EXPORT_DECL EXPORT_DEF */


typedef struct at_queue_cmd
{
	at_cmd_t		cmd;			/*!< command code */
	at_res_t		res;			/*!< expected response code, can be RES_OK, RES_CMGR, RES_SMS_PROMPT */

	unsigned		flags;			/*!< flags */
#define ATQ_CMD_FLAG_DEFAULT		0x00		/*!< empty flags */
#define ATQ_CMD_FLAG_STATIC		0x01		/*!< data is static no try deallocate */
#define ATQ_CMD_FLAG_IGNORE		0x02		/*!< ignore response non match condition */
#define ATQ_CMD_FLAG_SUPPRESS_ERROR	0x04		/*!< don't print error message if command fails */

	struct timeval		timeout;		/*!< timeout value, started at time when command actually written on device */
#define ATQ_CMD_TIMEOUT_SHORT	1		/*!< timeout value  1 sec */
#define ATQ_CMD_TIMEOUT_MEDIUM	5		/*!< timeout value  5 sec */
#define ATQ_CMD_TIMEOUT_LONG	40		/*!< timeout value 40 sec */

	char*			data;			/*!< command and data to send in device */
	unsigned		length;			/*!< data length */
} at_queue_cmd_t;

/* initializers */
#define ATQ_CMD_INIT_STF(e,icmd,iflags,idata)	do {	\
	(e).cmd = (icmd);				\
	(e).res = RES_OK;				\
	(e).flags = iflags | ATQ_CMD_FLAG_STATIC;	\
	(e).timeout.tv_sec = ATQ_CMD_TIMEOUT_MEDIUM;	\
	(e).timeout.tv_usec = 0;			\
	(e).data = (char*)(idata);			\
	(e).length = STRLEN(idata);			\
	} while(0)
#define ATQ_CMD_INIT_ST(e,icmd,idata)		ATQ_CMD_INIT_STF(e, icmd, ATQ_CMD_FLAG_DEFAULT, idata)

#define ATQ_CMD_INIT_DYNF(e,icmd,iflags)	do {	\
	(e).cmd = (icmd);				\
	(e).res = RES_OK;				\
	(e).flags = iflags & ~ATQ_CMD_FLAG_STATIC;	\
	(e).timeout.tv_sec = ATQ_CMD_TIMEOUT_MEDIUM;	\
	(e).timeout.tv_usec = 0;			\
	} while(0)
#define ATQ_CMD_INIT_DYN(e,icmd)		ATQ_CMD_INIT_DYNF(e, icmd, ATQ_CMD_FLAG_DEFAULT)
#define ATQ_CMD_INIT_DYNI(e,icmd)		ATQ_CMD_INIT_DYNF(e, icmd, ATQ_CMD_FLAG_IGNORE)

/* static initializers */
#define ATQ_CMD_DECLARE_STFT(cmd,res,data,flags,s,u)	{ (cmd), (res), ATQ_CMD_FLAG_STATIC|flags, {(s), (u)}, (char*)(data), STRLEN(data) }
#define ATQ_CMD_DECLARE_STF(cmd,res,data,flags)		ATQ_CMD_DECLARE_STFT(cmd, res, data, flags, ATQ_CMD_TIMEOUT_MEDIUM, 0)
//#define ATQ_CMD_DECLARE_STF(cmd,res,data,flags)	{ (cmd), (res), ATQ_CMD_FLAG_STATIC|flags, {ATQ_CMD_TIMEOUT_MEDIUM, 0}, (char*)(data), STRLEN(data) }
#define ATQ_CMD_DECLARE_ST(cmd,data)		ATQ_CMD_DECLARE_STF(cmd, RES_OK, data, ATQ_CMD_FLAG_DEFAULT)
#define ATQ_CMD_DECLARE_STI(cmd,data)		ATQ_CMD_DECLARE_STF(cmd, RES_OK, data, ATQ_CMD_FLAG_IGNORE)
#define ATQ_CMD_DECLARE_STIT(cmd,data,s,u)	ATQ_CMD_DECLARE_STFT(cmd, RES_OK, data, ATQ_CMD_FLAG_IGNORE,s,u)

#define ATQ_CMD_DECLARE_DYNFT(cmd,res,flags,s,u) { (cmd), (res),  flags & ~ATQ_CMD_FLAG_STATIC, {(s), (u)}, 0,      0 }
#define ATQ_CMD_DECLARE_DYNF(cmd,res,flags)	ATQ_CMD_DECLARE_DYNFT(cmd,res,flags,ATQ_CMD_TIMEOUT_MEDIUM, 0)
//#define ATQ_CMD_DECLARE_DYNF(cmd,res,flags)	{ (cmd), (res),  flags & ~ATQ_CMD_FLAG_STATIC, {ATQ_CMD_TIMEOUT_MEDIUM, 0}, 0,      0 }
#define ATQ_CMD_DECLARE_DYN(cmd)		ATQ_CMD_DECLARE_DYNF(cmd, RES_OK, ATQ_CMD_FLAG_DEFAULT)
#define ATQ_CMD_DECLARE_DYNI(cmd)		ATQ_CMD_DECLARE_DYNF(cmd, RES_OK, ATQ_CMD_FLAG_IGNORE)
#define ATQ_CMD_DECLARE_DYNIT(cmd,s,u)		ATQ_CMD_DECLARE_DYNFT(cmd, RES_OK, ATQ_CMD_FLAG_IGNORE,s,u)
#define ATQ_CMD_DECLARE_DYN2(cmd,res)		ATQ_CMD_DECLARE_DYNF(cmd, res, ATQ_CMD_FLAG_DEFAULT)

typedef struct at_queue_task
{
	AST_LIST_ENTRY (at_queue_task) entry;

	unsigned	cmdsno;
	unsigned	cindex;
	struct cpvt*	cpvt;

	int uid;

	at_queue_cmd_t	cmds[0];	/* this field must be last */
} at_queue_task_t;


EXPORT_DECL int at_queue_insert_const (struct cpvt * cpvt, const at_queue_cmd_t * cmds, unsigned cmdsno, int athead);
EXPORT_DECL int at_queue_insert (struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead);
EXPORT_DECL int at_queue_insert_uid (struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead, int uid);
EXPORT_DECL void at_queue_handle_result (struct pvt * pvt, at_res_t res);
EXPORT_DECL void at_queue_flush (struct pvt * pvt);
EXPORT_DECL const at_queue_task_t * at_queue_head_task (const struct pvt * pvt);
EXPORT_DECL const at_queue_cmd_t * at_queue_head_cmd(const struct pvt * pvt);
EXPORT_DECL int at_queue_timeout(const struct pvt * pvt);
EXPORT_DECL int at_queue_run (struct pvt * pvt);

INLINE_DECL at_cmd_suppress_error_t at_cmd_suppress_error_mode(int flags)
{
	return ((flags & ATQ_CMD_FLAG_SUPPRESS_ERROR) ? SUPPRESS_ERROR_ENABLED : SUPPRESS_ERROR_DISABLED);
}

static inline const at_queue_cmd_t * at_queue_task_cmd (const at_queue_task_t * task)
{
	return task ? &task->cmds[task->cindex] : NULL;
}

/* direct device write, dangerouse */
/* TODO: move */
EXPORT_DECL int at_write (struct pvt * pvt, const char * buf, size_t count);
EXPORT_DECL size_t write_all (int fd, const char * buf, size_t count);
#endif /* CHAN_DONGLE_AT_CMD_QUEUE_H_INCLUDED */
