/*
 * KK_MqttSdk.cpp
 *
 *  Created on: Dec 2, 2018
 *      Author: sds
 */
#include "KK_MqttSdk.h"

#include "EH_DEBUG.h"
#include "json/reader.h"
#include "json/writer.h"
#include "json/value.h"
#include "LSC_Logger.h"
#include "ace/Reactor.h"

extern "C"{
#include "MQTTAsync.h"
#include "string.h"
#include <time.h>
}

using namespace std;

#define TIMEOUT  10000L

static MQTTAsync S_client = NULL;
static bool S_Working;

static const char* OPT_SEND = "MQTTAsync_sendMessage";
static const char* OPT_SUB = "MQTTAsync_subscribe";
static const char* OPT_UNSUB = "MQTTAsync_unsubscribe";

KK_MQTTSDK* KK_MQTTSDK::GetInstance() {
	if (ms_Instance == NULL) {
		ms_Instance = new KK_MQTTSDK();
	}
	return ms_Instance;
}

KK_MQTTSDK* KK_MQTTSDK::ms_Instance = NULL;

KK_MQTTSDK::KK_MQTTSDK() {

}

//连接丢失
static void onConnectLost(void *context, char *cause){
	S_Working = false;
	LOGW(" 连接丢失 断开原因onConnectLost,disconnect cause:%s \n",cause);
	KK_MQTTSDK::EH_SDKCallbackHandler*  callback = KK_MQTTSDK::GetInstance()->getSdkCallbackHandler();
	callback->onCloudDisConnect();
//	KK_MQTTSDK::GetInstance()->recon(3);
}

//连接断开时
static void onDisConnected(void *context, MQTTProperties* properties,enum MQTTReasonCodes reasonCode){
	S_Working = false;
	LOGW("onDisConnected，断开连接，可能被broker踢出.\n");
	KK_MQTTSDK::EH_SDKCallbackHandler*  callback = KK_MQTTSDK::GetInstance()->getSdkCallbackHandler();
	callback->onCloudDisConnect();
//	KK_MQTTSDK::GetInstance()->recon(3);
}

//连接建立
static void onConnectBuild(void *context, char *cause){
	S_Working = true;
	LOGI("onConnectBuild,disconnect cause:%s \n",cause);
	KK_MQTTSDK::EH_SDKCallbackHandler*  callback = KK_MQTTSDK::GetInstance()->getSdkCallbackHandler();
	callback->onCloudConnect();
}

//连接失败
static void onConnectFailure(void* context, MQTTAsync_failureData* response){
	LOGW("onConnectFailure, return: %d\n", response ? response->code : 0);
//	KK_MQTTSDK::GetInstance()->recon(3);
}

//连接成功
static void onConnectSuccess(void* context, MQTTAsync_successData* response){
	LOGI("onConnectSuccess to server:%s,version:%d,sessionPresent:%d \n",
				response->alt.connect.serverURI,response->alt.connect.MQTTVersion,
				response->alt.connect.sessionPresent);
}

//消息到达时
static int onMessageArrived(void* context, char* topicName, int topicLen, MQTTAsync_message* message){
	string payload((char *)message->payload,message->payloadlen);
	string TopicName = topicName;

	LOGI("MQTT topic:%s,   payload:%s.\n",topicName,payload.c_str());
	KK_MQTTSDK::EH_SDKCallbackHandler*  callback = KK_MQTTSDK::GetInstance()->getSdkCallbackHandler();
	callback->onCloudMsgRecv(payload,TopicName);
	MQTTAsync_freeMessage(&message);
	MQTTAsync_free(topicName);
	return 1;
}

//当事件完成时
static void onDeliveryComplete(void* context, MQTTAsync_token token){
	LOGD("onDeliveryComplete,token:%d \n",token);
}

