﻿#include "VbfFileDownloader.hpp"
#include "asam/d/MCDAccessKeys.hpp"
#include "asam/d/MCDFlashJob.hpp"
#include "asam/d/MCDRequest.hpp"
#include "asam/mcd/MCDDbProject.hpp"
#include "asam/mcd/MCDLogicalLink.hpp"
#include "otx/diagcom/asam/MCDProjectProvider.hpp"
#include <codecvt>
#include <locale>
#include "ECUFlasherImpl.hpp"
#include <iostream>
#include <stdio.h>
#include "otx/api/OTXAPI.hpp"
#include "MCDCmdTrans.hpp"
#include "tracer.h"
#include "MyStringUtils.h"

namespace otx {
	namespace flash {

		VbfFileDownloader::VbfFileDownloader() {
			this->mFlasher = nullptr;
			this->mErrorCode = 0;
		}

		void VbfFileDownloader::onConfigurationRecordLoaded(asam::d::MCDConfigurationRecord& configurationRecord) {
			throw - 1;// 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onDefinableDynIdListChanged(asam::mcd::MCDValues& dynIdList, asam::mcd::MCDLogicalLink& link) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onFirstEvent() {
			//printf("onFirstEvent\r\n"); 
		}

		void VbfFileDownloader::onInterfaceError(asam::d::MCDInterface& carinterface, asam::mcd::MCDError& error) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}
		void VbfFileDownloader::onInterfacesChanged() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onInterfaceStatusChanged(asam::d::MCDInterface& carinterface, asam::d::MCDInterfaceStatus status) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLastEvent() {
			//printf("onLastEvent\r\n"); 
		}

