#pragma once

#include <stdbool.h>

enum ipc_err_t {
    IPC_ERR_SUCCESS = 0,
    IPC_ERR_NO_MEM = 1, /* 内存不足 */
    IPC_ERR_INVAL = 2,  /* 入参非法 */
    IPC_ERR_NO_CONN = 3, /* 未建立与服务器连接 */
    IPC_ERR_PAYLOAD_SIZE = 4, 
    IPC_ERR_MALFORMED_UTF8 = 5,
    IPC_ERR_QOS_NOT_SUPPORTED = 6, /* qos值不支持 */
    IPC_ERR_OVERSIZE_PACKET = 7,
    IPC_ERR_EAGAIN = 8, /* 需要再次尝试 */

    IPC_ERR_UNKNOWN = 100,
};

/*
 * ipc通信实体
 */
struct ipc_client;

/*
 * ipc通信主题
 */
struct ipc_topic {
    char *name;
    int qos;
};

/*
 * ipc通信负载
 */
struct ipc_payload {
    unsigned int len;
    void *buf;
};

/*
 * ipc通信消息，其主要包括topic和payload两部分
 */
struct ipc_message {
    int mid;
    struct ipc_topic t;
    struct ipc_payload p;
    int qos;
    bool retain;
};

/*
 * 注意：所有回调函数实现均不应做耗时操作，否则会导致IPC模块线程阻塞
 *
 */
struct ipc_event_ops {
    /*
     * on_connect: 与IPC通信服务器连接上后的callback
     * @c: ipc通信实体
     * @obj: 用户的私有数据，参见IPC_CLIENT_INFO_FAST/IPC_CLIENT_INFO
     * @rc
     */
    void (*on_connect)(struct ipc_client *c, void *obj, int rc);
    
    /*
     * on_disconnect: 与IPC通信服务器断开后的callback
     * @c: ipc通信实体
     * @obj: 用户的私有数据，参见IPC_CLIENT_INFO_FAST/IPC_CLIENT_INFO
     * @rc
     */
    void (*on_disconnect)(struct ipc_client *c, void *obj, int rc);
    
    /*
     * on_publish: 消息成功publish的callback
     * @c: ipc通信实体
     * @obj: 用户的私有数据，参见IPC_CLIENT_INFO_FAST/IPC_CLIENT_INFO
     * @mid: 消息id
     */
    void (*on_publish)(struct ipc_client *c, void *obj, int mid);
    
    /*
     * on_subscribe: 消息成功subscribe的callback
     * @c: ipc通信实体
     * @obj: 用户的私有数据，参见IPC_CLIENT_INFO_FAST/IPC_CLIENT_INFO
     * @mid: 消息id
     * @qos_cnt: qos_granted数组的count
     * @qos_granted: qos数组
     */
    void (*on_subscribe)(struct ipc_client *c, void *obj, int mid, int qos_cnt, const int *qos_granted);
    
    /*
     * on_unsubscribe: 消息成功unsubscribe的callback（目前不可用）
     * @c: ipc通信实体
     * @obj: 用户的私有数据，参见IPC_CLIENT_INFO_FAST/IPC_CLIENT_INFO
     * @mid: 消息id
     */
    void (*on_unsubscribe)(struct ipc_client *c, void *obj, int mid);
    /*
     * on_message: 收到订阅消息的callback
     * @c: ipc通信实体
     * @obj: 用户的私有数据，参见IPC_CLIENT_INFO_FAST/IPC_CLIENT_INFO
     * @m
     */
    void (*on_message)(struct ipc_client *c, void *obj, struct ipc_message *m);
};

struct ipc_client_info {
    const char *id;
    bool  clean_session;
    void *priv_data;
    const char *user;
    const char *passwd;
    const char *srv_host;
    int srv_port;
    int keep_alive; // 保活间隔
    struct ipc_event_ops *event_ops;
};


#define __IPC_CLIENT_INFO(ID, CLEAN_SESSION, PRIV_DATA, USER, PASSWD, SRV_HOST, SRV_PORT, KEEP_ALIVE, EVENT_OPS) \
	{\
	    	.id = ID, \
	    	.clean_session = CLEAN_SESSION, \
	        .priv_data = PRIV_DATA, \
		.user = USER, \
		.passwd = PASSWD, \
		.srv_host = SRV_HOST, \
		.srv_port = SRV_PORT, \
		.keep_alive = KEEP_ALIVE, \
		.event_ops = EVENT_OPS \
	}

/*
 * IPC_CLIENT_INFO_FAST - 快速创建struct ipc_client_info结构变量
 * @name: ipc_client_info变量名称
 * @id: ipc通信实体id标识
 * @host: ipc通信服务器主机名（字串指针）
 * @priv_data: 用户私有数据指针，用于回调引用；若不需要，可以设为NULL
 * @port: ipc通信服务器监听端口（整型，0~65535）
 * @ev_ops：struct ipc_event_ops指针
 *
 */
#define IPC_CLIENT_INFO_FAST(name, id, host, port, priv_data, ev_ops) \
	struct ipc_client_info name = __IPC_CLIENT_INFO(id, true, priv_data, NULL, NULL, host, port, 5, ev_ops)

