// Copyrigth Xtooltech @ 2023- All rights reserved.(LXH)

#include "ReportRecordCollector.hpp"
#include <ctime>
#include "MyStringUtils.h"
#include <windows.h>
#include <sys/timeb.h>

namespace otx {
	namespace jidu {

		ReportRecordCollector::ReportRecordCollector() {
			this->mStationDataReportRecord = nullptr;
			this->mCollectEndTime = 0;
			this->mCollectStartTime = 0;
			// this->mStationResultReportRecord = nullptr;
			// this->mPrintResultReportRecord = nullptr;
		}
		ReportRecordCollector::~ReportRecordCollector() {
			//collectEnd();
			delete this->mStationDataReportRecord;
			this->mStationDataReportRecord = nullptr;
		}

		void ReportRecordCollector::collectBegin() {
			if (this->mStationDataReportRecord != nullptr) {
				delete this->mStationDataReportRecord;
				this->mStationDataReportRecord = nullptr;
			}

			this->mStationDataReportRecord = new StationDataReportRecord();
			//this->mStationResultReportRecord = new StationResultReportRecord();
			//this->mPrintResultReportRecord = new PrintResultReportRecord();
			this->mStationDataReportRecord->setExecutionTimeUtc(datetimeNowString());
			this->mCollectStartTime = now();

			this->mTPMSId.clear();
			this->mSshStatus.clear();
			this->mElectricity.clear();
		}

		void ReportRecordCollector::collectEnd() {

			if (this->mStationDataReportRecord) {
				this->mStationDataReportRecord->setTestTimeUtc(datetimeNowString());
				this->mCollectEndTime = now();
				// 生成其他2中报告。
				generate02();
				generate03();

			}
			// if (this->mStationResultReportRecord) {
			//   delete this->mStationResultReportRecord;
			//   this->mStationResultReportRecord = nullptr;
			// }
			// if (this->mPrintResultReportRecord) {
			//   delete this->mPrintResultReportRecord;
			//   this->mPrintResultReportRecord = nullptr;
			// }
		}

		void ReportRecordCollector::collectSerialNo(const std::string& v) {
			if (this->mStationDataReportRecord) {
				this->mStationDataReportRecord->setSerialNo(v);
			}
		}
		void ReportRecordCollector::collectMixNo(const std::string& v) {
			if (this->mStationDataReportRecord) {
				this->mStationDataReportRecord->setMixNo(v);
			}
		}
		void ReportRecordCollector::collectVehicleType(const std::string& v) {
			if (this->mStationDataReportRecord) {
				this->mStationDataReportRecord->setVehicleType(v);
			}
		}
		void ReportRecordCollector::collectVin(const std::string& v) {
			if (this->mStationDataReportRecord) {
				this->mStationDataReportRecord->setVin(v);
			}
		}
		void ReportRecordCollector::collectStation(const std::string& v) {
			if (this->mStationDataReportRecord) {
				this->mStationDataReportRecord->setStation(v);
			}
		}
		void ReportRecordCollector::collectReworkStation(const std::string& v) {
			if (this->mStationDataReportRecord) {
				this->mStationDataReportRecord->setReworkStation(v);
			}
		}
		void ReportRecordCollector::collectResultType(const std::string& v) {
			if (this->mStationDataReportRecord) {
				this->mStationDataReportRecord->setResultType(v);
			}
		}
		void ReportRecordCollector::collectCell(const std::string& v) {
			if (this->mStationDataReportRecord) {
				this->mStationDataReportRecord->setCell(v);
			}
		}
		void ReportRecordCollector::collectCarConfig(const std::string& v) {
			this->mCarConfig = v;
		}
		void ReportRecordCollector::collectTpmsId(const std::string& v) {
			this->mTPMSId = v;
		}

		void ReportRecordCollector::collectBncmFeedbackResult(const std::string& v)
		{
			this->mBncmFeedbackResult = v;
		}

		void ReportRecordCollector::collectSshStatus(const std::string& v)
		{
			this->mSshStatus = v;
		}

		void ReportRecordCollector::collectChargingIsOpen(const std::string& v)
		{
			this->mChargingIsOpen = v;
		}
		void ReportRecordCollector::collectGloveboxIsOpen(const std::string& v)
		{
			this->mGloveboxIsOpen = v;
		}

		void ReportRecordCollector::collectElectricity(const std::string& v)
		{
			this->mElectricity = v;
		}