static void onOptSuccess(void* context, MQTTAsync_successData* response){
	if(strcmp((char *)context,OPT_SEND)==0){
		LOGD("MQTTAsync_sendMessage success,return token:%d,msg length:%d \n",
				response->token,response->alt.pub.message.payloadlen);

	}else if(strcmp((char *)context,OPT_SUB)==0){
		LOGI("MQTTAsync_subscribe success,return token:%d \n",response->token);
	}else if(strcmp((char *)context,OPT_UNSUB)==0){
		LOGI("MQTTAsync_unsubscribe success,return token:%d \n",response->token);
	}
}

static void onOptFail(void* context,  MQTTAsync_failureData* response){
	if(strcmp((char *)context,OPT_SEND)==0){
		LOGW("MQTTAsync_sendMessage fail,token:%d,code:%d,msg:%s \n",
				response->token,response->code,response->message);
	}else if(strcmp((char *)context,OPT_SUB)==0){
		LOGW("MQTTAsync_subscribe fail,return token:%d \n",response->token);
	}else if(strcmp((char *)context,OPT_UNSUB)==0){
		LOGW("MQTTAsync_unsubscribe fail,return token:%d \n",response->token);
	}
}

//回调打印日志的接口
static void mqttTraceCallback(enum MQTTASYNC_TRACE_LEVELS level, char *message){
//	LOGD("mqttTraceCallback level:%d,msg:%s.\n",level,message);
}


int KK_MQTTSDK::handle_timeout(const ACE_Time_Value &current_time, const void *act) {
	destroy();
	if(reinit("")!= 0){
		ACE_OS::exit(1);
		return 0;
	}
	con(0,2);
	return 0;
}

void KK_MQTTSDK::recon(int delayInSec){
	ACE_Time_Value delay(delayInSec);
	reconTimerId = ACE_Reactor::instance()->schedule_timer(this, NULL, delay);
	if(reconTimerId!=-1){
		LOGI("set recon timer:%d in %d seconds delay.\n",reconTimerId,delayInSec);  //设置恢复计时器：%d（延迟%d秒）
	}else{
		LOGW("set recon timer failed.\n");
	}
}

int KK_MQTTSDK::init(string brokerAddr,string clientId,string username,string password,string willTopic,string heartbeat_Topic,string PTPO_Topic,string gw_id) {
	LOGD("MQTT 初始化.\n");
	if(this->callbackHandler == NULL){
		LOGE("callbackHandler not set.\n");
		return -1;
	}
	this->brokerAddr = brokerAddr;
	this->clientId = clientId;
	this->username = username;
	this->password = password;
	this->willTopic = willTopic;
	this->PTPO_Topic = PTPO_Topic;
	this->heartbeat_Topic = heartbeat_Topic;
	this->gw_id = gw_id;


	//打印日志的回调函数
	MQTTAsync_setTraceCallback(mqttTraceCallback);

	MQTTAsync_createOptions opts  = MQTTAsync_createOptions_initializer;
//	opts.MQTTVersion = MQTTVERSION_3_1_1;
	opts.MQTTVersion = MQTTVERSION_5;
	//此函数创建一个MQTT客户端，准备连接到*指定的服务器并使用指定的持久性存储（请参阅* MQTTAsync_persistence）
	int rc = MQTTAsync_createWithOptions(&S_client,(const char* )brokerAddr.c_str(),
			(const char* )clientId.c_str(), MQTTCLIENT_PERSISTENCE_NONE,NULL,&opts);

	LOGI("MQTTAsync_create new client:0x%0x.\n",S_client);

	if(rc != MQTTASYNC_SUCCESS){
		LOGW("Failed to create mqtt sdk client, return code %d\n", rc);
		return -1;
	}

	//此函数用于为特定客户端的连接丢失事件设置回调函数
	MQTTAsync_setConnectionLostCallback(S_client,NULL,onConnectLost);
	//此函数用于为特定客户端的消息到达事件设置回调函数
	MQTTAsync_setMessageArrivedCallback(S_client,NULL,onMessageArrived);
	//此函数用于为特定客户端的传递完成事件设置回调函数
	MQTTAsync_setDeliveryCompleteCallback(S_client,NULL,onDeliveryComplete);
	// 为客户端设置MQTTAsync_connected（）回调函数
	MQTTAsync_setConnected(S_client,NULL,onConnectBuild);
	//为客户端设置MQTTAsync_disconnected（）回调函数
	MQTTAsync_setDisconnected(S_client,NULL,onDisConnected);

	return 0;
}

