//
// Created by Ht on 2021/2/6.
//

#ifndef LIBDEVCPP_CMDNS_H
#define LIBDEVCPP_CMDNS_H


/**
 *  基于UDP广播的名字地址服务
 *      运行后，各服务可向其广播源，其它服务可向其查询服务。
 *      1、服务的定义，由json消息结构完成。各通信消息不分包；
 *      2、局域网采用组播机制进行通信；
        ~，命令类型，p为公告服务
        @，服务端口类型
        #，安全印记，用于较长生存周期的服务、防止其它非法撤销该服务
        l，数据有效生成周期，整型、单位秒
        d，服务数据：
            k：服务关键字
            h：主机地址、必须与数据的来源端口相同，可选
            p：服务端口
            i：服务优先级
            c: 服务描述

 *      已发现的服务结构分类树：
 *          tcp
 *            +----- key1
 *                     +==== priority->source_ip : port : disc
 *                     +==== priority->source_ip : port : disc
 *            +----- key2
 *                     +==== priority->source_ip : port : disc
 *                     +==== priority->source_ip : port : disc
 *          udp
 *            +----- key1
 *                     +==== priority->source_ip : port : disc
 *                     +==== priority->source_ip : port : disc
 *            +----- key2
 *                     +==== priority->source_ip : port : disc
 *                     +==== priority->source_ip : port : disc
 */

#if defined(__cplusplus)
extern "C" {
#endif

#include "cm_hashtable.h"
#include "cmdns_hal_intf.h"

static const char *vkey_cmdns_cmd_ = "~";
static const char *vkey_cmdns_type_ = "@";
static const char *vkey_cmdns_secc_ = "#";
static const char *vkey_cmdns_ttl_ = "l";
static const char *vkey_cmdns_payload_ = "d";
static const char *vkey_cmdns_payload_key_ = "k";
static const char *vkey_cmdns_payload_sub_ = "n";       //子网
static const char *vkey_cmdns_payload_host_ = "h";
static const char *vkey_cmdns_payload_port_ = "p";
static const char *vkey_cmdns_payload_priority_ = "i";
static const char *vkey_cmdns_payload_query_type_ = "t";
static const char *vkey_cmdns_payload_comment_ = "c";

static const char *sval_default_cmdns_type_tcp_ = "tcp";
static const char *sval_default_cmdns_type_udp_ = "udp";
static const char *sval_default_cmdns_subnet_open_ = "#0";

static const int ival_default_cmdns_priority = 1000; //默认优先级
static const int ival_default_cmdns_ttl = 20;        //服务生存有效期
static const unsigned char ucval_sec_key_len_ = 8;   //安全码长度，16
static const char *sval_default_cmdns_addr_ = "224.0.0.253";
static const int ival_default_cmdns_port_ = 61053;
static const int ival_default_cmdns_port_min_ = 10000;
static const char *sval_default_cmdns_query_type_ = "a";
static const int MAX_CMDNS_BUFF_SIZE = 2048;

static const char *publishMessage
        = "{\"~\":\"p\",\"@\":\"%s\",\"l\":%d,\"d\":{\"#\":\"%s\",\"k\":\"%s\", \"p\":%d,\"i\":%d,\"c\":\"%s\",\"n\":\"%s\"}}";

static const char *withdrawMessage
        = "{\"~\":\"w\",\"@\":\"%s\",\"d\":{\"#\":\"%s\", \"k\":\"%s\",\"n\":\"%s\"}}";

static const char *queryMessage
        = "{\"~\":\"q\",\"@\":\"%s\",\"l\":%d,\"d\":{\"#\":\"%s\",\"k\":\"%s\",\"t\":\"%s\",\"n\":\"%s\"}}";

static const char *answerMessage
        = "{\"~\":\"r\",\"@\":\"%s\",\"d\":%s}";

static const char *quitMessage
        = "{\"~\":\"x\",\"#\":\"%s\"}";

typedef struct _cmdns {
    CmHashTable *pubs_;
    CmHashTable *qrys_;
    void *context_obj_;
    char* addr_;
    int   port_;
} CMDns;

typedef void (*servicePublishLocker) (void *publish, CMBool lockType);

typedef struct _service_pub {
    CMDns *cmdns_;
    const char *type_;
    const char *service_key_;
    int priority_;
    int port;
    const char *code;
    int ttl;
    const char *comment_;
    const char *subnet_;

    char *publish_buff_;
    CMSize publish_buff_size_;
    char *withdraw_buff_;
    CMSize withdraw_buff_size_;
    CMTimer *timer_post_;
    CMNetIntf *net_intf_;
    CMBool stop_;

    servicePublishLocker locker_;
    pthread_mutex_t*    mutex_;
} CMDnsServicePublish;

typedef struct _service_queried {
    const char *type;
    const char *service_key;
    int priority;
    const char *host;
    int port;
    const char *code;
    const char *comment;
    const char *subnet;
} CMdnsServiceQueried;

//回调函数
typedef void (*serviceQueryHandler) (void *query,const CMdnsServiceQueried services[], int svc_cnt, const char* ip, int port);
typedef void (*serviceQueryLocker) (void *query, CMBool lockType);

typedef struct _service_qry {
    CMDns *cmdns_;
    const char *type_;
    const char *service_key_;
    const char *subnet_;
    const char *code;   //查询码，标志查询本身
    int ttl;
    const char *query_type_;
    char *query_buff_;
    CMSize query_buff_size_;
    CMTimer *timer_post_;

    char *read_buff_;
    CMSize read_buff_size_;
    CMTimer *timer_read_;
    CMNetIntf *net_intf_;
    serviceQueryHandler handler_;
    serviceQueryLocker  locker_;
    pthread_mutex_t*    mutex_;
    unsigned char stop_;
} CMDnsServiceQuery;


JE_API CMDns *createCmDns(CMDns **cmdns, void *obj);
JE_API void releaseCmDns(CMDns *cmdns);
JE_API void clearCmDns(CMDns *cmdns);
JE_API void setCmDnsAddr(CMDns *cmdns, const char* addr, int port);

JE_API CMDnsServicePublish *
createCmDnsServicePublishP(CMDns *cmdns, const char *type, const char *key, int priority, int port, int ttl, const char *comment, const char* subnet, servicePublishLocker locker);

JE_API CMDnsServicePublish *
createCmDnsServicePublish(CMDns *cmdns, const char *type, const char *key, int priority, int port, int ttl, const char *comment, const char* subnet);


JE_API void releaseServicePublish(CMDns *cmdns, CMDnsServicePublish *pub);
JE_API CMBool publishCmDnsService(CMDns *cmdns, CMDnsServicePublish *pub);
JE_API void withdrawService(CMDns *cmdns, CMDnsServicePublish *pub);

JE_API CMDnsServiceQuery *
createCmDnsServiceQueryP(CMDns *cmdns, const char *type, const char *key, const char *subnet, int ttl, const char *queryType,
                        serviceQueryHandler handler, serviceQueryLocker locker);
JE_API CMDnsServiceQuery *
createCmDnsServiceQuery(CMDns *cmdns, const char *type, const char *key, const char *subnet, int ttl, const char *queryType,
                        serviceQueryHandler handler);

JE_API void releaseServiceQuery(CMDns *cmdns, CMDnsServiceQuery *query);
JE_API CMBool queryCmDnsService(CMDns *cmdns, CMDnsServiceQuery *query);

JE_API void stopQuery(CMDns *cmdns, CMDnsServiceQuery *query);
JE_API void stopQueries(CMDns *cmdns);

#if defined(__cplusplus)
}
#endif


#endif //LIBDEVCPP_CMDNS_H
