/*
 * EH_Client.cpp
 *
 */
#include <arpa/inet.h>
#include <errno.h>
#include <EH_Exception.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "EH_Client.h"

#include "json/reader.h"
#include "json/writer.h"
#include "json/value.h"
#include "LSC_Logger.h"
#include "../lib/EH_DEBUG.h"
#include "EH_ToolKit.h"
#include "../lib/EH_SDKDef.h"

string EH_Client::getClientStatusStr(ClientStatus status) {
	string statusString = "UNKNOWN";
	switch (status) {
	case UNINIT:
		statusString = "UNINIT";
		break;
	case INITING:
		statusString = "INITING";
		break;
	case INIT_FINISH:
		statusString = "INIT_FINISH";
		break;
	case FIND_CCU_IP:
		statusString = "FIND_CCU_IP";
		break;
	case LINK_BUILDING:
		statusString = "LINK_BUILDING";
		break;
	case LINK_BUILD_OK:
		statusString = "LINK_BUILD_OK";
		break;
	case LINK_BUILD_FAILED:
		statusString = "LINK_BUILD_FAILED";
		break;
	case LINK_BROKEN:
		statusString = "LINK_BROKEN";
		break;
	case LOGINING:
		statusString = "LOGINING";
		break;
	case LOGIN_FINISH_OK:
		statusString = "LOGIN_FINISH_OK";
		break;
	case LOGIN_FINISH_FAILED:
		statusString = "LOGIN_FINISH_FAILED";
		break;
	case WORKING:
		statusString = "WORKING";
		break;
	case STOP:
		statusString = "STOP";
		break;
	default:
		statusString = "UNKNOWN";
		break;
	}

	return statusString;
}

void* EH_Client::run0(void* opt) {
	EH_Client* client = (EH_Client*) opt;  //这个的含义是什么？
	return client->run(opt);
}

EH_Client::EH_Client() :sendMsgErrorSum(0) {
	connectToCcuFd = -1;
	ccuServerIp = "";
	destroyed = false;
}

int EH_Client::init(string ccuId, string accessKey,
		EH_ClientCallBackHandler* cb, bool autoDestroyHandler) {
	if (cb == NULL) {
		EH_WARNING("please set client callback handler.\n");
		return -1;
	}
	LOGI("EH_Client:%p.\n", this);
	this->cb = cb;
	changeClientStatus(INITING);
	this->ccuId = ccuId;
	this->accessKey = accessKey;
	this->ccuServerPort = SERVER_LISTEN_PORT;
	this->autoDestroyHandler = autoDestroyHandler;
	changeClientStatus(INIT_FINISH);

	if (this->ccuId == "") {
		ccuServerIp = "127.0.0.1";
		ccuServerPort = 5000;
	}

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

	/*创建一个新线程，从执行START-ROUTINE开始
	正在传递ARG。创造属性来自属性。新的
	句柄存储在*NEWTHREAD中。*/
	pthread_create(&tid, NULL, run0, this);

	return 0;
}

EH_Client::ClientStatus EH_Client::getClientStatus() const {
	return currentStatus;
}

int EH_Client::sendRequest(string nodeId, string opcode, string arg,
		string requester) {
	if (currentStatus != WORKING) {
		EH_WARNING("Client current status:%s NOT in WORKING,please wait...\n",
				getClientStatusStr(currentStatus).c_str());
		return -1;
	}
	/*create json request*/
	string loginReq = "!"
			+ EH_ToolKit::CreateReqJsonMsg(opcode, arg, requester, nodeId)
			+ "$";
	return sendMsgToServer(loginReq);
}

