#include "hikcamera.h"
#include <algorithm> 
#include <iostream>
#include <thread>


MV_CC_DEVICE_INFO_LIST HIKCamera::stDeviceList;
HIKCamera* HIKCamera::HikController = nullptr;
std::vector<void*> HIKCamera::CameraHandle;

void __stdcall ImageCallBackEx1(unsigned char* pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
	HIKCamera* HIKCTRL = (HIKCamera*)pUser;
	if (pFrameInfo)
	{
		memcpy(HIKCTRL->p_pImage_buf, pData, pFrameInfo->nFrameLen);
		cv::Mat image = cv::Mat(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3, HIKCTRL->p_pImage_buf);
		cv::Mat image_clone = image.clone();
		if (image_clone.data)
		{
			HIKCTRL->p_image_queue->put(image_clone);
		}
	}
}

void __stdcall LossCallBack(unsigned int pData, void* pUser)
{
	
}
void __stdcall FallingGpioEvent(MV_EVENT_OUT_INFO* pEventInfo, void* pUser)
{
	
	
}
void __stdcall RisingGpioEvent(MV_EVENT_OUT_INFO* pEventInfo, void* pUser)
{
	
}

HIKCamera::HIKCamera(int DeviceNum):CamSelect(DeviceNum)
{
	p_pImage_buf = (unsigned char*)malloc(5000 * 5000 * 3);
}

HIKCamera::~HIKCamera()
{
	delete p_pImage_buf;
}

HIKCamera* HIKCamera::InitHIKController(int DeviceNum)
{
	if (HIKCamera::HikController == nullptr)
	{
		HIKCamera::HikController = new HIKCamera(DeviceNum);
	}
	return HIKCamera::HikController;
}

int HIKCamera::Enumerate()
{
	int status = 0;
	int hik_cnt = 0;
	memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));

	status = MV_CC_EnumDevices(MV_GIGE_DEVICE| MV_USB_DEVICE, &stDeviceList);
	if (MV_OK == status)
	{
		for (int i = 0; i < stDeviceList.nDeviceNum; i++)
		{
			MV_CC_DEVICE_INFO* pDeviceInfo = HIKCamera::stDeviceList.pDeviceInfo[i];
			std::string manufacture;
			char chManufacturer[32];
			if (pDeviceInfo->nTLayerType == MV_GIGE_DEVICE)
			{
				MV_GIGE_DEVICE_INFO* TempPtr = &(pDeviceInfo->SpecialInfo.stGigEInfo);
				memcpy(chManufacturer, TempPtr->chManufacturerName, 32);
				manufacture = chManufacturer;
			}
			else if (pDeviceInfo->nTLayerType == MV_USB_DEVICE)
			{
				MV_USB3_DEVICE_INFO* TempPtr = &(pDeviceInfo->SpecialInfo.stUsb3VInfo);
				memcpy(chManufacturer, TempPtr->chManufacturerName, 32);
				manufacture = chManufacturer;
			}
			else
			{
				continue;
			}

			std::cout << "Camera:" << i << ",vendor = " << manufacture << std::endl;
			transform(manufacture.begin(), manufacture.end(), manufacture.begin(), ::toupper);
			if ((!manufacture.compare(0, 3, "HIK")) || (!manufacture.compare(0, 3, "U3V")))
			{
				void* camhandle;
				MV_CC_CreateHandle(&camhandle, HIKCamera::stDeviceList.pDeviceInfo[i]);
				hik_cnt++;
				HIKCamera::CameraHandle.push_back(camhandle);
			}
		}
		return hik_cnt;
	}
	else
	{
		return 0;
	}
}

