﻿#include "ECUFlasherManager.hpp"
#include "SingleECUFlasher.hpp"
#include "otx/diagcom/asam/MCDProjectProvider.hpp"
#include "asam/mcd/MCDDbProject.hpp"
#include "MyStringUtils.h"
#include "MCDCmdBoardcast.hpp"
#include "MCDCmdTrans.hpp"
#include "otx/api/OTXAPI.hpp"
#include <tracer.h>

namespace otx {
	namespace flash {

		ECUFlasherManager::ECUFlasherManager() : ECUFlasherImpl() {
			this->mStarted = false;
			this->mPool = new TaskPool(5);

#ifdef _DEBUG
			otx_load_database();
			otx_connect_phy_mvci();
			otx_connect_vehicle();
#endif // _DEBUG
		}
		ECUFlasherManager::ECUFlasherManager(int parallelSize) : ECUFlasherImpl() {
			this->mStarted = false;
			if (parallelSize <= 0) {
				parallelSize = 5;
			}
			else if (parallelSize > 7) {
				parallelSize = 7;
			}
			this->mPool = new TaskPool(parallelSize);
		}

		ECUFlasherManager::~ECUFlasherManager() {
			this->mPool->shutdown();
			delete this->mPool;

			auto it = this->mECUs.begin();
			for (; it != this->mECUs.end();) {
				delete (it->second);
				it->second = nullptr;
				this->mECUs.erase(it++);
			}
			this->mECUs.clear();
			this->mSequenceECUNames.clear();

			auto it1 = this->mPriorityECUs.begin();
			for (; it1 != this->mPriorityECUs.end();) {
				delete (it1->second);
				it1->second = nullptr;
				this->mPriorityECUs.erase(it1++);
			}
			this->mPriorityECUs.clear();

#ifdef _DEBUG
			//otx_disconnect_vehicle();
			//otx_disconnect_mvci();
			//otx_unload_database();
#endif // _DEBUG
		}

