#include "ParallelECUFlasher.hpp"
#include "TimeUtils.h"
#include "MCDCmdTrans.hpp"
#include "MCDCmdBoardcast.hpp"
#include "otx/api/OTXAPI.hpp"
#include "tracer.h"
#include "MyStringUtils.h"

namespace otx {
	namespace flash {

		ParallelECUFlasher::ParallelECUFlasher(TaskPool* pool) {
			this->mPool = pool;
			this->mCompletedECUs.store(0, std::memory_order::memory_order_relaxed);
		}

		bool ParallelECUFlasher::download() {
			int size = this->mECUs.size();
			this->mStartTime = getTimeStamp();

			for (int i = 0; i < size; i++) {
				ECU* ecu = this->mECUs[i];
				downloadECU(ecu);
			}

			while (1) {
				int num = this->mCompletedECUs.load(std::memory_order::memory_order_relaxed);
				if (num == size) {
					break;
				}
			}
			return true;
		}

		bool ParallelECUFlasher::ecuReady(ECU* ecu)
		{
			if (ecu->getSecurityAccess())
			{
				MCDCmdTrans* cmdTrans = new MCDCmdTrans(ecu);
				if (cmdTrans) {
					if (!cmdTrans->EnterSystem())
					{
						delete(cmdTrans);
						return false;
					}

					if (ecu->getSignatureMode() != 0) {
						ecu->isProd = (ecu->getSignatureMode() == 0x02);
					}
					else {
						cmdTrans->getEcuSignatureMode(ecu->isProd);
					}


					string pinCode = ecu->getFiles()->at(0).getPinCode();
					if (0 == ecu->getSecurityAccessMode()) {
						if (!cmdTrans->SecurityAccess_ECOS(pinCode))
						{
							if (!cmdTrans->SecurityAccess("FFFFFFFFFF", 2))
							{
								delete(cmdTrans);
								return false;
							}
						}
					}
					else {
						if (!cmdTrans->SecurityAccess("FFFFFFFFFF", 2))
						{
							if (!cmdTrans->SecurityAccess_ECOS(pinCode))
							{
								delete(cmdTrans);
								return false;
							}
						}
					}

					delete(cmdTrans);
					return true;
				}
				return false;
			}

			return true;
		}

		bool ParallelECUFlasher::FinishProgram(ECU* ecu)
		{
			if (ecu->getInstallVerify())
			{
				MCDCmdTrans* cmdTrans = new MCDCmdTrans(ecu);
				bool flag = cmdTrans->FinishProgram();
				delete(cmdTrans);
				return flag;
			}
			return true;
		}

		// 在并行环境下执行ECU刷写。
		void ParallelECUFlasher::downloadECU(ECU* ecu) {
			// 在任务完成或取消后被清除。
			FlashTaskArg* arg = new FlashTaskArg();
			arg->mEcu = ecu;
			arg->mFlasher = this;

			// 在任务完成或取消后被清除。
			Task* task = new Task(taskWork, taskCancel, arg);
			this->mPool->addTask(task);// 内部会开始调度执行。
		}