		// block控制。

		void ReportRecordCollector::blockBegin(const std::string& categoryId, const std::string& categoryName, const std::string& blockId, const std::string& blockName) {
			// 开始一个大项中的测试模块，该模块里面可能会有许多小项目。
			std::lock_guard<std::mutex> lock(this->mBlockLock);
			// 1. 获取或新建category（即所谓的大项）
			StationDataCategory* category = this->mStationDataReportRecord->getTestCategoryById(categoryId);
			if (category == nullptr) {
				category = this->mStationDataReportRecord->newCategory();
				category->setTestStatisticId(categoryId);
				category->setTestStatisticName(categoryName);
			}
			// 2. 在大项中找出模块即block。
			StationDataBlock* block = category->getTestBlockById(blockId);
			if (block == nullptr) {
				block = category->newTestBlock();
				block->setTestBlockId(blockId);
				block->setTestBlockName(blockName);
			}
			block->setBeginTime(datetimeNowString());
		}
		void ReportRecordCollector::blockEnd(const std::string& categoryId, const std::string& blockId) {
			// 结束模块测试，生成一个报告，由于数据量的原因，每个模块测试结果单独上报。
			std::lock_guard<std::mutex> lock(this->mBlockLock);
			StationDataCategory* category = this->mStationDataReportRecord->getTestCategoryById(categoryId);
			if (category == nullptr) {
				throw - 1;
			}
			StationDataBlock* block = category->getTestBlockById(blockId);
			if (block == nullptr) {
				throw - 1;
			}

			block->setEndTime(datetimeNowString());
			block->updateState();

			// 生成一个新的数据对象。
			StationDataReportRecord reportRecord;
			reportRecord.setSerialNo(this->mStationDataReportRecord->getSerialNo());
			reportRecord.setMixNo(this->mStationDataReportRecord->getMixNo());
			reportRecord.setVehicleType(this->mStationDataReportRecord->getVehicleType());
			reportRecord.setVin(this->mStationDataReportRecord->getVin());
			reportRecord.setStation(this->mStationDataReportRecord->getStation());
			reportRecord.setReworkStation(this->mStationDataReportRecord->getReworkStation());
			reportRecord.setTestBlockTotalNum(1);
			reportRecord.setTestItemTotalNum(block->getTestItemList()->size());
			reportRecord.setResultType("01");
			reportRecord.setCell(this->mStationDataReportRecord->getCell());
			reportRecord.setExecutionTimeUtc(block->getBeginTime());
			reportRecord.setTestTimeUtc(block->getEndTime());
			reportRecord.setOverallResult(block->getState());

			// 仅仅是引用block，不会管理block。
			reportRecord.addStationDataBlock(block);
			// 发出事件。
			postBlockCompleted(&reportRecord);
		}

		// 小项和步骤控制。

		void ReportRecordCollector::collectItemStepOK(const std::string& blockId, const std::string& itemId, const std::string& itemName, const std::string& ecuName, const std::string& ecuAddr, const std::string& expectedData, unsigned char* request, int sizeOfRequest, unsigned char* response, int sizeOfResponse) {
			std::lock_guard<std::mutex> lock(this->mItemLock);

			StationDataBlock* block = getBlockById(blockId);
			if (block == nullptr) {
				throw "can not found block in report.";
			}

			// 2. find out block item.
			StationDataItem* testItem = block->getTestItemById(itemId);
			if (testItem == nullptr) {
				testItem = block->newTestItem();
				testItem->setTestItemName(itemName);
				testItem->setTestItemId(itemId);
			}
			// 3. add ecu(diag step) from block item.
			StationDataECU* ecu = testItem->newEcu();
			ecu->setEcuName(ecuName);
			ecu->setEcuAddress((ecuAddr.size() > 0) ? ecuAddr : "--");
			ecu->setServerId(toHex(request, 0, 1));
			ecu->setDid((sizeOfRequest == 1) ? "NA" : toHex(request, 1, sizeOfRequest - 1));
			ecu->setResultData("OK");
			ecu->setValue((sizeOfResponse == 0) ? "NA" : toHex(response, 0, sizeOfResponse));
			ecu->setExpectData(expectedData);
			int num = 0;
			std::vector<StationDataECU*>* vecTemp = testItem->getTestEcuItems();
			for (StationDataECU* tempEcu : *vecTemp) {
				if (tempEcu->getEcuName()._Equal(ecuName)) {
					num++;
				}
			}
			ecu->setNumber(num);
		}