//设置回调，
void KK_MQTTSDK::setSdkCallbackHandler(EH_SDKCallbackHandler* handler){
	callbackHandler = handler;
}

KK_MQTTSDK::EH_SDKCallbackHandler* KK_MQTTSDK::getSdkCallbackHandler(){
	return callbackHandler;
}

int KK_MQTTSDK::subTopic(string topic,int qos,int waitTimeout){
	if(!S_Working){
		LOGW("No Connection build to MQ Server.\n");
		return -1;
	}
	LOGI("to subtopic:%s \n",topic.c_str());


	// Init list of topics to subscribe
    char** topicsPath  = (char**)calloc(10, sizeof(char*));
    int* topicsQoS     = (int*)calloc(10, sizeof(int));
   //ptp应用接口订阅的Topic
    topic1 = topic+"/device/delete"; //删除子设备
    topicsPath[0]   = const_cast<char*>(topic1.c_str()); 
    topicsQoS[0]    = 0;
    topic2 = topic+"/device/set"; //6. 改变子设备状态
    topicsPath[1]   = const_cast<char*>(topic2.c_str());
    topicsQoS[1]    = 0;
	topic3 = topic+"/devices_public"; //13. 获取设备列表信息
    topicsPath[2]   = const_cast<char*>(topic3.c_str());
    topicsQoS[2]    = 0;
	topic4 = topic+"/cg_custom_msg"; //16. XCG发送自定义数据到PTP
    topicsPath[3]   = const_cast<char*>(topic4.c_str());
    topicsQoS[3]    = 0;
	topic5 = topic+"/firmware/check/result"; //18. 查询最新的升级任务结果
    topicsPath[4]   = const_cast<char*>(topic5.c_str());
    topicsQoS[4]    = 0; 
	topic6 = topic+"/device/search"; //2. 搜索子设备
    topicsPath[5]   = const_cast<char*>(topic6.c_str());
    topicsQoS[5]    = 0; 
	topic7 = topic+"/device/stopsearch"; //20. 停止搜索子设备
    topicsPath[6]   = const_cast<char*>(topic7.c_str());
    topicsQoS[6]    = 0; 
	//ptpo运维订阅的Topic
    topic8 = PTPO_Topic+"/switch"; //21. 网关地址切换
    topicsPath[7]   = const_cast<char*>(topic8.c_str()); 
    topicsQoS[7]    = 0;
	topic9 = PTPO_Topic+"/optcall"; //25. 运维功能调用
    topicsPath[8]   = const_cast<char*>(topic9.c_str()); 
    topicsQoS[8]    = 0;
    topic10 = PTPO_Topic+"/gwevent"; //27. 网关事件通知
    topicsPath[9]   = const_cast<char*>(topic10.c_str()); 
    topicsQoS[9]    = 0;
	// Create subcribe parameters
	MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
	int rc ;
	opts.onSuccess = onOptSuccess;
	opts.onFailure = onOptFail;
	opts.context = (void*)OPT_SUB;
    for(int i = 0; i < 10 ; i++)
    {
    	LOGI("topicsPath:%s topicsQoS: %d\n",topicsPath[i], topicsQoS[i]);
    }


	// Subscribe and disconnect if error
	if ((rc = MQTTAsync_subscribeMany(S_client,10,topicsPath,topicsQoS, &opts)) != MQTTASYNC_SUCCESS){//订阅多个主题
//	if ((rc = MQTTAsync_subscribe(S_client,topic.c_str(), qos, &opts)) != MQTTASYNC_SUCCESS){//订阅单个主题
		LOGW("Failed to start subscribe Many, return code:%d.\n", rc);
		return -1;
	}else{
		LOGI("SUCCESS to start subscribe Many, return code:%d.\n", rc);
	}
	return MQTTAsync_waitForCompletion(S_client, opts.token, waitTimeout);
}

