/*
 *  Initialization routine for the 3rd party display driver
 */
static enum OMAP_ERROR create_display_devices(void)
{
	PFN_CMD_PROC pfnCmdProcList[OMAP_DC_CMD_COUNT];
	IMG_UINT32 aui32SyncCountList[OMAP_DC_CMD_COUNT][2];
	int i;
	unsigned int bytes_to_alloc;

	DEBUG_PRINTK("Initializing 3rd party display driver");

	/* Ask for the number of displays available */
	omap_display_init();
	/* TODO: allow more displays */
	display_devices_count = 1; // omap_display_count();

	DEBUG_PRINTK("Found %i displays", display_devices_count);

	/*
	 * Obtain the function pointer for the jump table from kernel
	 * services to fill it with the function pointers that we want
	 */
	if(get_pvr_dc_jtable ("PVRGetDisplayClassJTable",
		&pfnGetPVRJTable) != OMAP_OK)
	{
		ERROR_PRINTK("Unable to get the function to get the"
			" jump table display->services");
		return OMAP_ERROR_INIT_FAILURE;
	}

	/*
	 * Allocate the display device structures, one per display available
	 */
	bytes_to_alloc =
		sizeof(struct OMAP_DISP_DEVINFO) * display_devices_count;
	pDisplayDevices = (struct OMAP_DISP_DEVINFO *) kmalloc(
		bytes_to_alloc, GFP_KERNEL);
	if(!pDisplayDevices)
	{
		pDisplayDevices = NULL;
		ERROR_PRINTK("Out of memory");
		return OMAP_ERROR_OUT_OF_MEMORY;
	}
	memset(pDisplayDevices, 0, bytes_to_alloc);