int HIKCamera::OpenCamera()
{
	int nRet = MV_OK;
	int nnRet = MV_OK;
	void* handle = HIKCamera::CameraHandle[CamSelect];
	nRet = MV_CC_OpenDevice(handle);
	// nRet = MV_CC_SetEnumValue(handle, "LineSelector", 1);	//0:Line0 1:Line1 2:Line2 
	// if (nRet) { std::cout << "can not set LineSelector" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValue(handle, "LineMode", 8);	//0:Input 1:Output 2:Trigger 8:Strobe
	// if (nRet) { std::cout << "can not set LineMode" << std::endl; nnRet = nRet; }

	//0:ExposureStartActive 5:SoftTriggerActive  6:HardTriggerActive
	// nRet = MV_CC_SetEnumValue(handle, "LineSource", 5);
	// if (nRet) { std::cout << "can not set LineSource" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetBoolValue(handle, "StrobeEnable", TRUE);
	// if (nRet) { std::cout << "can not set StrobeEnable" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetIntValueEx(handle, "StrobeLineDuration", 2000);
	// if (nRet) { std::cout << "can not set StrobeLineDuration" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValue(handle, "LineSelector", 0);
	// if (nRet) { std::cout << "can not set LineSelector" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetIntValueEx(handle, "LineDebouncerTime", 2000);
	// if (nRet) { std::cout << "can not set LineDebouncerTime" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValue(handle, "LineSelector", 2);
	// if (nRet) { std::cout << "can not set LineSelector" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetIntValueEx(handle, "LineDebouncerTime", 2000);
	// if (nRet) { std::cout << "can not set LineDebouncerTime" << std::endl; nnRet = nRet; }

	nRet = MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF);
	if (nRet) { std::cout << "can not set TriggerMode" << std::endl; nnRet = nRet; }

	nRet = MV_CC_SetEnumValue(handle, "TriggerSource", MV_TRIGGER_SOURCE_SOFTWARE);
	if (nRet) { std::cout << "can not set TriggerSource" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValue(handle, "TriggerActivation", 1);//1表示下降沿
	// if (nRet) { std::cout << "can not set TriggerActivation" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValue(handle, "BinningHorizontal", 2);//设置binning
	// if (nRet) { std::cout << "can not set BinningHorizontal" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValue(handle, "BinningVertical", 2);//设置binning
	// if (nRet) { std::cout << "can not set BinningVertical" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValueByString(handle, "EventSelector", "Line0FallingEdge");
	// if (nRet) { std::cout << "can not set EventSelector" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValueByString(handle, "EventNotification", "On");
	// if (nRet) { std::cout << "can not set EventNotification" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValueByString(handle, "EventSelector", "Line0RisingEdge");
	// if (nRet) { std::cout << "can not set EventSelector" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_SetEnumValueByString(handle, "EventNotification", "On");
	// if (nRet) { std::cout << "can not set EventNotification" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_RegisterExceptionCallBack(handle, LossCallBack, this);
	// if (nRet) { std::cout << "can not register loss callback" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_RegisterEventCallBackEx(handle, "Line0FallingEdge", FallingGpioEvent, this);
	// if (nRet) { std::cout << "can not register GPIO callback" << std::endl; nnRet = nRet; }

	// nRet = MV_CC_RegisterEventCallBackEx(handle, "Line0RisingEdge", RisingGpioEvent, this);
	// if (nRet) { std::cout << "can not register GPIO callback" << std::endl; nnRet = nRet; }

	if (HIKCamera::stDeviceList.pDeviceInfo[CamSelect]->nTLayerType == MV_GIGE_DEVICE)
	{
		int nPacketSize = MV_CC_GetOptimalPacketSize(handle);
		if (nPacketSize > 0)
		{
			nRet = MV_CC_SetIntValue(handle, "GevSCPSPacketSize", nPacketSize);
			if (nRet != MV_OK)
			{
				//printf("Warning: Set Packet Size fail nRet [0x%x]!", nRet);
			}
		}
		else
		{
			//printf("Warning: Get Packet Size fail nRet [0x%x]!", nPacketSize);
		}
	}
	MV_CC_RegisterImageCallBackForRGB(handle, ImageCallBackEx1, this);
	nRet = MV_CC_StartGrabbing(handle);
	return nnRet;
}

int HIKCamera::CloseCamera()
{
	int nRet = MV_OK;
	void* handle = HIKCamera::CameraHandle[CamSelect];
	nRet = MV_CC_StopGrabbing(handle);
	nRet = MV_CC_CloseDevice(handle);
	return nRet;
}

void HIKCamera::SetTriggerMode(MV_CAM_TRIGGER_MODE mode)
{
	int nRet = MV_OK;
	int nnRet = MV_OK;
	void* handle = HIKCamera::CameraHandle[CamSelect];
	
	nRet = MV_CC_SetEnumValue(handle, "TriggerMode", mode);
	if (nRet) { std::cout << "can not set TriggerMode" << std::endl; nnRet = nRet; }
}

cv::Mat HIKCamera::SoftWareTrigger()
{
	int nRet = MV_OK;
	int nnRet = MV_OK;
	void* handle = HIKCamera::CameraHandle[CamSelect];

	image = cv::Mat();
	nRet = MV_CC_SetCommandValue(handle, "TriggerSoftware");
    if (nRet) { std::cout << "can not send TriggerSoftware" << std::endl; nnRet = nRet; }
	int cnt = 10;
	while(cnt)
	{
		if(image.data)break;
		Sleep(200);
		cnt--;
	}
	return image.clone();
}