//删除指定客户端进行的现有订阅
int KK_MQTTSDK::unsubTopic(string topic,int waitTimeout){
	LOGI("删除指定客户端进行的现有订阅 to unsubtopic:%s \n",topic.c_str());

	MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
	int rc;

	opts.onSuccess = onOptSuccess;
	opts.onFailure = onOptFail;
	opts.context = (void*)OPT_UNSUB;

	if ((rc = MQTTAsync_unsubscribe(S_client,topic.c_str(),  &opts)) != MQTTASYNC_SUCCESS){
		LOGW("Failed to start unubscribe, return code:%d.\n", rc);
		return -1;
	}
	return MQTTAsync_waitForCompletion(S_client, opts.token, waitTimeout);
}

int KK_MQTTSDK::sendMsg(string topic,string payload,int qos){
	if(!S_Working){
		LOGW("No Connection build to MQ Server.\n");
		return -1;
	}
	//连接状态
	int connite = MQTTAsync_isConnected(S_client);

	MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
	MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
	int rc;

	opts.onSuccess = onOptSuccess;
	opts.onFailure = onOptFail;
	opts.context =  (void*)OPT_SEND;

	pubmsg.payload = (void*)payload.c_str();
	pubmsg.payloadlen = payload.length();
	pubmsg.qos = qos;
	pubmsg.retained = 0;

	LOGD("客户机当前连接服务器状态 :%d  mqtt send payload len:%d, QOS:%d.\n",connite,pubmsg.payloadlen,qos);

	if ((rc = MQTTAsync_sendMessage(S_client, topic.c_str(), &pubmsg, &opts)) != MQTTASYNC_SUCCESS){
		LOGW("Failed to start sendMessage, return code:%d.\n", rc);
		return -1;
	}
	return 0;
}

int KK_MQTTSDK::reinit(string serverAddr) {
	LOGE("MQTT 重新 初始化.\n");
	if(this->callbackHandler == NULL){
		LOGE("callbackHandler not set.\n");
		return -1;
	}

	if(S_client!=NULL){
		LOGE("MQTTAsync handle not NULL.\n");
		return -1;
	}

	if(serverAddr!=""){
		this->brokerAddr = serverAddr;
	}
	MQTTAsync_createOptions opts  = MQTTAsync_createOptions_initializer;
	opts.MQTTVersion = MQTTVERSION_3_1_1;

	int rc = MQTTAsync_createWithOptions(&S_client,(const char* )brokerAddr.c_str(),
			(const char* )clientId.c_str(), MQTTCLIENT_PERSISTENCE_NONE,NULL,&opts);

	LOGD("MQTTAsync_create new client:0x%0x.\n",S_client);

	if(rc != MQTTASYNC_SUCCESS){
		LOGW("Failed to create mqtt sdk client, return code %d\n", rc);
		return -1;
	}
	MQTTAsync_setConnectionLostCallback(S_client,NULL,onConnectLost);
	MQTTAsync_setMessageArrivedCallback(S_client,NULL,onMessageArrived);
	MQTTAsync_setDeliveryCompleteCallback(S_client,NULL,onDeliveryComplete);
	MQTTAsync_setConnected(S_client,NULL,onConnectBuild);
	MQTTAsync_setDisconnected(S_client,NULL,onDisConnected);

	return 0;
}

