#include "Vision.h"
#include "Types.h"
#include <stdio.h>
#include <string.h>
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
*/
#include <fcntl.h>
#include <linux/videodev2.h>


#if defined(__GNUC__)
#include <unistd.h>
#endif
#if defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
#endif
#define V4L2_DEV "/dev/video0"
#define WIDTH  1920
#define HEIGHT 1080

// 初始化V4L2虚拟设备
int init_v4l2(int fd) {
	struct v4l2_format fmt = {0};
	fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
	fmt.fmt.pix.width = WIDTH;
	fmt.fmt.pix.height = HEIGHT;
	fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
	fmt.fmt.pix.field = V4L2_FIELD_NONE;

	if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
		perror("Failed to set V4L2 format");
		return -1;
	}
	return 0;
}
/*
   bool FrameProcCb(uint16_t* pFrameInShort, uint16_t iFrameID,
   int32_t iFrameWidth, int32_t iFrameHeight,
   uint32_t iTimeCount, void* pParam) {
   int v4l_fd = *(int*)pParam;

   if (pFrameInShort) {
// 直接写入V4L2设备（假设数据已经是YUYV格式）
if (write(v4l_fd, pFrameInShort, iFrameWidth * iFrameHeight * 2) < 0) {
perror("V4L2 write failed");
return false;
}
return true; // 继续接收帧
}
return false; // 停止接收
}
*/
bool FrameProcCb(uint16_t* pFrameInShort, uint16_t iFrameID,
		int32_t iFrameWidth, int32_t iFrameHeight,
		uint32_t iTimeCount, void* pParam) {
	int v4l_fd = *(int*)pParam;

	if (pFrameInShort) {
		// 1. 转换12bit raw到V4L2支持的格式（以YUYV为例）
		size_t yuyv_size = iFrameWidth * iFrameHeight * 2; // YUYV每像素2字节
		unsigned char* yuyv_buffer = malloc(yuyv_size);

		// 简易转换（实际需根据12bit raw的具体排列方式调整）
		for (int i = 0; i < iFrameWidth * iFrameHeight; i++) {
			// 取高8位作为Y分量（示例，实际转换算法需根据raw格式调整）
			yuyv_buffer[i*2] = (pFrameInShort[i] >> 4) & 0xFF; // Y
			yuyv_buffer[i*2+1] = i % 2 ? 0x80 : 0x00; // U/V交替（示例值）
		}

		// 2. 写入V4L2设备
		if (write(v4l_fd, yuyv_buffer, yuyv_size) < 0) {
			perror("V4L2 write failed");
			free(yuyv_buffer);
			return false;
		}

		free(yuyv_buffer);
		return true;
	}
	return false;
}
int main(void)
{
	int v4l_fd = open(V4L2_DEV, O_WRONLY);
	if (v4l_fd < 0 || init_v4l2(v4l_fd)) {
		fprintf(stderr, "Failed to initialize V4L2 device\n");
		return -1;
	}
	unsigned int iDeviceNum = 0;
	const char* pDeviceID = NULL;
	SDeviceInfo sDeviceInfo;
	SNICInfo sLocalInfo;
	int64_t iValue = 0;
	double fValue = 0;
	int64_t iEntryID = 0;
	int32_t iWidth = 0;
	int32_t iHeight = 0;
	int32_t iPixelBits = 0;
	int32_t iPixelIndex = 0;
	uint16_t iFrameID = 0;
	uint32_t iTimeCount = 0;
	unsigned int i = 0;
	int j = 0;
	int k = 0;
	int iAttributeNumber = -1;
	EAttrType eAttrType;
	EAttrAccessMode eAccessMode;
	char* pValue = NULL;
	int iEntryNumber = -1;
	char* pFrame = NULL;
	uint16_t* pFrameInShort = NULL;
	char* pMappedFrame = NULL;
	FILE* pFile = NULL;
	int32_t iPixelNum = 0;
	Stream pStream = NULL;
	int iKey = 0;
	//Init
	Init();
	//GetVersion
	fprintf(stderr, "SDK Version: %s\n", GetVersionText());
	//SetCallLogEnable
	if(SetCallLogEnable(true))
	{
		fprintf(stderr, "SetCallLogEnable=Enable\n");
	}else{
		fprintf(stderr, "SetCallLogEnable=Error:%s\n", GetLastErrorText());
		return 0;
	}
	//SearchforDevice
	iDeviceNum = SearchforDevice();
	if(iDeviceNum >= 0){
		fprintf(stderr, "\nTotal number of devices: %d\n\n", iDeviceNum);
	}else{
		//Uninit
		Uninit();
		fprintf(stderr, "Press Enter key to exit\n");
		scanf("%c", &iKey);
		return 0;
	}

	for(i = 0; i < iDeviceNum; i++){
		//Get device MAC
		pDeviceID = GetDeviceID(i);
		fprintf(stderr, "***************************************************************\n");
		fprintf(stderr, "  Device Information:\n");

		//GetDeviceInfo
		GetDeviceInfo(pDeviceID, &sDeviceInfo);
		fprintf(stderr, "    MAC=%s\n", sDeviceInfo.pMac);
		fprintf(stderr, "    IP=%s\n", sDeviceInfo.pIP);
		fprintf(stderr, "    Control Port=%d\n", sDeviceInfo.iCtrlPort);
		fprintf(stderr, "    Data Port=%d\n", sDeviceInfo.iDataPort);
		fprintf(stderr, "    Mask=%s\n", sDeviceInfo.pMask);
		fprintf(stderr, "    Gateway=%s\n", sDeviceInfo.pGateway);
		fprintf(stderr, "    VenderName=%s\n", sDeviceInfo.pVenderName);
		fprintf(stderr, "    ModelName=%s\n", sDeviceInfo.pModelName);
		fprintf(stderr, "    Version=%s\n", sDeviceInfo.pVersion);
		fprintf(stderr, "    SerialNumber=%s\n", sDeviceInfo.pSerialNumber);
		if(sDeviceInfo.bReachable){
			fprintf(stderr, "    Reachable\n");
		}else{
			fprintf(stderr, "    Unreachable\n");
		}

		//LocalInfo
		GetLocalNICInfo(pDeviceID,&sLocalInfo);
		fprintf(stderr, "  Localf Network Adapter Information:\n");
		fprintf(stderr, "    MAC=%s\n", sLocalInfo.pMac);
		fprintf(stderr, "    IP=%s\n", sLocalInfo.pIP);
		fprintf(stderr, "    Mask=%s\n", sLocalInfo.pMask);
		fprintf(stderr, "    InterfaceName=%s\n", sLocalInfo.pInterfaceName);
		fprintf(stderr, "    Broadcast=%s\n", sLocalInfo.pBroadcast);

		//Force IP
		if(ForceIP(pDeviceID, sDeviceInfo.pIP, sLocalInfo.pMask, sDeviceInfo.pGateway)){
			fprintf(stderr, "  ForceIP=OK\n");
		}else{
			fprintf(stderr, "  ForceIP=Error:%s\n", GetLastErrorText());
		}
		//OpenDevice
		if(OpenDevice(pDeviceID)){
			fprintf(stderr, "  OpenDevice=OK\n");
		}else{
			fprintf(stderr, "  OpenDevice=Error:%s\n", GetLastErrorText());
			continue;
		}
		//The following code is to demonstrate how to set or get related parameters of each attribute 
		//Get the number of attributes
		iAttributeNumber = GetNumberOfAttribute(pDeviceID);
		if(iAttributeNumber >= 0){
			fprintf(stderr, "  Attribute information, %d in total:\n", iAttributeNumber);
		}else{
			fprintf(stderr, "  GetNumberOfAttribute=Error:%s\n", GetLastErrorText());
			continue;
		}
		for(j = 0; j < iAttributeNumber; j++){
			//GetAttributeName, GetAttributeType, GetAttribute, SetAttribute
			const char* pAttrName = GetAttributeName(pDeviceID, j);
			if(pAttrName == NULL){
				fprintf(stderr, "    GetAttributeName=Error:%s\n", GetLastErrorText());
				continue;
			}
			fprintf(stderr, "++++Name=%s\n", pAttrName);
			if(!GetAttributeType(pDeviceID, pAttrName, &eAttrType)){
				fprintf(stderr, "    GetAttributeType=Error:%s\n", GetLastErrorText());
				continue;
			}
			if(eAttrType == INT_T){
				fprintf(stderr, "    Value Type=int\n");
				iValue = 0;
				if(GetAttrMaxInt(pDeviceID, pAttrName, &iValue)){
					fprintf(stderr, "    Maximum=%d\n", iValue);
				}else{
					fprintf(stderr, "    Maximum=Error:%s\n", GetLastErrorText());
				}
				if(GetAttrMinInt(pDeviceID, pAttrName, &iValue)){
					fprintf(stderr, "    Minimum=%d\n", iValue);
				}else{
					fprintf(stderr, "    Minimum=Error:%s\n", GetLastErrorText());
				}
				if(GetAttrIncInt(pDeviceID, pAttrName, &iValue)){
					fprintf(stderr, "    Increment=%d\n", iValue);
				}else{
					fprintf(stderr, "    Increment=Error:%s\n", GetLastErrorText());
				}
				if(GetAttrInt(pDeviceID, pAttrName, &iValue, DDR_T)){
					fprintf(stderr, "    Current Value=%d\n", iValue);
				}else{
					fprintf(stderr, "    Current Value=Error:%s\n", GetLastErrorText());
				}
				if(SetAttrInt(pDeviceID, pAttrName, iValue, DDR_T)){
					fprintf(stderr, "    New Value=%d\n", iValue);
				}else{
					fprintf(stderr, "    New Value=Error:%s\n", GetLastErrorText());
				}
			}else if(eAttrType == DOUBLE_T){
				fprintf(stderr, "    Value Type=double\n");
				fValue = 0;
				if(GetAttrMaxFloat(pDeviceID, pAttrName, &fValue)){
					fprintf(stderr, "    Maximum=%f\n", fValue);
				}else{
					fprintf(stderr, "    Maximum=Error:%s\n", GetLastErrorText());
				}
				if(GetAttrMinFloat(pDeviceID, pAttrName, &fValue)){
					fprintf(stderr, "    Minimum=%f\n", fValue);
				}else{
					fprintf(stderr, "    Minimum=Error:%s\n", GetLastErrorText());
				}
				if(GetAttrIncFloat(pDeviceID, pAttrName, &fValue)){
					fprintf(stderr, "    Increment=%f\n", fValue);
				}else{
					fprintf(stderr, "    Increment=Error:%s\n", GetLastErrorText());
				}
				if(GetAttrFloat(pDeviceID, pAttrName, &fValue, DDR_T)){
					fprintf(stderr, "    Current Value=%f\n", fValue);
				}else{
					fprintf(stderr, "    Current Value=Error:%s\n", GetLastErrorText());
				}
				if(SetAttrFloat(pDeviceID, pAttrName, fValue, DDR_T)){
					fprintf(stderr, "    New Value=%f\n", fValue);
				}else{
					fprintf(stderr, "    New Value=Error:%s\n", GetLastErrorText());
				}
			}else if(eAttrType == STRING_T){
				fprintf(stderr, "    Value Type=string\n");
				pValue = (char*)malloc(ATTR_TXT_SIZE_IN_BYTES);
				if(GetAttrString(pDeviceID, pAttrName, pValue, DDR_T)){
					fprintf(stderr, "    Current Value=%s\n", pValue);
				}else{
					fprintf(stderr, "    Current Value=Error:%s\n", GetLastErrorText());
				}
				free(pValue);
			}else if(eAttrType == ENUMERATION_T){
				fprintf(stderr, "    Value Type=enumeration\n");
				fprintf(stderr, "    Enumeration Entries:\n");
				//Get number of entries
				iEntryNumber = GetNumberOfEntry(pDeviceID, pAttrName);
				//Display entries
				for(k = 0; k < iEntryNumber; k++){
					int iEntryID = GetEntryID(pDeviceID, pAttrName, k);
					const char* pEntryName = GetEntryName(pDeviceID, pAttrName, k);
					if(iEntryID != -1 && pEntryName != NULL){
						fprintf(stderr, "      %d=%s\n", iEntryID, pEntryName);
					}else{
						fprintf(stderr, "	   Fail to get the entry with index=%d\n", k);
					}
				}

				iEntryID = 0;
				if(GetAttrInt(pDeviceID, pAttrName, &iEntryID, DDR_T)){
					fprintf(stderr, "    Current Value=%lld text=%s\n", iEntryID, GetEntryNameByID(pDeviceID, pAttrName, (unsigned int)iEntryID));
				}else{
					fprintf(stderr, "    Current Value=Error:%s\n", GetLastErrorText());
				}
				if(SetAttrInt(pDeviceID, pAttrName, iEntryID, DDR_T)){
					fprintf(stderr, "    New Value=%d\n", iEntryID);
				}else{
					fprintf(stderr, "    New Value=Error:%s\n", GetLastErrorText());
				}
			}else{
				fprintf(stderr, "    Value Type=Unsupported\n");
			}
			//GetAttributeAccessMode
			if(GetAttributeAccessMode(pDeviceID, pAttrName, &eAccessMode)){
				if(eAccessMode == RO_T){
					fprintf(stderr, "    Access Mode=RO\n");
				}else{
					fprintf(stderr, "    Access Mode=RW\n");
				}
			}else{
				fprintf(stderr, "    GetAttributeAccessMode=Error:%s\n", GetLastErrorText());
			}
		}
		//The following code is to demonstrate how to set some most commonly used attributes. Before setting, one getting is called to get the default value.
		//Set OffsetX
		if(GetAttrInt(pDeviceID, "OffsetX", &iValue, DDR_T)){
			if(SetAttrInt(pDeviceID, "OffsetX", iValue, DDR_T)){
				fprintf(stderr, "Set OffsetX to %d\n", iValue);
			}else{
				fprintf(stderr, "Warnning: Fail to Set OffsetX, %s\n", GetLastErrorText());
			}
		}else{
			fprintf(stderr, "Warnning: Fail to get OffsetX, %s\n", GetLastErrorText());
		}

		//Set ImageWidth
		if(GetAttrInt(pDeviceID, "ImageWidth", &iValue, DDR_T)){
			if(SetAttrInt(pDeviceID, "ImageWidth", iValue, DDR_T)){
				fprintf(stderr, "Set ImageWidth to %d\n", iValue);
			}else{
				fprintf(stderr, "Warnning: Fail to Set ImageWidth, %s\n", GetLastErrorText());
			}
		}else{
			fprintf(stderr, "Warnning: Fail to get ImageWidth, %s\n", GetLastErrorText());
		}
		//Create Stream
		pStream = CreateStream(pDeviceID);
		if(pStream != NULL){
			fprintf(stderr, "++++CreateStream=OK\n");
		}else{
			fprintf(stderr, "++++CreateStream=ERROR:%s\n", GetLastErrorText());
			continue;
		}
		SetStreamLogEnable(pStream, "StreamLog.txt");
		//There are two ways to process frames:
		//1:Use a callback function to process frame
		//Start Stream
		if(StartStream(pStream, true, FrameProcCb, &v4l_fd)){
			fprintf(stderr, "++++StartStream=OK\n");
		}else{
			fprintf(stderr, "++++StartStream=ERROR:%s\n", GetLastErrorText());
			continue;
		}
#if defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64)
		Sleep(100);
#else
		usleep(100000);
#endif
		while(1);
		//StopStream
		StopStream(pStream);

		fprintf(stderr, "FAIL to get one frame\n");
	}
	//Set frame processing
	if(SetFrameProc(pStream, ROTATE, 90)){
		fprintf(stderr, "++++SetFrameProc=OK\n");
	}else{
		fprintf(stderr, "++++SetFrameProc=ERROR:%s\n", GetLastErrorText());
	}
	//GetFrameInShort

	//Set frame processing
	//Uninit
	Uninit();
	return 0;
}
