#include "MCDCmdBoardcast.hpp"

#include "asam/d/MCDAccessKeys.hpp"
#include "asam/d/MCDRequest.hpp"
#include "asam/mcd/MCDDbProject.hpp"
#include "asam/mcd/MCDLogicalLink.hpp"
#include "asam/mcd/MCDResponseParameter.hpp"
#include "asam/mcd/MCDValue.hpp"

#include "otx/diagcom/asam/MCDApiWrapper.hpp"
#include "otx/diagcom/asam/MCDProjectProvider.hpp"
#include <codecvt>
#include <locale>
#include <iostream>

#include "otx/api/OTXAPI.hpp"
#include "ECUFlasherImpl.hpp"
#include "tracer.h"

namespace otx {
	namespace flash {

		MCDCmdBoardcast* MCDCmdBoardcast::instance = NULL;
		asam::mcd::MCDLogicalLink* MCDCmdBoardcast::logicalLink = NULL;

		MCDCmdBoardcast::MCDCmdBoardcast()
		{
			mcdSystem = otx::diagcom::MCDProjectProvider::getMCDSystem();
			mcdProject = otx::diagcom::MCDProjectProvider::getSelectedProject();
			mInterface = otx::diagcom::MCDProjectProvider::getConnectedInterface();

			if (mInterface == NULL) {
				Tracer::error("MCDCmdBoardcast", "mInterface =null");
				return;
			}

			asam::mcd::MCDDatatypeShortName shortName = "LL_FG_UDSOnDoIP";
			logicalLink = mcdProject->createLogicalLinkByNameAndInterface(shortName, *mInterface);
			asam::mcd::MCDLogicalLinkState state = logicalLink->getState();
			if (state == asam::mcd::MCDLogicalLinkState::eCREATED) {

				try {
					if (logicalLink == NULL) return;
					logicalLink->open();
					if (logicalLink == NULL) return;
					logicalLink->gotoOnline();
				}
				catch (...){
					Tracer::debug(__FUNCTION__, to_string(__LINE__));
					return;
				}

				// functional group logical link.
				asam::d::MCDDiagComPrimitive* cop = logicalLink->createDiagComPrimitiveByType(asam::mcd::MCDObjectType::eMCDSTARTCOMMUNICATION);
				asam::mcd::MCDResult* result = cop->executeSync();
				// important.
				delete (result); // must do this.
			}
		}

		MCDCmdBoardcast::~MCDCmdBoardcast()
		{
			if (logicalLink) {
				mcdProject->removeLogicalLink(*logicalLink);
				logicalLink = nullptr;
			}
			instance = NULL;
		}

		MCDCmdBoardcast* MCDCmdBoardcast::getInstance()
		{
			if (instance == NULL) {
				instance = new MCDCmdBoardcast();
			}
			if (logicalLink) {
				asam::mcd::MCDLogicalLinkState state = logicalLink->getState();
				if (state == asam::mcd::MCDLogicalLinkState::eCREATED) {
					try {
						logicalLink->open();
						logicalLink->gotoOnline();
					}
					catch (...) {
						Tracer::debug(__FUNCTION__, to_string(__LINE__));
						delete(instance);
						instance = NULL;
					}
				}
			}

			return instance;
		}

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

		CBinary MCDCmdBoardcast::sendCommand(CBinary binSend)
		{
			CBinary rfData;
			if (logicalLink) {
				try {
					asam::mcd::MCDLogicalLinkState state = logicalLink->getState();
					if (state == asam::mcd::MCDLogicalLinkState::eCREATED) {
						logicalLink->open();
						logicalLink->gotoOnline();
					}
				}
				catch (...) {
					Tracer::debug(__FUNCTION__, to_string(__LINE__));
					return rfData;
				}
			}
			else {
				return rfData;
			}

			asam::d::MCDDiagComPrimitive* hexCop = logicalLink->createDiagComPrimitiveByType(asam::mcd::MCDObjectType::eMCDHEXSERVICE);
			asam::d::MCDRequest* request = hexCop->getRequest();
			asam::mcd::MCDValue pdu(asam::mcd::MCDDataType::eA_BYTEFIELD);
			otx::O_BYTEFIELD dSend;
			for (int i = 0; i < binSend.GetSize(); i++) {
				dSend.push_back(binSend.GetAt(i));
			}

			//Tracer::hex("boardcast_req", binSend.m_pData, MIN(16, binSend.GetSize()));

			pdu.setBytefield(dSend);
			request->enterPDU(pdu);

			asam::mcd::MCDResult* result = hexCop->executeSync();
			if (result->getResultState()->getExecutionState() != asam::d::MCDExecutionState::eALL_FAILED) {
				asam::mcd::MCDResponse* response = result->getResponses()->getItemByIndex(0);
				otx::O_BYTEFIELD dRecv;
				dRecv = response->getResponseMessage().getBytefield();
				rfData.Init(dRecv.data(), dRecv.size());

				//Tracer::hex("boardcast_res", rfData.m_pData, MIN(16, rfData.GetSize()));
			}

			logicalLink->removeDiagComPrimitive(*hexCop);
			delete (result);
			return rfData;
		}

		void MCDCmdBoardcast::EnterProgramMode()
		{
			CBinary bin31010206("\x31\x01\x02\x06", 4);
			sendCommand(bin31010206);
			delay(1000);

			//CBinary binReboot("\x10\x82", 2);
			//sendCommand(binReboot);
			//delay(30000);
		}
	}
}