void EH_Client::processRecvMsg(string msg) {
	//	EH_DEBUG("recvMsgFromServer:%s. \n", msg.c_str());
		if (currentStatus == LOGINING || currentStatus == WORKING) {
			bool error = false;
			EH::Json::Value recvMsgObj = parseRecvMsg(msg, error);
			if (error) {
				return;
			}
			if (currentStatus == LOGINING) {
				if (recvMsgObj["opcode"].asString() == "LC_LOGIN"
						|| recvMsgObj["opcode"].asString() == "LOGIN") {
					if (recvMsgObj["status"].asString() == "success") {
						changeClientStatus(LOGIN_FINISH_OK);
						changeClientStatus(WORKING);
					} else {
						changeClientStatus(LOGIN_FINISH_FAILED);
					}
				} else {/*not login resp msg,drop it*/
				}
			} else if (currentStatus == WORKING) {
				/*push msg to 3rd handler*/
				if (cb) {
					if(recvMsgObj["opcode"].asString()!= "SET_HOMEBRIDGE_STATUS"){
					cb->onRecvMsg(recvMsgObj["nodeid"].asString(),
							recvMsgObj["opcode"].asString(), recvMsgObj["arg"],
							recvMsgObj["status"].asString());
					}
				}
			}
		} else {/*drop msg*/
			EH_WARNING("client status not in WORKING,drop recv msg:%s.\n",
					msg.c_str());
		}

////	EH_DEBUG("recvMsgFromServer:%s. \n", msg.c_str());
//	if (currentStatus == WORKING) {
//			/*push msg to 3rd handler*/
//			if (cb) {
////				cb->onRecvMsg(msg);
//			}
//
//	} else {/*drop msg*/
//		EH_WARNING("client status not in WORKING,drop recv msg:%s.\n",msg.c_str());
//	}
}


EH::Json::Value EH_Client::parseRecvMsg(string msg, bool& error) {
	error = false;
	EH::Json::Value jsonObject;
	try {
		EH::Json::Reader reader;
		if (!reader.parse(msg, jsonObject) || !jsonObject.isObject()) {
			EH_WARNING("Message format is wrong.primary msg is: %s\n",
					msg.c_str());
			error = true;
		}
	} catch (EH_Exception &e) {
		EH_WARNING(
				"Messege is not match with our protocol. primary msg is:%s\n",
				msg.c_str());
		error = true;
	}
	return jsonObject;
}

void EH_Client::sendLoginReq() {
	string loginReq = "";
	if (ccuId == "") {
		loginReq = "!{\"nodeid\":\"*\",\"opcode\":\"LC_LOGIN\",\"arg\":{\"username\":\"local_root\",\"password\":\"local_root\"},\"requester\":\"EH_Server\"}$";
	} else {
		EH::Json::Value arg;
		arg["device"] = "kk_c++_sdk";
		arg["seq"] = "100";
		arg["device_id"] = "CCU_" + ccuId;
		arg["access_key"] = accessKey;
		arg["token"] = "1212";
		arg["version"] = "1.0.0";
		arg["username"] = "C++_SDK";
		loginReq = "!"+ EH_ToolKit::CreateReqJsonMsg("LOGIN_ACCESSKEY",arg.toStyledString(), "HJ_Server") + "$";
	}
	int len = sendMsgToServer(loginReq);
	if(len > 0){
		changeClientStatus(WORKING);
	}
}

void EH_Client::changeClientStatus(ClientStatus newStatus) {
	currentStatus = newStatus;
	if (cb) {
		cb->onClientStatusChanged(currentStatus);
	}
}

int EH_Client::sendMsgToServer(string msg) {
	char* buf = (char*) msg.c_str();
	LOGI("sendMsgToServer:%s. \n", msg.c_str());
	int msgLen = msg.length();
	int result, oldLen = msg.length();
	do {
		result = send(connectToCcuFd, buf, msgLen, 0);
		if (result == -1)
			return -1;
		msgLen -= result;
		buf += result;
	} while (msgLen);
	return oldLen;
}

void EH_Client::onCCUIpChanged(string ccuNewIp) {
	EH_DEBUG("ccuId:%s ip:%s.try connect to ccu.\n", ccuId.c_str(),ccuNewIp.c_str());
	ccuServerIp = ccuNewIp;
	if (currentStatus == WORKING) {
		/*try connect...*/
	}
}