		void ParallelECUFlasher::delay(int ms)
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(ms));
		}
		bool ParallelECUFlasher::reset(ECU* ecu)
		{
			bool rFlag = true;
			if (string::npos == ecu->getName().find("BGM")) {
				MCDCmdTrans* cmd = new MCDCmdTrans(ecu);
				if (cmd)
				{
					cmd->sendCommand(CBinary("\x11\x81", 2));
					delete(cmd);
					delay(180000);
				}
			}
			else {
				MCDCmdBoardcast* boardcast = MCDCmdBoardcast::getInstance();
				if (boardcast == NULL) {
					return false;
				}

				MCDCmdTrans* cmd = new MCDCmdTrans(ecu);
				if (cmd)
				{
					cmd->sendCommand(CBinary("\x11\x81", 2));
					delete(boardcast);
					delete(cmd);
					delay(10000);
					rFlag = otx_reconnect_vehicle_ex(50, 5000);
					if (rFlag) {
						boardcast = MCDCmdBoardcast::getInstance();
						delay(170000);
					}
					return rFlag;
				}
			}
			return rFlag;
		}

		bool ParallelECUFlasher::securityAccess(int iLevel)
		{
			CBinary bin10("\x10\x03", 2);
			CBinary bin2701("\x27\x01", 2);
			CBinary bin2702("\x27\x02\x00\x00\x00", 5);

			CBinary binConKey = CBinary("\x00", 1);

			CBinary binSeed;
			CBinary binMask;
			CBinary binKEY;

			BYTE nSession = 0;
			BYTE nSessionCur = 0;
			BYTE uSeedLen = 3;

			switch (iLevel)
			{
			case 1: //诊断解锁
				nSession = 3;
				bin10.SetAt(1, 0x03);
				bin2701.SetAt(1, 0x03);
				bin2702.SetAt(1, 0x04);
				break;
			case 2: //编程解锁 
				nSession = 2;
				bin10.SetAt(1, 0x02);
				bin2701.SetAt(1, 0x01);
				bin2702.SetAt(1, 0x02);
				break;
			case 3: //诊断写VIN
				nSession = 3;
				bin10.SetAt(1, 0x03);
				bin2701.SetAt(1, 0x05);
				bin2702.SetAt(1, 0x06);
				break;
			case 4://写公钥和安全常数
				nSession = 2;
				bin10.SetAt(1, 0x02);
				bin2701.SetAt(1, 0x01);
				bin2702.SetAt(1, 0x02);
				break;
			case 7: //
			{
				nSession = 3;
				bin10.SetAt(1, 0x03);
				bin2701.SetAt(1, 0x07);
				bin2702.SetAt(1, 0x08);
				uSeedLen = 16;
			}

			break;
			case 11: //
				nSession = 3;
				bin10.SetAt(1, 0x03);
				bin2701.SetAt(1, 0x11);
				bin2702.SetAt(1, 0x12);
				break;
			default:
				return false;
				break;
			}


			MCDCmdTrans* cmdTrans = new MCDCmdTrans("EV_ECM3", "LL_ECM3_DoIP");

			CBinary rfData = cmdTrans->sendCommand(bin10);
			if ((rfData.IsEmpty()) || rfData[0] == 0x7F)
			{
				delete(cmdTrans);
				return false;
			}

			rfData = cmdTrans->sendCommand(bin2701);
			if ((rfData.IsEmpty()) || rfData[0] == 0x7F || rfData.GetSize() < (2 + uSeedLen))
			{
				delete(cmdTrans);
				Tracer::error("response", "security err");
				return false;
			}

			binSeed.Append(rfData.m_pData + 2, uSeedLen);
			bool bCheck = false;

			for (int i = 0; i < uSeedLen; i++)
			{
				if (binSeed[i] != 0)
				{
					bCheck = TRUE;
				}
			}

			if (bCheck == FALSE)//SEED全为0，不需要计算秘钥
			{
				delete(cmdTrans);
				return TRUE;
			}

			//TODO 读取Ecu掩码
			if (!cmdTrans->Get_SecurityMask(0x1630, binMask)) {
				delete(cmdTrans);
				return false;
			}

			binKEY = cmdTrans->SeedToKey(binSeed, binMask);
			bin2702[2] = binKEY[0];
			bin2702[3] = binKEY[1];
			bin2702[4] = binKEY[2];

			cmdTrans->delay(1000);

			rfData = cmdTrans->sendCommand(bin2702);
			if ((rfData.IsEmpty()) || rfData[0] == 0x7F)
			{
				delete(cmdTrans);
				return false;
			}

			delete(cmdTrans);
			return true;
		}

		bool ParallelECUFlasher::exitFota(int iLevel)
		{

			CBinary bin10("\x10\x03", 2);
			CBinary bin2701("\x27\x01", 2);
			CBinary bin2702("\x27\x02\x00\x00\x00", 5);
			CBinary binConKey = CBinary("\x00", 1);

			CBinary binSeed;
			CBinary binMask;
			CBinary binKEY;

			BYTE nSession = 0;
			BYTE nSessionCur = 0;
			BYTE uSeedLen = 3;

			switch (iLevel)
			{
			case 1: //诊断解锁
				nSession = 3;
				bin10.SetAt(1, 0x03);
				bin2701.SetAt(1, 0x03);
				bin2702.SetAt(1, 0x04);
				break;
			case 2: //编程解锁 
				nSession = 2;
				bin10.SetAt(1, 0x02);
				bin2701.SetAt(1, 0x01);
				bin2702.SetAt(1, 0x02);
				break;
			case 3: //诊断写VIN
				nSession = 3;
				bin10.SetAt(1, 0x03);
				bin2701.SetAt(1, 0x05);
				bin2702.SetAt(1, 0x06);
				break;
			case 4://写公钥和安全常数
				nSession = 2;
				bin10.SetAt(1, 0x02);
				bin2701.SetAt(1, 0x01);
				bin2702.SetAt(1, 0x02);
				break;
			case 7: //
			{
				nSession = 3;
				bin10.SetAt(1, 0x03);
				bin2701.SetAt(1, 0x07);
				bin2702.SetAt(1, 0x08);
				uSeedLen = 16;
			}

			break;
			case 11: //
				nSession = 3;
				bin10.SetAt(1, 0x03);
				bin2701.SetAt(1, 0x11);
				bin2702.SetAt(1, 0x12);
				break;
			default:
				return false;
				break;
			}


			MCDCmdTrans* cmdTrans = new MCDCmdTrans("EV_ECM3", "LL_ECM3_DoIP");

			CBinary rfData = cmdTrans->sendCommand(bin10);
			if ((rfData.IsEmpty()) || rfData[0] == 0x7F)
			{
				delete(cmdTrans);
				return false;
			}

			rfData = cmdTrans->sendCommand(bin2701);
			if ((rfData.IsEmpty()) || rfData[0] == 0x7F)
			{
				delete(cmdTrans);
				return false;
			}
			else if (rfData.GetSize() < (2 + uSeedLen))
			{
				delete(cmdTrans);
				return false;
			}

			binSeed.Append(rfData.m_pData + 2, uSeedLen);
			bool bCheck = false;

			for (int i = 0; i < uSeedLen; i++)
			{
				if (binSeed[i] != 0)
				{
					bCheck = TRUE;
				}
			}

			if (bCheck == FALSE)//SEED全为0，不需要计算秘钥
			{
				delete(cmdTrans);
				return TRUE;
			}

			//TODO 读取Ecu掩码
			if (!cmdTrans->Get_SecurityMask(0x1630, binMask)) {
				delete(cmdTrans);
				return false;
			}

			binKEY = cmdTrans->SeedToKey(binSeed, binMask);
			bin2702[2] = binKEY[0];
			bin2702[3] = binKEY[1];
			bin2702[4] = binKEY[2];

			cmdTrans->delay(1000);

			rfData = cmdTrans->sendCommand(bin2702);
			if ((rfData.IsEmpty()) || rfData[0] == 0x7F)
			{
				delete(cmdTrans);
				return false;
			}

			//TODO 退出Fota模式
			CBinary cmd3101428900("\x31\x01\x42\x89\x00", 5);
			rfData = cmdTrans->sendCommand(cmd3101428900);
			if ((rfData.IsEmpty()) || rfData[0] == 0x7F)
			{
				delete(cmdTrans);
				return false;
			}

			delete(cmdTrans);
			return true;
		}

		void ParallelECUFlasher::taskWork(Task* task) {
			// 在线程池的线程中执行该任务。
			FlashTaskArg* arg = (FlashTaskArg*)task->getUserState();
			otx::flash::ParallelECUFlasher* _this = arg->mFlasher;
			ECU* ecu = arg->mEcu;
			//========================================================
			otx::flash::ECUFlasherECUEventArg eventArg(_this, ecu);
			_this->postECUDownloadStart(eventArg);

			std::vector<otx::flash::File>* files = ecu->getFiles();
			int size = files->size();

			bool isSucceed = false;
			if (_this->ecuReady(ecu)) {
				for (int i = 0; i < size; i++) {
					File ecuFile = files->at(i);
					isSucceed = _this->downloadFile(ecu, &ecuFile);
					if (!isSucceed) {//TODO Ecu一个文件下载失败，不进行后续文件下载
						break;
					}
				}
			}

			if (isSucceed)
			{
				//TODO 如果只刷写一个PBL文件，无需重复安装
				if (files->size() == 1 && 3 == files->at(0).getFileType()) {
					ECUFlasherECUEventArg arg = ECUFlasherECUEventArg(_this, ecu, true);
					_this->postECUDownloadEnd(arg);
				}
				else {
					ECUFlasherECUEventArg arg = ECUFlasherECUEventArg(_this, ecu, _this->FinishProgram(ecu));

					string ecuName = ecu->getName();
					if (string::npos != ecuName.find("TCAM") || string::npos != ecuName.find("BGM") || string::npos != ecuName.find("CDC") || string::npos != ecuName.find("ACU")) {

						otx::flash::ECUFlasherECUEventArg eventArg(_this, ecu);
						_this->postECUResetStart(eventArg);
						_this->reset(ecu);
						_this->postECUResetEnd(eventArg);
					}

					_this->postECUDownloadEnd(arg);
				}
			}
			else {

				string ecuName = ecu->getName();
				if (string::npos != ecuName.find("TCAM") || string::npos != ecuName.find("BGM") || string::npos != ecuName.find("CDC") || string::npos != ecuName.find("ACU")) {

					otx::flash::ECUFlasherECUEventArg eventArg(_this, ecu);
					_this->postECUResetStart(eventArg);
					_this->reset(ecu);
					_this->postECUResetEnd(eventArg);
				}

				ECUFlasherECUEventArg arg = ECUFlasherECUEventArg(_this, ecu, false);
				_this->postECUDownloadEnd(arg);
			}

			_this->mCompletedECUs.fetch_add(1, std::memory_order::memory_order_relaxed);
			//========================================================
			delete arg;  // 任务参数应该被销毁。
			delete task; // 任务应该被销毁，否则内存泄露。
		}
		void ParallelECUFlasher::taskCancel(Task* task) {
			// 非线程池的调用者线程中执行该任务。

			FlashTaskArg* arg = (FlashTaskArg*)task->getUserState();
			delete arg; // 任务参数应该被销毁。
			delete task; // 任务应该被销毁，否则内存泄露。
		}

	} // namespace flash
} // namespace otx