﻿#include "jidu.h"
#include "tracer.h"
#include "jidu_macro.h"
#include "jidu_dataCenter.h"
#include "jidu_envConfig.h"
#include <mutex>
#include "binary.h"
#include "MyStringUtils.h"
#include <FlashFileUtils.h>


static std::mutex mItemLock;
static map<std::string, vector<JIDU_DTC_INFO_t>> m_allEcuDtcInfo;
static map<std::string, vector<std::string>> m_mapEcuDtcWhiteList;
static map<std::string, CBinary> m_allEcuDtcAck;
int jd_PCBU_4BYTE(uint8_t* binId, int inLen, char* outBuffer, int outBufferSize)
{
	if (outBufferSize < 7 && inLen < 6) {
		return -1;
	}
	char head = '*';
	switch (binId[3] & 0xC0)
	{
	case 0x00: {head = 'P'; binId[3] = binId[3] - 0x00; } break;
	case 0x40: {head = 'C'; binId[3] = binId[3] - 0x40; } break;
	case 0x80: {head = 'B'; binId[3] = binId[3] - 0x80; } break;
	case 0xC0: {head = 'U'; binId[3] = binId[3] - 0xC0; } break;
	default: break;
	}
	sprintf(outBuffer, "%c%02X%02X%02X", head, binId[3], binId[4], binId[5]);

	return 7;
}

inline bool dtc_calc(uint8_t* rf, int rfLen, vector<string>& vecDtcNo)
{
	uint8_t binDtcId[8] = { 0x8B, 0x01, 0x01, 0x00, 0x00, 0x00 };
	int iDtcNo = (rfLen - 3) / 4;

	string msg;
	for (int i = 0; i < iDtcNo; i++)
	{
		JIDU_DTC_INFO stcDtc;
		binDtcId[3] = rf[3 + i * 4];
		binDtcId[4] = rf[4 + i * 4];
		binDtcId[5] = rf[5 + i * 4];

		if ((binDtcId[3] == 0) && (binDtcId[4] == 0) && (binDtcId[5] == 0))
		{
			continue;
		}

		char dtcIdBuffer[128] = { 0 };
		int rLen = jd_PCBU_4BYTE(binDtcId, 6, dtcIdBuffer, 128);
		if (rLen > 0)
		{
			vecDtcNo.push_back(dtcIdBuffer);
		}
	}

	return true;
}

int jd_dtc_queryByDTCNo(const char* ecuName, const char* dtcNo, char* outBuffer, int outBufferSize)
{
	return -1;
}

int jd_dtc_readAll(const char* ecuName, uint8_t* dtcBytefiled, uint32_t inLen)
{
	std::lock_guard<std::mutex> lock(mItemLock);

	Tracer::hex(string("dtc[") + ecuName + "]", dtcBytefiled, inLen);
	vector<string> vecDtcNo;
	dtc_calc(dtcBytefiled, inLen, vecDtcNo);

	if (m_allEcuDtcInfo.end() != m_allEcuDtcInfo.find(ecuName)) {
		m_allEcuDtcInfo[ecuName].clear();
	}

	m_allEcuDtcAck[ecuName] = CBinary(dtcBytefiled, inLen);

	for (int i = 0; i < vecDtcNo.size(); i++) {

		JIDU_DTC_INFO_t dtcItem;
		dtcItem.strDTCNo = vecDtcNo.at(i);
		dtcItem.strContent = "未定义";
		m_allEcuDtcInfo[ecuName].push_back(dtcItem);
	}

	return (int)vecDtcNo.size();
}

int jd_dtc_setWhiteList(const char* whiteList)
{
	Tracer::warn("dtc whiteList", whiteList);
	m_mapEcuDtcWhiteList.clear();

	vector<std::string> vecEcu;
	MyStringUtils::split(vecEcu, whiteList, ";");

	for (string temp : vecEcu) {
		vector<std::string> vecTemp;
		MyStringUtils::split(vecTemp, temp, ":");
		if (vecTemp.size() == 2) {
			string ecuName = vecTemp.at(0);
			string dtcList = vecTemp.at(1);
			vector<std::string> vecDtc;
			MyStringUtils::split(vecDtc, dtcList, ",");
			for (string tempDtc : vecDtc) {
				m_mapEcuDtcWhiteList[ecuName].push_back(tempDtc);

				Tracer::warn("dtc whiteList: " + ecuName, tempDtc);
			}
		}
	}

	return int();
}