		void VbfFileDownloader::onLinkActivityStateIdle(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDLogicalLinkState linkstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkActivityStateRunning(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDLogicalLinkState linkstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkActivityStateSuspended(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDLogicalLinkState linkstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkError(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDError& error) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkOneVariantIdentified(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDLogicalLinkState linkstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkOneVariantSelected(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDLogicalLinkState linkstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkQueueCleared(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDLogicalLinkState linkstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkStateCommunication(asam::mcd::MCDLogicalLink& link) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkStateCreated(asam::mcd::MCDLogicalLink& link) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkStateOffline(asam::mcd::MCDLogicalLink& link) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkStateOnline(asam::mcd::MCDLogicalLink& link) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkVariantIdentified(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDLogicalLinkState linkstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkVariantSelected(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDLogicalLinkState linkstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onLinkVariantSet(asam::mcd::MCDLogicalLink& link, asam::mcd::MCDLogicalLinkState linkstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onMonitoringFramesReady(asam::d::MCDMonitoringLink& monLink) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onPrimitiveBufferOverflow(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onPrimitiveCanceledDuringExecution(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onPrimitiveCanceledFromQueue(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onPrimitiveError(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link, asam::mcd::MCDError& error) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onPrimitiveHasIntermediateResult(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link, asam::d::MCDResultState& resultstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onPrimitiveHasResult(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link, asam::d::MCDResultState& resultstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onPrimitiveJobInfo(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link, asam::A_ASCIISTRING& info) {
			//printf("onPrimitiveJobInfo\r\n");
		}

		void VbfFileDownloader::onPrimitiveProgressInfo(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link, asam::A_UINT8 progress) {
			printf("onPrimitiveProgressInfo %d\r\n", progress);
		}

		void VbfFileDownloader::onPrimitiveRepetitionStopped(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onPrimitiveTerminated(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link, asam::d::MCDResultState& resultstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onPrimitiveIdle(asam::d::MCDDiagComPrimitive& primitive, asam::mcd::MCDLogicalLink& link, asam::d::MCDResultState& resultstate) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onStaticInterfaceError(asam::mcd::MCDError& error) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemClampStateChanged(asam::A_ASCIISTRING& clamp, asam::d::MCDClampState clampState) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemConfigurationClosed() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemConfigurationOpened() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemError(asam::mcd::MCDError& error) {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemLocked() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。 
		}

		void VbfFileDownloader::onSystemLogicallyConnected() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemLogicallyDisconnected() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemProjectDeselected() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemProjectSelected() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemUnlocked() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemVehicleInfoDeselected() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		void VbfFileDownloader::onSystemVehicleInfoSelected() {
			throw - 1; // 不可能发生的事件我们抛出异常，提醒开发者注意。
		}

		int VbfFileDownloader::getErrorCode() { return this->mErrorCode; }
		std::string VbfFileDownloader::getMessage() { return this->mMessage; }

		//TODO 0:Data;  1:SBL;  2:App   3 : PBL
		bool VbfFileDownloader::downloadFile(ECU* ecu, File* ecuFile) {
			this->mEcuFile = ecuFile;
			this->mECU = ecu;
			string filepath = ecuFile->getPath();
			size_t fileSize = ecuFile->getSize();

			cmdTrans = new MCDCmdTrans(ecu);
			if (cmdTrans) {

				//TODO 刷下数据需要先擦除
				vector<std::string> vecErase = mEcuFile->parser.getHeader().getListOfErase();
				for (std::string eraseInfo : vecErase) {
					vector<string> vecCgRes;
					MyStringUtils::split(vecCgRes, eraseInfo, ",");

					if (vecCgRes.size() != 2) {
						delete cmdTrans;
						return false;
					}

					printf("erase {%s,%s}\r\n", vecCgRes.at(0).c_str(), vecCgRes.at(1).c_str());

					uint32_t uMem = MyStringUtils::toLong(vecCgRes.at(0), 16);
					uint32_t uSize = MyStringUtils::toLong(vecCgRes.at(1), 16);

					if (!cmdTrans->EraseMemory(uMem, uSize))
					{
						Tracer::error("EraseMemory", "failed");
						delete cmdTrans;
						return false;
					}
				}

				if (!transferFile())
				{
					Tracer::error("transferFile", "failed");
					delete cmdTrans;
					return false;
				}

				if (ecuFile->parser.getHeader().getVersion()._Equal("2.6"))
				{
					if (0 == ecu->getSignatureMode())
					{
						if (ecu->isProd) {
							if (!cmdTrans->VerifyAuthenticity(ecuFile->parser.getHeader().getSWSignature(), false))
							{
								if (!cmdTrans->VerifyAuthenticity(ecuFile->parser.getHeader().getSWSignatureDev(), false))
								{
									Tracer::error("VerifyAuthenticity", "failed");
									delete cmdTrans;
									return  false;
								}
							}
						}
						else {
							if (!cmdTrans->VerifyAuthenticity(ecuFile->parser.getHeader().getSWSignatureDev(), false))
							{
								if (!cmdTrans->VerifyAuthenticity(ecuFile->parser.getHeader().getSWSignature(), false))
								{
									Tracer::error("VerifyAuthenticity", "failed");
									delete cmdTrans;
									return  false;
								}
							}
						}
					}
					else if (1 == ecu->getSignatureMode()) {
						if (!cmdTrans->VerifyAuthenticity(ecuFile->parser.getHeader().getSWSignatureDev(), false))
						{
							Tracer::error("VerifyAuthenticity", "failed");
							delete cmdTrans;
							return  false;
						}
					}
					else if (2 == ecu->getSignatureMode()) {
						if (!cmdTrans->VerifyAuthenticity(ecuFile->parser.getHeader().getSWSignature(), false))
						{
							Tracer::error("VerifyAuthenticity", "failed");
							delete cmdTrans;
							return  false;
						}
					}
				}
				else {
					Tracer::warn("no signature", ecuFile->parser.getHeader().getVersion());
				}

				//TODO 刷写完SBL后需要激活
				if (1 == ecuFile->getFileType()) {
					std::string activeSBLAddress = mEcuFile->parser.getHeader().getCall();
					if (!cmdTrans->ActivateSbl(activeSBLAddress))
					{
						Tracer::error("RequestTransferExit", "failed");
						delete cmdTrans;
						return  false;
					}
				}

				delete cmdTrans;
				return true;
			}

			return false;
		}
		void VbfFileDownloader::delay(int ms)
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(ms));
		}

		bool VbfFileDownloader::transferFile()
		{
			bool bRet = true;
			vector<VBFBlock> vecBlock = mEcuFile->parser.GetVBFBlocks();

			std::string strFormatIdentifier = mEcuFile->parser.getHeader().getDataFormatIndetifier();
			uint32_t formatIdentifer = 0x00;
			if (!strFormatIdentifier.empty())
			{
				formatIdentifer = MyStringUtils::toLong(strFormatIdentifier, 16);
			}

			for (int i = 0; i < vecBlock.size(); i++) {
				VBFBlock mBlock = vecBlock.at(i);

				//TODO requestDownload
				if (!cmdTrans->RequestDownload(formatIdentifer, mBlock.GetStartAddress(), mBlock.GetLength())) {
					return false;
				}

				uint32_t nFrameSize = cmdTrans->getFrameSize();

				//TODO transferData
				uint32_t nDataLength = mBlock.GetLength();
				uint32_t nLastFrameSize = nDataLength % (nFrameSize);
				uint32_t nTotalFrames = nDataLength / (nFrameSize);

				vector<unsigned char> oneBlockData = mBlock.GetBlockData();
				unsigned char* buffer = oneBlockData.data();

				uint8_t nCTR = 1; int nDataLen = 0;
				size_t oldProgress = 0;
				for (unsigned int frameIndex = 0; frameIndex < nTotalFrames; frameIndex++)
				{
					CBinary binSend = CBinary("\x36", 1) + nCTR;
					binSend.Append(buffer + nDataLen, nFrameSize);
					CBinary binRecv = cmdTrans->SendFrame_Cmd36(binSend.GetBuffer(), binSend.GetSize());
					if (binRecv.IsEmpty())
					{
						std::cout << "WriteOneBlock:SendFrame failed" << endl;
						bRet = false;
						return bRet;
					}

					nCTR++;
					nDataLen += nFrameSize;
					if (nCTR > 0xFF)
					{
						nCTR = 0;
					}

					//TODO 更新进度
					ECUFlasherImpl* flasher = dynamic_cast<ECUFlasherImpl*>(this->mFlasher);
					size_t mDsize = this->mECU->getDownloadedSize() + nFrameSize;
					this->mECU->setDownloadedSize(mDsize);
					this->mEcuFile->increaseDownloadedSize(nFrameSize);

					if (this->mECU->getDownloadedSize() == this->mECU->getTotalSize()) {
						otx::flash::ECUFlasherFileDownloadProgressEventArg pearg(this->mFlasher, this->mECU, this->mEcuFile, 100);
						flasher->postFileDownloadProgress(pearg);
					}
					else {
#if false
						size_t newProgress = 100 * (double)this->mECU->getDownloadedSize() / (double)this->mECU->getTotalSize();
						if (oldProgress != newProgress) {
							otx::flash::ECUFlasherFileDownloadProgressEventArg pearg(this->mFlasher, this->mECU, this->mEcuFile, newProgress);
							flasher->postFileDownloadProgress(pearg);

							oldProgress = newProgress;
						}
#else
						size_t newProgress = 100 * (double)this->mECU->getDownloadedSize() / (double)this->mECU->getTotalSize();
						otx::flash::ECUFlasherFileDownloadProgressEventArg pearg(this->mFlasher, this->mECU, this->mEcuFile, newProgress);
						flasher->postFileDownloadProgress(pearg);
#endif
					}
				}
				if (nLastFrameSize > 0)
				{
					CBinary binSend = CBinary("\x36", 1) + nCTR;
					binSend.Append(buffer + nDataLen, nLastFrameSize);
					CBinary binRecv = cmdTrans->SendFrame_Cmd36(binSend.GetBuffer(), binSend.GetSize());
					if (binRecv.IsEmpty())
					{
						std::cout << "error" << "WriteOneBlock:SendFrame failed" << endl;
						bRet = false;
						return bRet;
					}

					//TODO 更新进度
					ECUFlasherImpl* flasher = dynamic_cast<ECUFlasherImpl*>(this->mFlasher);
					size_t mDownloadSize = this->mECU->getDownloadedSize() + nLastFrameSize;
					this->mECU->setDownloadedSize(mDownloadSize);
					this->mEcuFile->increaseDownloadedSize(nLastFrameSize);

					if (this->mECU->getDownloadedSize() == this->mECU->getTotalSize()) {
						otx::flash::ECUFlasherFileDownloadProgressEventArg pearg(this->mFlasher, this->mECU, this->mEcuFile, 100);
						flasher->postFileDownloadProgress(pearg);
					}
				}

				//TODO transferExit
				cmdTrans->RequestTransferExit();
			}

			bRet = true;
			return bRet;
		}

		ECUFlasher* VbfFileDownloader::getFlasher() {
			return this->mFlasher;
		}
		void VbfFileDownloader::setFlasher(ECUFlasher* flasher) {
			this->mFlasher = flasher;
		}

		void VbfFileDownloader::printResponseParameter(asam::mcd::MCDResponseParameter* param, int level) {
			asam::mcd::MCDResponseParameters* subParams = param->getParameters();
			asam::A_ASCIISTRING pname = param->getShortName();

			printLevel(level);
			if (subParams->getCount() == 0) {
				asam::mcd::MCDValue pvalue = param->getValue();
				printValue(pname, pvalue);
			}
			else {
				// printf("+ %s(%u): \r\n", pname.c_str(), pvalue.getUint32());
				printf("+ %s: \r\n", pname.c_str());
				for (int i = 0; i < subParams->getCount(); i++) {
					printResponseParameter(subParams->getItemByIndex(i), level + 1);
				}
			}
		}
		void VbfFileDownloader::printValue(asam::A_ASCIISTRING& pname, asam::mcd::MCDValue& pvalue) {
			std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff, std::little_endian>, char16_t> conv;
			if (pvalue.isValid()) {
				asam::mcd::MCDDataType dt = pvalue.getDataType();
				if (dt == asam::mcd::MCDDataType::eA_UINT32) {
					printf("%s=%u\r\n", pname.c_str(), pvalue.getUint32());
				}
				else if (dt == asam::mcd::MCDDataType::eA_UINT16) {
					printf("%s=%u\r\n", pname.c_str(), pvalue.getUint16());
				}
				else if (dt == asam::mcd::MCDDataType::eA_UINT8) {
					printf("%s=%u\r\n", pname.c_str(), pvalue.getUint8());
				}
				else if (dt == asam::mcd::MCDDataType::eA_INT32) {
					printf("%s=%d\r\n", pname.c_str(), pvalue.getInt32());
				}
				else if (dt == asam::mcd::MCDDataType::eA_INT16) {
					printf("%s=%d\r\n", pname.c_str(), pvalue.getInt16());
				}
				else if (dt == asam::mcd::MCDDataType::eA_INT8) {
					printf("%s=%d\r\n", pname.c_str(), pvalue.getInt8());
				}
				else if (dt == asam::mcd::MCDDataType::eA_ASCIISTRING) {
					printf("%s=%s\r\n", pname.c_str(), pvalue.getAsciistring().c_str());
				}
				else if (dt == asam::mcd::MCDDataType::eA_UNICODE2STRING) {
					printf("%s=%s\r\n", pname.c_str(), conv.to_bytes(pvalue.getValueAsString()).c_str());
				}
				else {
					printf("%s other data type,ignored.\r\n", pname.c_str());
				}
			}
			else {
				printf("%s invalid.\r\n", pname.c_str());
			}
		}
		void VbfFileDownloader::printLevel(int level) {
			int width = level * 2;
			for (int i = 0; i < width; i++) {
				printf(" ");
			}
		}

	} // namespace flash
} // namespace otx