int KK_MQTTSDK::con(int autoRecon,int timeoutInSec){
	if(S_client!=NULL){

		//连接状态信息
		MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;

		unsigned int seed = time(NULL);
		conn_opts.keepAliveInterval = ACE_OS::rand_r(&seed)%120+60;
		conn_opts.connectTimeout = timeoutInSec;
		conn_opts.automaticReconnect = autoRecon;
		conn_opts.minRetryInterval = 1;
		conn_opts.maxRetryInterval = 32;
		conn_opts.username = this->username.c_str();
		conn_opts.password = this->password.c_str();

		conn_opts.cleansession = 1;
		conn_opts.onSuccess = onConnectSuccess;
		conn_opts.onFailure = onConnectFailure;

		LOGD("MQTTAsync_connectOptions [keepAliveInterval:%d,automaticReconnect:%d,"
				"minRetryInterval:%d,maxRetryInterval:%d,cleansession:%d]\n",
				conn_opts.keepAliveInterval,conn_opts.automaticReconnect,conn_opts.minRetryInterval,
				conn_opts.maxRetryInterval,conn_opts.cleansession);

		//遗嘱信息
		MQTTAsync_willOptions will_opts = MQTTAsync_willOptions_initializer;
		will_opts.topicName = willTopic.c_str();
		will_opts.qos = 0;
		will_opts.retained = 0;
		will_opts.message= "offline";

		conn_opts.will = &will_opts;

		if(brokerAddr.substr(0,3)=="ssl"){
			LOGI("connect to broker use ssl.\n");
			MQTTAsync_SSLOptions ssl_opts = MQTTAsync_SSLOptions_initializer;
			ssl_opts.verify = 0;
			ssl_opts.enableServerCertAuth = 0;
			conn_opts.ssl = &ssl_opts;
		}else{
			LOGI("connect to broker use tcp.\n");
			conn_opts.ssl = NULL;
		}
		int rc = 0;
		LOGI("MQTTAsync_connect for client:0x%0x.\n",S_client);
		if ((rc = MQTTAsync_connect(S_client, &conn_opts)) != MQTTASYNC_SUCCESS){
			LOGW("Failed to start connect, return code %d\n", rc);
			return -1;
		}

		/*线程标识符。属性类型的结构不是故意暴露。*/
		pthread_t tid;

		/*创建一个新线程，从执行START-ROUTINE开始
		正在传递ARG。创造属性来自属性。新的
		句柄存储在*NEWTHREAD中。*/
		pthread_create(&tid, NULL, run0, this);
	}
	return -1;
}
void* KK_MQTTSDK::run0(void* opt) {
	KK_MQTTSDK* mqttheartbeat_pthread = (KK_MQTTSDK*) opt;  //这个的含义是什么？
	return mqttheartbeat_pthread->run(opt);
}

//MQTT心跳
void* KK_MQTTSDK::run(void* opt){
	EH::Json::FastWriter writer;
	EH::Json::Value Payload;
	string ptpo_heartbeat_Topic = PTPO_Topic+"/ptpo_keepalive";

	Payload["interval"] = 30;
	string sendPayload = writer.write(Payload);
	LOGD("Topic:%s Payload:%s\n",heartbeat_Topic.c_str(),sendPayload.c_str());
	sendMsg(heartbeat_Topic,sendPayload,0);

	LOGD("ptpo_heartbeat_Topic:%s Payload:%s\n",ptpo_heartbeat_Topic.c_str(),sendPayload.c_str());
	sendMsg(ptpo_heartbeat_Topic,sendPayload,0);
	while (true) {
		//与Roma平台30秒心跳
		sleep(30);
		LOGD("Topic:%s Payload:%s\n",heartbeat_Topic.c_str(),sendPayload.c_str());
		sendMsg(heartbeat_Topic,sendPayload,0);
		LOGD("ptpo_heartbeat_Topic:%s Payload:%s\n",ptpo_heartbeat_Topic.c_str(),sendPayload.c_str());
		sendMsg(ptpo_heartbeat_Topic,sendPayload,0);
	}
	//delete this;
	return NULL;
}

int KK_MQTTSDK::discon(){
	int rc = MQTTAsync_isConnected(S_client);
	LOGI("client:0x%0x is connected:%d.\n",S_client,rc);
	if(S_client!=NULL){
		MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer;
		int ret =  MQTTAsync_disconnect(S_client,&opts);
		LOGI("MQTTAsync_disconnect ret:%d.\n",ret);
		return ret;
	}
	return -1;
}

int KK_MQTTSDK::destroy(){
	S_Working = false;
	if(S_client!=NULL){
		LOGI("MQTTAsync_destroy  client:0x%0x.\n",S_client);
		MQTTAsync_destroy(&S_client);
		S_client = NULL;
	}
	return 0;
}