		void ReportRecordCollector::collectItemStepNOK(const std::string& blockId, const std::string& itemId, const std::string& itemName, const std::string& ecuName, const std::string& ecuAddr, const std::string& expectedData, const std::string& failReason, unsigned char* request, int sizeOfRequest, unsigned char* response, int sizeOfResponse) {
			std::lock_guard<std::mutex> lock(this->mItemLock);

			StationDataBlock* block = getBlockById(blockId);
			if (block == nullptr) {
				throw "can not found block in report.";
			}

			// 2. find out block item.
			StationDataItem* testItem = block->getTestItemById(itemId);
			if (testItem == nullptr) {
				testItem = block->newTestItem();
				testItem->setTestItemName(itemName);
				testItem->setTestItemId(itemId);
			}
			testItem->setTestItemResult("NOK");//有一个步骤失败整个项就失败。
			this->mStationDataReportRecord->setOverallResult("NOK");// 整个报告就存在失败。
			// 向Block添加失败原因。

			std::vector<std::string> vecString;
			MyStringUtils::split(vecString, failReason, "\n");
			for (int i = 0; i < vecString.size(); i++) {
				block->addFailReason(vecString.at(i));
			}

			// 03类型报告需要这个信息。
			block->getCategory()->setTestStatisticResult("NOK");
			block->setTestBlockResult("NOK");


			string sid = toHex(request, 0, 1);
			if (!sid._Equal("00"))
			{
				// 3. add ecu(diag step) from block item.
				StationDataECU* ecu = testItem->newEcu();
				ecu->setEcuName(ecuName);
				ecu->setEcuAddress((ecuAddr.size() > 0) ? ecuAddr : "--");
				ecu->setServerId(sid);
				ecu->setDid((sizeOfRequest == 1) ? "NA" : toHex(request, 1, sizeOfRequest - 1));
				ecu->setResultData("NOK");
				ecu->setValue((sizeOfResponse == 0) ? "NA" : toHex(response, 0, sizeOfResponse));

				ecu->setExpectData(expectedData);
				int num = 0;
				std::vector<StationDataECU*>* vecTemp = testItem->getTestEcuItems();
				for (StationDataECU* tempEcu : *vecTemp) {
					if (tempEcu->getEcuName()._Equal(ecuName)) {
						num++;
					}
				}
				ecu->setNumber(num);
			}
		}

		void ReportRecordCollector::addEventHandler(ReportCollectorEventHandler* handler) {
			this->mEventHandlers.push_back(handler);
		}
		void ReportRecordCollector::removeEventHandler(ReportCollectorEventHandler* handler) {
			auto it = this->mEventHandlers.begin();
			int found = 0;
			for (; it != this->mEventHandlers.end(); it++) {
				if (*it == handler) {
					found = 1;
					break;
				}
			}
			if (found) {
				this->mEventHandlers.erase(it);
			}
		}

		std::string ReportRecordCollector::datetimeNowString() {
			time_t t = time(0);
			char ch[64] = { 0 };

			SYSTEMTIME wtm;
			GetLocalTime(&wtm);
			sprintf(ch, "%04d-%02d-%02d %02d:%02d:%02d.%03d", wtm.wYear, wtm.wMonth, wtm.wDay, wtm.wHour, wtm.wMinute, wtm.wSecond, wtm.wMilliseconds);
			return ch;
		}

		void ReportRecordCollector::postBlockCompleted(StationDataReportRecord* newBlock) {
			int size = this->mEventHandlers.size();
			for (int i = 0; i < 1; i++) {
				ReportCollectorEventHandler* item = this->mEventHandlers[i];
				item->onBlockCompleted(newBlock);
			}
		}
		void ReportRecordCollector::postStationResultCompleted(StationResultReportRecord* newResult) {
			int size = this->mEventHandlers.size();
			for (int i = 0; i < 1; i++) {
				ReportCollectorEventHandler* item = this->mEventHandlers[i];
				item->onStationResultCompleted(newResult);
			}
		}
		void ReportRecordCollector::postPrintResultCompleted(PrintResultReportRecord* newResult) {
			int size = this->mEventHandlers.size();
			for (int i = 0; i < 1; i++) {
				ReportCollectorEventHandler* item = this->mEventHandlers[i];
				item->onPrintResultCompleted(newResult);
			}
		}