		void ECUFlasherManager::delay(int ms)
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(ms));
		}

		bool ECUFlasherManager::download() {
			if (!this->mStarted) {
				this->mStarted = true;
			}

			MCDCmdBoardcast* boardcast = MCDCmdBoardcast::getInstance();
			if (boardcast == NULL)
			{
				std::vector<ECU*>* ecus = nullptr;
				for (auto it = this->mPriorityECUs.begin(); it != this->mPriorityECUs.end(); it++) {
					ecus = it->second;

					ParallelECUFlasher flasher(this->mPool);
					flasher.addEventHandler(this);
					int size = ecus->size();
					for (int i = 0; i < size; i++) {
						ECU* ecu = ecus->at(i);

						ECUFlasherECUEventArg arg = ECUFlasherECUEventArg(NULL, ecu, false);
						postECUDownloadEnd(arg);
					}
				}
				return false;
			}
			// 由于map在插入元素时就已经按照key从小到大顺序排好序，所以直接使用。
			std::vector<ECU*>* ecus = nullptr;
			for (auto it = this->mPriorityECUs.begin(); it != this->mPriorityECUs.end(); it++) {
				ecus = it->second;

				ParallelECUFlasher flasher(this->mPool);
				flasher.addEventHandler(this);
				int size = ecus->size();
				for (int i = 0; i < size; i++) {
					ECU* ecu = ecus->at(i);
					flasher.addECU(ecu);
				}

				if (size > 0)
				{
					flasher.download();
					for (int i = 0; i < size; i++) {
						ECU* ecu = ecus->at(i);
						ecu->setDownloadedSize(ecu->getTotalSize());
					}
				}
			}
			boardcast = MCDCmdBoardcast::getInstance();
			delete(boardcast);

			return true;
		}
		unsigned long long ECUFlasherManager::getRemainTime(ECU* ecu) {
			if (!this->mStarted) {
				throw - 1; // 启动前不能访问。
			}
			return ECUFlasherImpl::getRemainTime(ecu);
		}
		void ECUFlasherManager::addECU(const std::string& name, const std::string& id, const std::string& dbLogicalLinkName, int priority, int securityAccessMode, int signatureMode, int securityAccess, int installVerify) {
			if (this->mStarted) {
				throw - 1; // 启动后就不能再修改了。
			}
			if (this->mECUs.find(name) != this->mECUs.end()) {
				throw - 1;
			}

			ECU* ecu = new ECU();// 在析构函数中删除。
			ecu->setId(id);
			ecu->setName(name);
			ecu->setDbLogicalLinkName(dbLogicalLinkName);
			ecu->setPriority(priority);
			ecu->setSecurityAccess(securityAccess);
			ecu->setInstallVerify(installVerify);
			ecu->setSecurityAccessMode(securityAccessMode);
			ecu->setSignatureMode(signatureMode);

			this->mECUs.insert(std::pair<std::string, ECU*>(name, ecu));
			this->mSequenceECUNames.push_back(name);

			std::vector<ECU*>* ecus = nullptr;
			auto it = this->mPriorityECUs.find(priority);
			if (it != this->mPriorityECUs.end()) {
				ecus = it->second;
			}
			else {
				ecus = new std::vector<ECU*>();// 在析构函数中删除。
				this->mPriorityECUs.insert(std::pair<int, std::vector<ECU*>*>(priority, ecus));
			}
			ecus->push_back(ecu);
		}
		void ECUFlasherManager::addECUFile(const std::string& ecuName, int fileType, const std::string& pinCode, const std::string& keyInfo, VBFParserSmall* parser)
		{
			if (this->mStarted) {
				throw - 1;// 启动后就不能再修改了。
			}
			ECU* ecu = nullptr;
			auto it = this->mECUs.find(ecuName);
			if (it != this->mECUs.end()) {
				ecu = it->second;
			}
			if (ecu) {
				otx::flash::File file;
				size_t fileSize = parser->GetVBFDataSize();
				file.setFileType(fileType);
				file.setSize(fileSize);
				file.setPinCode(pinCode);
				file.setPath(parser->m_strVBFileName);
				file.parser = VBFParserSmall();
				file.parser.m_oHeader = parser->getHeader();

				for (VBFBlock block : parser->GetVBFBlocks()) {
					VBFBlock temp(block.GetStartAddress(), block.GetLength(), block.GetCheckSum(), block.GetBlockData());
					file.parser.m_vecVBFBlocks.push_back(temp);
				}
				for (unsigned char dt : parser->GetVBFData()) {
					file.parser.m_vecFileBuffer.push_back(dt);
				}
				file.parser.m_strKeyInfo = parser->m_strKeyInfo;

				file.setKeyInfo(keyInfo);
				ecu->addFile(file);
				this->mTotalSize += fileSize;
			}
			else {
				throw - 1;// add ecu first.
			}

		}
		void ECUFlasherManager::addECUFile(const std::string& ecuName, const std::string& filePath, int fileType, size_t fileSize, const std::string& pinCode, const std::string& keyInfo)
		{
			printf("addECUFile: %s\r\n", filePath.c_str());
			if (this->mStarted) {
				throw - 1;// 启动后就不能再修改了。
			}
			ECU* ecu = nullptr;
			auto it = this->mECUs.find(ecuName);
			if (it != this->mECUs.end()) {
				ecu = it->second;
			}
			if (ecu) {
				otx::flash::File file;
				file.setPath(filePath);
				file.setFileType(fileType);
				file.setSize(fileSize);
				file.setPinCode(pinCode);
				file.setKeyInfo(keyInfo);
				ecu->addFile(file);
			}
			else {
				throw - 1;// add ecu first.
			}
		}

		bool ECUFlasherManager::getProjectPath(std::string& filepath)
		{
			asam::mcd::MCDProject* mcdProject = otx::diagcom::MCDProjectProvider::getSelectedProject();
			if (mcdProject) {
				asam::mcd::MCDDbProject* dbProject = mcdProject->getDbProject();
				filepath = dbProject->getShortName();
				return true;
			}
			return false;
		}

		bool ECUFlasherManager::getcmd(std::string ecuName, std::vector<std::string>& vecCmd)
		{
			if (this->mECUs.find(ecuName) != mECUs.end()) {
				ECU* ecu = mECUs.at(ecuName);
				vecCmd.assign(ecu->getCmd()->begin(), ecu->getCmd()->end());
				ecu->clearCmd();
				return true;
			}
			return false;
		}

		bool ECUFlasherManager::downloadFile(ECU* ecu, File* ecuFile) {
			throw - 1;// 永远不要调用这个方法。
		}

		// 事件处理。

		void ECUFlasherManager::onECUDownloadStart(ECUFlasherECUEventArg& arg) {
			this->postECUDownloadStart(arg);
		}
		void ECUFlasherManager::onECUDownloadEnd(ECUFlasherECUEventArg& arg) {
			this->postECUDownloadEnd(arg);
		}
		void ECUFlasherManager::onECUResetStart(ECUFlasherECUEventArg& arg)
		{
			this->postECUResetEnd(arg);
		}
		void ECUFlasherManager::onECUResetEnd(ECUFlasherECUEventArg& arg)
		{
			this->postECUResetEnd(arg);
		}
		void ECUFlasherManager::onFileDownloadStart(ECUFlasherFileEventArg& arg) {
			this->postFileDownloadStart(arg);
		}
		void ECUFlasherManager::onFileDownloadEnd(ECUFlasherFileDownloadEndEventArg& arg) {
			// 有些下载引擎下载进度可能只会报告到99%,100%时直接报告完成，这时我们需要修改已下载的字节。
			File* ecuFile = arg.getFile();
			if (ecuFile->getDownloadedSize() < ecuFile->getSize()) {
				// 说明最后1%没有触发onFileDownloadProgress事件。
				{ // zone of lock.
					std::lock_guard<std::mutex> lock(this->mFieldLock);
					ECU* ecu = arg.getEcu();
					if (ecu != nullptr) {
						size_t size = ecu->getDownloadedSize() + (ecuFile->getSize() - ecuFile->getDownloadedSize());
						ecu->setDownloadedSize(size);
					}

				}
			}
			this->postFileDownloadEnd(arg);
		}

		void ECUFlasherManager::onFileDownloadProgress(ECUFlasherFileDownloadProgressEventArg& arg) {
			this->postFileDownloadProgress(arg);
		}

	} // namespace flash
} // namespace otx