int reloadDtcMask(string filePath) {

	int errCode = 0;
	Tracer::debug(__FUNCTION__, filePath);
	FILE* fp = fopen(filePath.c_str(), "r");
	if (fp != NULL) {
		fseek(fp, 0, SEEK_END);
		int fileLenth = ftell(fp);
		fseek(fp, 0, SEEK_SET);
		char* tempBuffer = (char*)malloc(fileLenth + 1);
		fread(tempBuffer, 1, fileLenth, fp);
		fclose(fp);

		cJSON* objRoot = cJSON_Parse(tempBuffer);
		if (objRoot == NULL) {
			CFlashFileUtils fileUtil;
			string strConfig;
			if (fileUtil.LoadConfig(strConfig, filePath)) {
				objRoot = cJSON_Parse(strConfig.c_str());
			}
		}

		for (int i = 0; i < cJSON_GetArraySize(objRoot); i++) {
			cJSON* objItem = cJSON_GetArrayItem(objRoot, i);
			if (cJSON_HasObjectItem(objItem, "doipAddr")) {
				string doipAddr = cJSON_GetObjectItem(objItem, "doipAddr")->valuestring;
				char ecuName[32] = { 0 };
				if (get_jd_ecuName_byIpAddr(MyStringUtils::toLong(doipAddr, 16), ecuName, 32) > 0)
				{
					if (cJSON_HasObjectItem(objItem, "dtcList")) {
						cJSON* arrayDtcList = cJSON_GetObjectItem(objItem, "dtcList");

						vector<string> vecDtcMask;
						for (int j = 0; j < cJSON_GetArraySize(arrayDtcList); j++) {
							string dtcCode = cJSON_GetArrayItem(arrayDtcList, j)->valuestring;
							vecDtcMask.push_back(dtcCode);
							Tracer::debug("whiteList", string(ecuName) + ": " + dtcCode);
						}
						m_mapEcuDtcWhiteList.emplace(ecuName, vecDtcMask);
					}
				}
				else {
					Tracer::error("invalid doipAddr", doipAddr);
					errCode = -1;
				}
			}
		}
		cJSON_Delete(objRoot);

		free(tempBuffer);
	}
	else {
		Tracer::error("invalid filepath for dtcMask", filePath);
		errCode = -1;
	}
	return errCode;
}

int jd_dtc_reloadDtcMask()
{
	std::lock_guard<std::mutex> lock(mItemLock);
	m_mapEcuDtcWhiteList.clear();
	int errCode = 0;

	string appPath = EnvConfig::getInstance()->getAppPath();
	string configPath = appPath + "Diagnosis\\config\\" + EnvConfig::getInstance()->getEnvPath() + "\\config.json";
	Tracer::debug("config", configPath);
	FILE* fp = fopen(configPath.c_str(), "r");
	if (fp != NULL) {
		fseek(fp, 0, SEEK_END);
		int fileLenth = ftell(fp);
		fseek(fp, 0, SEEK_SET);
		char* tempBuffer = (char*)malloc(fileLenth + 1);
		fread(tempBuffer, 1, fileLenth, fp);
		fclose(fp);

		cJSON* objRoot = cJSON_Parse(tempBuffer);
		for (int i = 0; i < cJSON_GetArraySize(objRoot); i++) {
			cJSON* objItem = cJSON_GetArrayItem(objRoot, i);
			if (cJSON_HasObjectItem(objItem, "vehicleModelCode")) {
				string vehicleModelCode = cJSON_GetObjectItem(objItem, "vehicleModelCode")->valuestring;
				if (!vehicleModelCode._Equal(DataCenter::m_vehicleInfo.vehicleType)) {
					continue;
				}
			}

			if (cJSON_HasObjectItem(objItem, "configOptionName")) {
				string configOptionName = cJSON_GetObjectItem(objItem, "configOptionName")->valuestring;
				if (configOptionName._Equal("DtcMask")) {
					string resultFile = cJSON_GetObjectItem(objItem, "resultFile")->valuestring;

					errCode = reloadDtcMask(resultFile);
				}
			}
		}
		cJSON_Delete(objRoot);

		free(tempBuffer);
	}
	else {
		Tracer::error(__FUNCTION__, configPath);
	}

	return errCode;
}