		StationDataBlock* ReportRecordCollector::getBlockById(const std::string& blockId) {
			std::vector<otx::jidu::StationDataCategory*>* categories = this->mStationDataReportRecord->getTestCategories();
			int size = categories->size();
			StationDataBlock* result = nullptr;
			for (int i = 0; i < size; i++) {
				StationDataCategory* item = categories->at(i);
				result = item->getTestBlockById(blockId);
				if (result != nullptr) {
					break;
				}
			}
			return result;
		}

		std::string ReportRecordCollector::toHex(unsigned char* data, int index, int size) {
			std::string buff;
			for (int j = index; j < size + index; j++) {
				int high = data[j] / 16, low = data[j] % 16;
				buff += (high < 10) ? ('0' + high) : ('A' + high - 10);
				buff += (low < 10) ? ('0' + low) : ('A' + low - 10);

				if (j != size + index - 1)
					buff += ' ';
			}
			return buff;
		}

		void ReportRecordCollector::generate02() {
			int countOfBlocks = 0;
			int countOfItems = 0;
			int countOfFailedBlocks = 0;
			int countOfFailedItems = 0;
			int isFailed = 0;
			getInfoOfBlocksAndItems(&countOfBlocks, &countOfItems, &isFailed, &countOfFailedBlocks, &countOfFailedItems);

			StationResultReportRecord reportRecord;
			reportRecord.setSerialNo(this->mStationDataReportRecord->getSerialNo());
			reportRecord.setMixNo(this->mStationDataReportRecord->getMixNo());
			reportRecord.setVehicleType(this->mStationDataReportRecord->getVehicleType());
			reportRecord.setVin(this->mStationDataReportRecord->getVin());
			reportRecord.setStation(this->mStationDataReportRecord->getStation());
			reportRecord.setReworkStation(this->mStationDataReportRecord->getReworkStation());
			reportRecord.setTestBlockTotalNum(countOfBlocks);
			reportRecord.setTestItemTotalNum(countOfItems);
			reportRecord.setResultType("02");
			reportRecord.setCell(this->mStationDataReportRecord->getCell());
			reportRecord.setExecutionTimeUtc(this->mStationDataReportRecord->getExecutionTimeUtc());
			reportRecord.setTestTimeUtc(this->mStationDataReportRecord->getTestTimeUtc());
			if (isFailed) {
				reportRecord.setOverallResult("NOK");
			}
			else {
				reportRecord.setOverallResult("OK");
			}

			// 发出事件。
			postStationResultCompleted(&reportRecord);
		}
		void ReportRecordCollector::generate03() {
			int countOfBlocks = 0;
			int countOfItems = 0;
			int countOfFailedBlocks = 0;
			int countOfFailedItems = 0;
			int isFailed = 0;
			getInfoOfBlocksAndItems(&countOfBlocks, &countOfItems, &isFailed, &countOfFailedBlocks, &countOfFailedItems);

			PrintResultReportRecord reportRecord;
			reportRecord.setSerialNo(this->mStationDataReportRecord->getSerialNo());
			reportRecord.setVehicleType(this->mStationDataReportRecord->getVehicleType());
			reportRecord.setVin(this->mStationDataReportRecord->getVin());
			reportRecord.setStation(this->mStationDataReportRecord->getStation());
			reportRecord.setResultType("03");
			reportRecord.setReworkStation(this->mStationDataReportRecord->getReworkStation());
			reportRecord.setCell(this->mStationDataReportRecord->getCell());
			reportRecord.setCarConfig(this->mCarConfig);
			reportRecord.setTpmsId(this->mTPMSId);
			reportRecord.setSshStatus(this->mSshStatus);
			reportRecord.setChargingIsOpen(this->mChargingIsOpen);
			reportRecord.setGloveboxIsOpen(this->mGloveboxIsOpen);
			reportRecord.setElectricity(this->mElectricity);

			std::vector<otx::jidu::StationDataCategory*>* categories = this->mStationDataReportRecord->getTestCategories();
			int sizeOfStatistic = categories->size();
			int countOfFailedStatistic = 0;
			for (int i = 0; i < sizeOfStatistic; i++) {
				StationDataCategory* category = categories->at(i);
				int testStatisticId = MyStringUtils::toLong(category->getTestStatisticId(), 16);
				std::vector<otx::jidu::StationDataBlock*>* blocks = category->getTestBlockList();
				int sizeOfBlocks = blocks->size();
				if (sizeOfBlocks == 0) {
					continue;
				}
				if (category->getTestStatisticResult() != "OK") {
						countOfFailedStatistic++;
				}
				PrintResultModule* module = reportRecord.newStatisticItem();
				module->setTestStatisticId(category->getTestStatisticId());
				module->setTestStatisticName(category->getTestStatisticName());
				module->setTestStatisticResult(category->getTestStatisticResult());

				for (int j = 0; j < sizeOfBlocks; j++) {
					StationDataBlock* block = blocks->at(j);
					PrintResultItem* item = module->newTestBlockItem();
					item->setTestBlockId(block->getTestBlockId());
					item->setTestBlockName(block->getTestBlockName());
					item->setTestBlockResult(block->getTestBlockResult());

					std::vector<std::string>* failReasons = block->getFailReason();
					for (int k = 0; k < failReasons->size(); k++) {
						item->addFailReason(failReasons->at(k));
					}
				}
			}
			reportRecord.setTestStatisticTotalNum(sizeOfStatistic);
			reportRecord.setTestStatisticFailNum(countOfFailedStatistic);
			reportRecord.setTestBlockTotalNum(countOfBlocks);
			reportRecord.setTestBlockFailNum(countOfFailedBlocks);
			reportRecord.setStationTime(formatCollectDuration());
			reportRecord.setExecutionTimeUtc(this->mStationDataReportRecord->getExecutionTimeUtc());
			reportRecord.setTestTimeUtc(this->mStationDataReportRecord->getTestTimeUtc());

			postPrintResultCompleted(&reportRecord);
		}
		void ReportRecordCollector::getInfoOfBlocksAndItems(int* countOfBlocks, int* countOfItems, int* isFailed, int* countOfFailedBlocks, int* countOfFailedItems) {
			*countOfBlocks = 0;
			*countOfItems = 0;
			std::vector<otx::jidu::StationDataCategory*>* categories = this->mStationDataReportRecord->getTestCategories();
			int sizeOfCategories = categories->size();
			for (int i = 0; i < sizeOfCategories; i++) {
				StationDataCategory* category = categories->at(i);
				std::vector<otx::jidu::StationDataBlock*>* blocks = category->getTestBlockList();
				std::string tempStatisticResult = "OK";

				int sizeOfBlocks = blocks->size();
				*countOfBlocks += sizeOfBlocks;
				for (int j = 0; j < sizeOfBlocks; j++) {
					StationDataBlock* block = blocks->at(j);
					int blockId = std::stoi(block->getTestBlockId(), NULL, 16);
					if (blockId == 0x0601 || blockId == 0x0901 || blockId == 0x0082) {
						*countOfBlocks -= 1;
					}
					else {
						if (block->getTestBlockResult() != "OK") {
							*isFailed = 1;
							*countOfFailedBlocks += 1;
							tempStatisticResult = "NOK";
						}

						std::vector<otx::jidu::StationDataItem*>* items = block->getTestItemList();
						int sizeOfItems = items->size();
						*countOfItems += sizeOfItems;
						for (int k = 0; k < sizeOfItems; k++) {
							StationDataItem* item = items->at(k);
							if (item->getTestItemResult() != "OK") {
								*countOfFailedItems += 1;
							}
						}
					}
				}
				category->setTestStatisticResult(tempStatisticResult);
			}
		}

		uint64_t ReportRecordCollector::now() {
			timeb t;
			ftime(&t);
			return t.time * 1000 + t.millitm;
		}
		std::string ReportRecordCollector::formatCollectDuration() {
			uint64_t ts = this->mCollectEndTime - mCollectStartTime;
			uint32_t msec = ts % 1000;
			int sec = ((ts - msec) / 1000) % 60;
			int min = (ts - msec - 1000 * sec) / 1000 / 60 % 60;
			int hour = (ts - msec - 1000 * sec - 60 * 1000 * min) / 1000 / 60 / 60 % 24;

			std::string result;
			//result.append(std::to_string(hour));
			//result.append("h");
			result.append(std::to_string(min));
			result.append("min ");
			result.append(std::to_string(sec));
			result.append("s");
			return result;
		}

	} // namespace jidu
} // namespace otx