	/*
	 * Initialize each display device
	 */
	for(i = 0; i < display_devices_count; i++)
	{
		struct omap_display_device *display;
		struct OMAP_DISP_DEVINFO * psDevInfo;
		enum omap_display_id id;

		psDevInfo = &pDisplayDevices[i];
		psDevInfo->display = 0;

		id = OMAP_DISPID_VIRTUAL;

		/*
		 * TODO: Modify this to allow primary, secondary,
		 * not only virtual
		 */
#if 0
		switch(i)
		{
			case 0:
				id = OMAP_DISPID_PRIMARY;
				break;
			case 1:
				id = OMAP_DISPID_SECONDARY;
				break;
			case 2:
				id = OMAP_DISPID_TERTIARY;
				break;
			case 3:
				id = OMAP_DISPID_VIRTUAL;
				break;
			default:
				ERROR_PRINTK("Invalid display type %i", i);
				BUG();
		}

#endif

		display = omap_display_get(id);
		if(!display)
			continue;

		if(init_display_device(psDevInfo, display) != OMAP_OK)
		{
			ERROR_PRINTK("Unable to initialize display '%s' type"
				" %u", display->name, display->id);
			continue;
#if 0
			kfree(pDisplayDevices);
			pDisplayDevices = NULL;
			return OMAP_ERROR_INIT_FAILURE;
#endif
		}

		/*
		 * Populate each display device structure
		*/
		if(!(*pfnGetPVRJTable)(&psDevInfo->sPVRJTable))
		{
			ERROR_PRINTK("Unable to get the jump table"
				" display->services for display '%s'",
				display->name);
			return OMAP_ERROR_INIT_FAILURE;
		}

		/* Populate the function table that services will use */
		psDevInfo->sDCJTable.ui32TableSize =
			sizeof(PVRSRV_DC_SRV2DISP_KMJTABLE);
		psDevInfo->sDCJTable.pfnOpenDCDevice = OpenDCDevice;
		psDevInfo->sDCJTable.pfnCloseDCDevice = CloseDCDevice;
		psDevInfo->sDCJTable.pfnEnumDCFormats = EnumDCFormats;
		psDevInfo->sDCJTable.pfnEnumDCDims = EnumDCDims;
		psDevInfo->sDCJTable.pfnGetDCSystemBuffer = GetDCSystemBuffer;
		psDevInfo->sDCJTable.pfnGetDCInfo = GetDCInfo;
		psDevInfo->sDCJTable.pfnGetBufferAddr = GetDCBufferAddr;
		psDevInfo->sDCJTable.pfnCreateDCSwapChain = CreateDCSwapChain;
		psDevInfo->sDCJTable.pfnDestroyDCSwapChain =
			DestroyDCSwapChain;
		psDevInfo->sDCJTable.pfnSetDCDstRect = SetDCDstRect;
		psDevInfo->sDCJTable.pfnSetDCSrcRect = SetDCSrcRect;
		psDevInfo->sDCJTable.pfnSetDCDstColourKey = SetDCDstColourKey;
		psDevInfo->sDCJTable.pfnSetDCSrcColourKey = SetDCSrcColourKey;
		psDevInfo->sDCJTable.pfnGetDCBuffers = GetDCBuffers;
		psDevInfo->sDCJTable.pfnSwapToDCBuffer = SwapToDCBuffer;
		psDevInfo->sDCJTable.pfnSwapToDCSystem = SwapToDCSystem;
		psDevInfo->sDCJTable.pfnSetDCState = SetDCState;

		/* Register the display device */
		if(psDevInfo->sPVRJTable.pfnPVRSRVRegisterDCDevice(
			&psDevInfo->sDCJTable,
			(IMG_UINT32*) &psDevInfo->ulDeviceID) != PVRSRV_OK)
		{
			ERROR_PRINTK("Unable to register the jump table"
				" services->display");
			return OMAP_ERROR_DEVICE_REGISTER_FAILED;
		}

		DEBUG_PRINTK("Display '%s' registered with the GPU with"
			" id %lu", display->name, psDevInfo->ulDeviceID);

		/*
		 * Register the ProcessFlip function to notify when a frame is
		 * ready to be flipped
		 */
		pfnCmdProcList[DC_FLIP_COMMAND] = ProcessFlip;
		aui32SyncCountList[DC_FLIP_COMMAND][0] = 0;
		aui32SyncCountList[DC_FLIP_COMMAND][1] = 2;
		if (psDevInfo->sPVRJTable.pfnPVRSRVRegisterCmdProcList(
			psDevInfo->ulDeviceID, &pfnCmdProcList[0],
			aui32SyncCountList, OMAP_DC_CMD_COUNT) != PVRSRV_OK)
		{
			ERROR_PRINTK("Unable to register callback for "
				"ProcessFlip command");
			return OMAP_ERROR_CANT_REGISTER_CALLBACK;
		}

	}
	return OMAP_OK;
}
int main(int argc, char *argp[])
{
	char shortoptions[] = "r:w:u:f:i:p:h:x:c:";
	int mode = O_RDWR,c,ret,index, display_index;
	int preview_fd, rsz_fd, capt_fd, display_fd, dev_idx;
	unsigned int oper_mode, user_mode=IMP_MODE_CONTINUOUS;
	struct rsz_channel_config rsz_chan_config;
	struct rsz_continuous_config rsz_cont_config; // continuous mode
	int level;
	int width = 720, height = 480;
	int *capbuf_addr;
	fd_set fds;
	struct timeval tv;
	int r;
	int quit=0;
	struct timezone zone;
	int frame_count=0;
	static int captFrmCnt = 0;
	struct v4l2_buffer cap_buf, disp_buf;
	int temp_index;


	for(;;) {
		c = getopt_long(argc, argp, shortoptions, NULL, (void *)&index);
		if(-1 == c)
			break;
		switch(c) {
		case 'f':
			out_format = atoi(optarg);
			if (out_format < 0 || out_format > 1) {
				printf("Choose 0 - UYVY 1 - NV12 for output pix format\n");
				exit(1);
			}
			break;
		case 'p':
		case 'P':
			printfn = atoi(optarg);
			break;
		case 'h':
			half_fps = atoi(optarg);
			break;
		case 'x':
			disp_second_output = atoi(optarg);
			break;
		case 'c':
			en_crop = atoi(optarg);
			break;
		case 'r':
			resize = atoi(optarg);
			break;
		case 'w':
			rsz_width = atoi(optarg);
			break;
		case 'u':
			rsz_height = atoi(optarg);
			break;

		case 'i':
		case 'I':
			input_index = atoi(optarg);
			if (input_index > 2)
				printf("choose index 0 for Composite,"
					" 1 - Svideo or 2 - component\n");
			break;
			default:
				usage();
				exit(1);
		}
	}

	if (allocate_user_buffers() < 0) {
		printf("Unable to Allocate user buffers\n");
		exit(1);
	}	

	// intialize resizer in continuous mode
	rsz_fd = init_resizer(user_mode);
	if (rsz_fd < 0) {
		exit(1);
	}

	// initialize previewer in continuous mode
	preview_fd = init_previewer(user_mode);

	if (preview_fd < 0) {
		close(rsz_fd);
		exit(1);
	}
	

	capt_fd = init_camera_capture();

	if (capt_fd < 0) {
		close(preview_fd);
		close(rsz_fd);
		exit(1);
	}
	
	if (!out_format)
		second_output_offset = in_width * in_height * 2;
	else
		second_output_offset = ALIGN(in_width ,32) * in_height * 1.5;

	second_output_offset = ALIGN(second_output_offset, 4096);

	printf("Second output offset = %d\n", second_output_offset);
	display_fd = init_display_device(0);
	if (display_fd < 0) {
		close(preview_fd);
		close(rsz_fd);
		close(capt_fd);
		exit(1);
	}

	printf("Initialized display\n");	


#if 0
	if (ioctl(preview_fd, PREV_DUMP_HW_CONFIG, &level) < 0) {
		perror("Error in debug ioctl\n");
		cleanup_capture(capt_fd);
		cleanup_display(display_fd);
		close(preview_fd);
		close(rsz_fd);
		exit(1);
	} 
#endif

	while (!quit)
	{
		unsigned long temp;

		CLEAR(cap_buf);
		CLEAR(disp_buf);

try1:
		disp_buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
		disp_buf.memory = V4L2_MEMORY_USERPTR;
		ret =  ioctl(display_fd, VIDIOC_DQBUF, &disp_buf);
		if (ret < 0) {
			if (errno == EAGAIN) {
				goto try1;
			}
			perror("VIDIOC_DQBUF for display failed\n");
			return ret;
		}
		
		cap_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		cap_buf.memory = V4L2_MEMORY_USERPTR;
try2:
		ret = ioctl(capt_fd, VIDIOC_DQBUF, &cap_buf);
		if (ret < 0) {
			if (errno == EAGAIN) {
				goto try2;
			}
			perror("VIDIOC_DQBUF for capture failed\n");
			return ret;
		}

		temp = cap_buf.m.userptr;
		if (disp_second_output) {
			cap_buf.m.userptr = disp_buf.m.userptr - second_output_offset;
			disp_buf.m.userptr = temp + second_output_offset;
		} else {
			cap_buf.m.userptr = disp_buf.m.userptr;
			disp_buf.m.userptr = temp;
		}
		//printf("disp_buf.m.userptr = %x\n", disp_buf.m.userptr); 

		ret = ioctl(capt_fd, VIDIOC_QBUF, &cap_buf);
		if (ret < 0) {
			perror("VIDIOC_QBUF for capture failed\n");
			return ret;
		}
		
		ret = ioctl(display_fd, VIDIOC_QBUF, &disp_buf);
		if (ret < 0) {
			perror("VIDIOC_QBUF for display failed\n");
			return ret;
		}
		if (captFrmCnt == 0)
			prev_ts = (cap_buf.timestamp.tv_sec*1000000) + cap_buf.timestamp.tv_usec;
		else {
			curr_ts = (cap_buf.timestamp.tv_sec*1000000) + cap_buf.timestamp.tv_usec;
			fp_period = curr_ts - prev_ts;
			if (captFrmCnt == 1) {
				fp_period_max = fp_period_min = fp_period_average = fp_period;
			}
			else {
				/* calculate jitters and average */
				if (fp_period > fp_period_max)
					fp_period_max = fp_period;
				if (fp_period < fp_period_min)
					fp_period_min = fp_period;
				
				fp_period_average =
					((fp_period_average * captFrmCnt) +
					fp_period)/(captFrmCnt + 1); 
			}
			prev_ts = curr_ts;
		}

		captFrmCnt++;
		
		if (printfn) {
			printf("frame:%5u, ", captFrmCnt);
			printf("buf.timestamp:%lu:%lu\n",
				cap_buf.timestamp.tv_sec, cap_buf.timestamp.tv_usec);
		}
		if (captFrmCnt > 5000)
			break;

	}
	printf("Cleaning capture\n");
	cleanup_capture(capt_fd);
	printf("Cleaning display\n");
	cleanup_display(display_fd);
	printf("Cleaning display - end\n");
	close(preview_fd);
	printf("closing preview- end\n");
	close(rsz_fd);
	printf("closing resize - end\n");
	exit(0);
}