int jd_dtc_isInWhiteList(const char* ecuName, const char* dtcNo)
{
	//TODO 白名单中的Ecu名称不包含域控后缀
	string newName = ecuName;
	if (MyStringUtils::endWith(newName, "_SOC") || MyStringUtils::endWith(newName, "_MCU")) {
		newName = newName.substr(0, newName.find('_'));
	}

	if (m_mapEcuDtcWhiteList.find(newName) != m_mapEcuDtcWhiteList.end()) {

		for (string tempDtc : m_mapEcuDtcWhiteList[newName]) {
			if (tempDtc._Equal(dtcNo)) {
				Tracer::debug(__FUNCTION__, string(newName) + ": " + dtcNo + ": Yes");
				return 1;
			}
		}
	}

	Tracer::debug(__FUNCTION__, string(newName) + ": " + dtcNo + ": No");
	return 0;
}

int jd_dtc_parsedByIndex(const char* ecuName, int index, char* outBuffer, int outBufferSize)
{
	memset(outBuffer, 0, outBufferSize);


	if (m_allEcuDtcInfo.end() == m_allEcuDtcInfo.find(ecuName)) {

		Tracer::error(ecuName, "has no dtc.");
		return -1;
	}
	vector<JIDU_DTC_INFO_t> vecDtcInfo = m_allEcuDtcInfo.at(ecuName);
	if (index >= vecDtcInfo.size()) {
		Tracer::error("index", "outof the bound.");
		return -1;
	}

	JIDU_DTC_INFO_t dtcItem = vecDtcInfo.at(index);

	if (outBufferSize > dtcItem.strDTCNo.size()) {
		memcpy(outBuffer, dtcItem.strDTCNo.c_str(), dtcItem.strDTCNo.size());
		return true;
	}

	Tracer::error("outBufferSize", "too small to receive.");
	return false;
}

int jd_dtc_report(char* statisticId, char* statisticName, char* blockId, char* blockName)
{
	for (const auto& kvPair : m_allEcuDtcInfo) {
		string ecuName = kvPair.first;
		string itemName = "ECU_DTC_Read", itemId = "01", ecuAddr = "0x1EFF", expectedData = "0x00";
		string itemInfo = string(blockId).append(",").append(itemName).append(",").append(itemId).append(",").append(ecuName).append(",").append(ecuAddr).append(",").append(expectedData);

		vector<JIDU_DTC_INFO_t> vecDtcInfo = kvPair.second;
		if (vecDtcInfo.size() > 0) {
			for (int i = 0; i < vecDtcInfo.size(); i++) {
				JIDU_DTC_INFO_t dtcItem = vecDtcInfo.at(i);

				//TODO 排除白名单
				if (m_mapEcuDtcWhiteList.find(ecuName) != m_mapEcuDtcWhiteList.end()) {
					vector<string> vecDtcWhiteList = m_mapEcuDtcWhiteList.at(ecuName);
					if (find(vecDtcWhiteList.begin(), vecDtcWhiteList.end(), dtcItem.strDTCNo) != vecDtcWhiteList.end()) {
						continue;
					}
				}

				string msg;
				msg.append(ecuName).append(": ").append(dtcItem.strDTCNo).append(",").append(dtcItem.strContent);
				jd_report_addNokItem((char*)itemInfo.c_str(), (uint8_t*)"\x19\x02\x09", 3, m_allEcuDtcAck[ecuName].GetBuffer(), m_allEcuDtcAck[ecuName].GetSize(), (char*)msg.c_str());
			}
		}
	}

	return 1;
}