/*
 * IPC_CLIENT_INFO - 创建struct ipc_client_info结构变量
 * @name: 同IPC_CLIENT_INFO_FAST
 * @id: 同IPC_CLIENT_INFO_FAST
 * @clean_session: 布尔值，用于指示是否清除ipc通信服务器侧的session信息
 * @priv_data: 同IPC_CLIENT_INFO_FAST
 * @user: 字串指针，指定ipc通信服务器的username
 * @passwd: 字串指针，指定ipc通信服务器的passwd
 * @host: 同IPC_CLIENT_INFO_FAST
 * @port: 同IPC_CLIENT_INFO_FAST
 * @ka: 整型，指定与ipc通信的keepalive间隔
 * @ev_ops: 同IPC_CLIENT_INFO_FAST
 *
 */
#define IPC_CLIENT_INFO(name, id, clean_session, priv_data, user, passwd, host, port, ka, ev_ops)  \
	struct ipc_client_info name = __IPC_CLIENT_INFO(id, clean_session, priv_data, user, passwd, host, port, ka, ev_ops)

/*
 * bmser_ipc_create - 创建ipc通信实体
 * @ci: ipc通信实体的基本信息（用户提供）
 * @client: ipc通信实体（输出参数)，该参数仅当返回值为IPC_ERR_SUCCESS时有效
 *
 * Return 
 * @rc: 错误码（输出参数）
 *      IPC_ERR_SUCCESS             - 创建成功
 *      IPC_ERR_NO_MEM              - 内存不足
 *      IPC_ERR_INVAL               - 传参错误
 */
extern int bmser_ipc_create(const struct ipc_client_info *ci, struct ipc_client **client);

/*
 * bmser_ipc_destroy - 清除ipc通信实体
 * @c: ipc通信实体
 *
 * Return
 *       无
 *
 * 使用者需要保证：
 *       1. 调用该函数前，所有的对该ipc通信实体的调用均已完成，不应存在其他线程仍在调用bmser_ipc_xxx接口未返回
 *       2. 调用该函数后，不再有对该ipc通信实体的调用
 */
extern void bmser_ipc_destroy(struct ipc_client *c);

/*
 * 订阅消息的回调处理函数类型，bmser_ipc_subscribe中使用该类型
 */
typedef void (*ipc_msg_cb_func)(struct ipc_client *c, struct ipc_message *m);

/*
 * bmser_ipc_subscribe - 订阅ipc通信topic
 * @c: ipc通信实体
 * @t: ipc通信topic
 * @f: 本topic消息指定的处理函数，若为NULL表示无特定处理函数
 *
 * Return
 *      IPC_ERR_SUCCESS - 订阅成功
 *      IPC_ERR_NO_MEM - 内存不足
 *      IPC_ERR_INVAL - 参数非法
 *      IPC_ERR_NO_CONN - 未与IPC server建连
 *      IPC_ERR_MALFORMED_UTF8 - topic不是UTF-8
 *      IPC_ERR_OVERSIZE_PACKET - 最终报文size不支持
 *
 * 注意：
 *      1. topic消息处理函数不应做耗时操作，否则会导致ipc模块阻塞。
 *      2. 若f为空，且创建ipc通信实体时(bmser_ipc_create)未指定on_message
 *         回调，则收到的对应topic消息缓存，待用户调用bmser_ipc_read_message同步读取。
 */
extern int bmser_ipc_subscribe(struct ipc_client *c, const struct ipc_topic *t, ipc_msg_cb_func f);

/*
 * bmser_ipc_publish - 发布ipc通信消息
 * @c: ipc通信实体
 * @t: ipc通信topic
 * @p: ipc通信消息payload
 * @retain: 是否保留
 *
 * Return
 *      IPC_ERR_SUCC - 发布成功
 *      IPC_ERR_NO_MEM - 内存不足
 *      IPC_ERR_INVAL - 参数非法
 *      IPC_ERR_NO_CONN - 未与IPC server建连
 *      IPC_ERR_PAYLOAD_SIZE - payload size超限
 *      IPC_ERR_MALFORMED_UTF8 - topic不是UTF-8
 *      IPC_ERR_QOS_NOT_SUPPORTED - IPC server不支持的qos
 *      IPC_ERR_OVERSIZE_PACKET - 最终报文size不支持
 *
 */
extern int bmser_ipc_publish(struct ipc_client *c, const struct ipc_topic *t, const struct ipc_payload *p, bool retain);

/*
 * bmser_ipc_get_message - 同步读取ipc消息
 * @c: ipc通信实体
 * @m: 输出参数，用于存放ipc消息指针
 *
 * Return
 *      IPC_ERR_SUCC - 读取消息成功
 *      IPC_ERR_EAGAIN - 暂无消息
 *
 * 注意：
 *      1. 该接口获取到的ipc_message需要调用bmser_ipc_release_message手动释放，否则会引起内存泄露
 *      2. 调用者需要周期调用接口提取消息，否则会导致历史消息被覆盖
 */
extern int bmser_ipc_get_message(struct ipc_client *c, struct ipc_message **m);

/*
 * bmser_ipc_release_message - 同步读取ipc消息
 * @c: ipc通信实体
 * @m: ipc消息
 *
 * Return
 *      IPC_ERR_SUCC - 读取消息成功
 *      IPC_ERR_EAGAIN - 暂无消息
 *
 * 注意：调用该函数前需要确保消息已无任何线程在使用，否则会造成野指针问题
 */
extern void bmser_ipc_release_message(struct ipc_message *m);