void* EH_Client::run(void* opt) {
	while (!destroyed) {
		if (ccuServerIp == "") {/*ccu ip not set,unit FIND_CCU return ccu addr  ccu ip未设置，单元查找ccu返回ccu地址*/
			sleep(2);
			continue;
		}
		changeClientStatus(LINK_BUILDING);
		/*build connection*/
		connectToCcuFd = socket(AF_INET, SOCK_STREAM, 0);
		if (connectToCcuFd < 0) {
			//创建失败
			perror("socket");

			changeClientStatus(LINK_BUILD_FAILED);
			connectToCcuFd = -1;
			sleep(2);
			continue;
		}
		//		名称：memset
		//		原型：void*memset（void*block，int c，size\t size）
		//		说明：
		//		此函数将c的值（转换为无符号字符）复制到对象的每个第一个大小字节中
		//		从块开始。它返回block的值。
		//		头文件：
		//		 memset(&addr, 0, sizeof(addr));
		struct sockaddr_in addr;
		addr.sin_family = AF_INET;
		addr.sin_port = htons(ccuServerPort);
		addr.sin_addr.s_addr = inet_addr(ccuServerIp.c_str());
//		addr.sin_addr.s_addr = htonl(INADDR_ANY) ;    //把一个u_long类型的值转化为网络字节顺序
//		addr.sin_addr.s_addr = INADDR_ANY;   //ip匹配所有的ip
		socklen_t addr_len = sizeof(addr);
		int connect_fd = connect(connectToCcuFd, (struct sockaddr*) &addr,
				addr_len);
		if (connect_fd < 0) {
			perror("connect");
			changeClientStatus(LINK_BUILD_FAILED);
			close(connectToCcuFd);
			connectToCcuFd = -1;
			sleep(2);
			continue;
		}

		timeval tv = { 2, 0 };
		int nb = setsockopt(connectToCcuFd, SOL_SOCKET, SO_RCVTIMEO,
				(char*) &tv, sizeof(timeval));
		if (nb == -1) {
			perror("setsockopt");
			changeClientStatus(LINK_BUILD_FAILED);
			close(connectToCcuFd);
			connectToCcuFd = -1;
			sleep(2);
			continue;
		}

		changeClientStatus(LINK_BUILD_OK);
		changeClientStatus(LOGINING);
		sendLoginReq();

		while (!destroyed) {
			char buf[RECV_MSG_SIZE] = { 0 };
			int rec = recv(connectToCcuFd, buf, RECV_MSG_SIZE - 1, 0); //接收信息
			if (rec > 0) {
				string msg = cacheInputMsg + string(buf);

				vector<string> msgs;
				EH_ToolKit::Split(msg, "$", msgs);
				/*
				 * split:
				 * !xxxx$
				 * return:
				 *       1:!xxxx
				 *       2:space
				 */
				for (uint i = 0; i < msgs.size() - 1; i++) {
					string totalMsg = EH_ToolKit::Trim(msgs[i]);
					if (totalMsg.find('!') == 0) {
						totalMsg.erase(0, 1);/*remove !*/

						/*recv total msg*/
						processRecvMsg(totalMsg);
					} else {/*should not happen 不应该发生*/
						EH_WARNING("recv invaild msg:%s.\n", totalMsg.c_str()); //无损MSG
					}
				}
				/*
				 * no found $
				 * if (msgs.size() == 1) {
				 }
				 */
				cacheInputMsg = msgs[msgs.size() - 1];
			} else if (rec < 0) {
				if (errno == EWOULDBLOCK) {
					continue;
				}
			} else {/*rec == 0*/
				changeClientStatus(LINK_BROKEN); //LINK_BROKEN 连接断开
				break;
			}
		}
	}
	delete this;
	return NULL;
}

void EH_Client::destroy() {  //摧毁  ——》主要是什么作用？？？
	destroyed = true;
	close(connectToCcuFd);
	EH_DEBUG("destroy:%s.\n", ccuId.c_str());
}

EH_Client::~EH_Client() {
	close(connectToCcuFd);
	cb = NULL;

	EH_INFO("~EH_Client:%p.\n", this);
}

