/*
 *******************************************************************************
 *  Copyright (c) 2010-2022 VATICS(KNERON) Inc. All rights reserved.
 *
 *  +-----------------------------------------------------------------+
 *  | THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED |
 *  | AND COPIED IN ACCORDANCE WITH THE TERMS AND CONDITIONS OF SUCH  |
 *  | A LICENSE AND WITH THE INCLUSION OF THE THIS COPY RIGHT NOTICE. |
 *  | THIS SOFTWARE OR ANY OTHER COPIES OF THIS SOFTWARE MAY NOT BE   |
 *  | PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. THE   |
 *  | OWNERSHIP AND TITLE OF THIS SOFTWARE IS NOT TRANSFERRED.        |
 *  |                                                                 |
 *  | THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT   |
 *  | ANY PRIOR NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY |
 *  | VATICS(KNERON) INC.                                             |
 *  +-----------------------------------------------------------------+
 *
 *******************************************************************************
 */
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

#include "vmf/video_source.h"
#include "vmf/fec_layout.h"
#include "comm/video_buf.h"
#include "vmf/video_encoder.h"
#include "fec_api.h"
#include "lvgl_example.h"

//extern VMF_LAYOUT_T g_tLayout;
//extern VMF_VSRC_HANDLE_T* g_ptVsrcHandle;
static VMF_FEC_PTZ_CONFIG_T  g_tFecPtzConfig[4];
static VMF_FEC_CELL_CONFIG_T g_tFecCellConfig[4];
static VMF_FEC_CELL_CONFIG_T g_tPIPFecCellConfig[2];
static VMF_FEC_LYT_CONFIG_T  g_tFecLytConfig;
static pthread_t eptz_thread;
static int stop_eptz_thread = 1;

static void *eptz_tester(void *);
static void setup_lyt_spec(VMF_ENC_SPEC_T *, VMF_VENC_CODEC_TYPE);
static void setup_fec_layout(VMF_FEC_LYT_CONFIG_T *, unsigned int, VMF_VENC_CODEC_TYPE);
static int setup_fec_4r_eptz(unsigned int, int, VMF_VENC_CODEC_TYPE);
static int setup_fec_4r_hcut(unsigned int, VMF_VENC_CODEC_TYPE);
static int setup_fec_pip(unsigned int, VMF_VENC_CODEC_TYPE);
static int setup_fec_1r(unsigned int, VMF_VENC_CODEC_TYPE);
static int setup_fec_1r_area(unsigned int, VMF_VENC_CODEC_TYPE);
static int setup_fec_p180(unsigned int, VMF_VENC_CODEC_TYPE);
static int setup_fec_p360_separate(unsigned int, VMF_VENC_CODEC_TYPE);
static void release_eptz_tester(void); 

void *eptz_tester(void* user_data  __attribute__((unused)))
{
	const unsigned int pan_step = 2;
	const unsigned int tilt_step = 2;
	g_tFecLytConfig.bCoeffOnly = 1;

	while (!stop_eptz_thread) {
		g_tFecPtzConfig[0].fPan += pan_step;
		g_tFecPtzConfig[0].fPan = (g_tFecPtzConfig[0].fPan <= 180)?  g_tFecPtzConfig[0].fPan : -180; // Pan range: [-180, 180]
		
		g_tFecPtzConfig[0].fTilt += tilt_step;
		g_tFecPtzConfig[0].fTilt = (g_tFecPtzConfig[0].fTilt <= 180)? g_tFecPtzConfig[0].fTilt : -180; // Tilt range: [-180, 180]

		g_tFecCellConfig[0].eFecMode   = VMF_FEC_COEF_MODE_PTZ;
		g_tFecCellConfig[0].pFecConfig = g_tFecPtzConfig;

		//! setup layout
		VMF_FEC_LYT_Quad(g_ptVsrcHandle, &g_tFecLytConfig, g_tFecCellConfig);
		usleep(DELAY_TIME);
	}

	return NULL;
}

void setup_lyt_spec(VMF_ENC_SPEC_T *ptSpec, VMF_VENC_CODEC_TYPE eCodecType)
{	
	if (eCodecType == VMF_VENC_CODEC_TYPE_H265){
		ptSpec->bEncH265 = 1;
	} else if (eCodecType == VMF_VENC_CODEC_TYPE_H264){
		ptSpec->bEncH264 = 1;
	} else if (eCodecType == VMF_VENC_CODEC_TYPE_MJPG){
		ptSpec->bEncJPEG = 1;
	}
}

void setup_fec_layout(VMF_FEC_LYT_CONFIG_T *ptFecLayout, unsigned int output_index, VMF_VENC_CODEC_TYPE eCodecType)
{
	ptFecLayout->dwOutputId = output_index;
	ptFecLayout->tLayout.dwCanvasWidth = g_tLayout.dwCanvasWidth;
	ptFecLayout->tLayout.dwCanvasHeight = g_tLayout.dwCanvasHeight;
	ptFecLayout->tLayout.dwVideoWidth = g_tLayout.dwVideoWidth;
	ptFecLayout->tLayout.dwVideoHeight = g_tLayout.dwVideoHeight;
	ptFecLayout->tLayout.dwVideoPosX = g_tLayout.dwVideoPosX;
	ptFecLayout->tLayout.dwVideoPosY = g_tLayout.dwVideoPosY;
	ptFecLayout->dwClearBackColor = 0; //! full FEC layout dont need to setup back color
	ptFecLayout->eGridSize = VMF_FEC_GRID_8X8;
	ptFecLayout->eLayoutMethod = VMF_FEC_METHOD_GTR; 
	setup_lyt_spec(&ptFecLayout->tEncSpec, eCodecType);
}

int setup_fec_4r_eptz(unsigned int output_index, int enable, VMF_VENC_CODEC_TYPE eCodecType)
{
	const unsigned int dwNum = 4;
	size_t i = 0;

	memset(g_tFecPtzConfig, 0, (sizeof(VMF_FEC_PTZ_CONFIG_T) * dwNum));
	memset(g_tFecCellConfig, 0, (sizeof(VMF_FEC_CELL_CONFIG_T) * dwNum));
	memset(&g_tFecLytConfig, 0, sizeof(VMF_FEC_LYT_CONFIG_T));
	
	setup_fec_layout(&g_tFecLytConfig, output_index, eCodecType);
	g_tFecLytConfig.eGridSize = VMF_FEC_GRID_32X32;	
	
	//! setup cell config parameters
	for (i = 0; i < dwNum; ++i) {
		g_tFecPtzConfig[i].fZoom        = 1;
		g_tFecPtzConfig[i].fFocalLength = 0.65;
		g_tFecPtzConfig[i].eAppType     = VMF_FEC_APP_TABL;
		g_tFecPtzConfig[i].eLensType    = VMF_FEC_LENS_STEREOGRAPHIC;

		if (VMF_FEC_APP_WALL != g_tFecPtzConfig[i].eAppType) {
			g_tFecPtzConfig[i].fPan  = i * 90;//(360 / ptz_num);
			g_tFecPtzConfig[i].fTilt = 35;
		} else {
			g_tFecPtzConfig[i].fPan  = (0 == (i >> 1)) ? 35 : -20;
			g_tFecPtzConfig[i].fTilt = (0 == (i %  2)) ? 20 : -20;
		}

		g_tFecCellConfig[i].eFecMode   = VMF_FEC_COEF_MODE_PTZ;
		g_tFecCellConfig[i].pFecConfig = g_tFecPtzConfig + i;
	}

	//! setup layout
	VMF_FEC_LYT_Quad(g_ptVsrcHandle, &g_tFecLytConfig, g_tFecCellConfig);

	if (enable) {
		stop_eptz_thread = 0;
		pthread_create(&eptz_thread, NULL, &eptz_tester, NULL);
	}

	return 0;
}

int setup_fec_4r_hcut(unsigned int output_index, VMF_VENC_CODEC_TYPE eCodecType)
{
	const unsigned int dwNum = 4;
	size_t i = 0;

	memset(g_tFecPtzConfig, 0, (sizeof(VMF_FEC_PTZ_CONFIG_T) * dwNum));
	memset(g_tFecCellConfig, 0, (sizeof(VMF_FEC_CELL_CONFIG_T) * dwNum));
	memset(&g_tFecLytConfig, 0, sizeof(VMF_FEC_LYT_CONFIG_T));
	
	setup_fec_layout(&g_tFecLytConfig, output_index, eCodecType);
	g_tFecLytConfig.eGridSize = VMF_FEC_GRID_32X32;	
	
	//! setup cell config parameters
	for (i = 0; i < dwNum; ++i) {
		g_tFecPtzConfig[i].fZoom        = 1;
		g_tFecPtzConfig[i].fFocalLength = 0.65;
		g_tFecPtzConfig[i].eAppType     = VMF_FEC_APP_TABL;
		g_tFecPtzConfig[i].eLensType    = VMF_FEC_LENS_STEREOGRAPHIC;

		if (VMF_FEC_APP_WALL != g_tFecPtzConfig[i].eAppType) {
			g_tFecPtzConfig[i].fPan  = i * 90;//(360 / ptz_num);
			g_tFecPtzConfig[i].fTilt = 35;
		} else {
			g_tFecPtzConfig[i].fPan  = (0 == (i >> 1)) ? 35 : -20;
			g_tFecPtzConfig[i].fTilt = (0 == (i %  2)) ? 20 : -20;
		}

		g_tFecCellConfig[i].eFecMode   = VMF_FEC_COEF_MODE_PTZ;
		g_tFecCellConfig[i].pFecConfig = g_tFecPtzConfig + i;
	}

	//! setup layout
	VMF_FEC_LYT_Quad_Hcut(g_ptVsrcHandle, &g_tFecLytConfig, g_tFecCellConfig);

	return 0;
}

int setup_fec_pip(unsigned int output_index, VMF_VENC_CODEC_TYPE eCodecType)
{
	const unsigned int dwNum = 2;
	size_t i = 0;
	VMF_FEC_P180_CONFIG_T tFecP180Config;
	VMF_FEC_PTZ_CONFIG_T  tFecPtzConfig;
	memset(&tFecP180Config, 0, sizeof(VMF_FEC_P180_CONFIG_T));
	memset(&tFecPtzConfig, 0, sizeof(VMF_FEC_PTZ_CONFIG_T));
	memset(g_tPIPFecCellConfig, 0, (sizeof(VMF_FEC_CELL_CONFIG_T) * dwNum));
	memset(&g_tFecLytConfig, 0, sizeof(VMF_FEC_LYT_CONFIG_T));

	setup_fec_layout(&g_tFecLytConfig, output_index, eCodecType);
	g_tFecLytConfig.eGridSize = VMF_FEC_GRID_16X16;

	//! setup cell config parameters
	for (i = 0; i < dwNum; ++i) {
		if(i == 0){
			printf("i = 0\n");
			tFecP180Config.fPan = 0;
			tFecP180Config.fTilt = 0;
			tFecP180Config.fZoom = 1;
			tFecP180Config.fFocalLength = 0.7;
			tFecP180Config.fRectCurvature = 0.65;
			tFecP180Config.fRectSlope = 0.8;
			tFecP180Config.eModeType = VMF_FEC_MODE_PANO_180_ONE_DIRECTION;

			g_tPIPFecCellConfig[i].eFecMode = VMF_FEC_COEF_MODE_P180;
			g_tPIPFecCellConfig[i].pFecConfig = &tFecP180Config;
		} else if(i == 1){
			printf("i = 1\n");
			tFecPtzConfig.fZoom = 1;
			tFecPtzConfig.fFocalLength = 0.85;
			tFecPtzConfig.eAppType = VMF_FEC_APP_WALL;
			tFecPtzConfig.eLensType = VMF_FEC_LENS_STEREOGRAPHIC;
			tFecPtzConfig.fPan  = 0;
			tFecPtzConfig.fTilt = -45;

			g_tPIPFecCellConfig[i].eFecMode   = VMF_FEC_COEF_MODE_PTZ;
			g_tPIPFecCellConfig[i].pFecConfig = &tFecPtzConfig;
		}
	}

	const unsigned int rectangle[2][2] = {
		{g_tFecLytConfig.tLayout.dwCanvasWidth, g_tFecLytConfig.tLayout.dwCanvasHeight},
             {320, 240}
       };

    const unsigned int offset[2][2] = {
             {0, 0},
		{g_tFecLytConfig.tLayout.dwCanvasWidth-320, g_tFecLytConfig.tLayout.dwCanvasHeight-240}
       };
	printf("Width = %d\n", g_tFecLytConfig.tLayout.dwCanvasWidth);
	printf("Height = %d\n", g_tFecLytConfig.tLayout.dwCanvasHeight);
	//! setup layout
	VMF_FEC_LYT_Double_Customize(g_ptVsrcHandle, &g_tFecLytConfig, g_tPIPFecCellConfig, rectangle, offset);
	print_msg("[%s] done\n", __func__);

	return 0;
}

int setup_fec_1r(unsigned int output_index, VMF_VENC_CODEC_TYPE eCodecType)
{
	VMF_FEC_PTZ_CONFIG_T  tFecPtzConfig; 
	VMF_FEC_CELL_CONFIG_T tFecCellConfig; 
	VMF_FEC_LYT_CONFIG_T  tFecLytConfig;
	
	memset(&tFecPtzConfig, 0, sizeof(VMF_FEC_PTZ_CONFIG_T) );
	memset(&tFecCellConfig, 0, sizeof(VMF_FEC_CELL_CONFIG_T));
	memset(&tFecLytConfig, 0, sizeof(VMF_FEC_LYT_CONFIG_T));

	setup_fec_layout(&tFecLytConfig, output_index, eCodecType);
	
	tFecPtzConfig.fZoom = 1;
	tFecPtzConfig.fFocalLength = 0.65;
	tFecPtzConfig.eAppType = VMF_FEC_APP_TABL;
	tFecPtzConfig.eLensType = VMF_FEC_LENS_STEREOGRAPHIC;

	if (VMF_FEC_APP_WALL != tFecPtzConfig.eAppType) {
		tFecPtzConfig.fPan  = 0;//(360 / ptz_num);
		tFecPtzConfig.fTilt = 35;
	} else {
		tFecPtzConfig.fPan  = (0 == (0 >> 1)) ? 35 : -20;
		tFecPtzConfig.fTilt = (0 == (0 %  2)) ? 20 : -20;
	}

	tFecCellConfig.eFecMode   = VMF_FEC_COEF_MODE_PTZ;
	tFecCellConfig.pFecConfig = &tFecPtzConfig;

	VMF_FEC_LYT_Single(g_ptVsrcHandle, &tFecLytConfig, &tFecCellConfig);
	print_msg("[%s] done\n", __func__);

	return 0;
}

int setup_fec_1r_area(unsigned int output_index, VMF_VENC_CODEC_TYPE eCodecType)
{
	VMF_FEC_AREA_CONFIG_T tFecAreaConfig;
	VMF_FEC_CELL_CONFIG_T tFecCellConfig;
	VMF_FEC_LYT_CONFIG_T  tFecLytConfig;
	
	memset(&tFecAreaConfig, 0, sizeof(VMF_FEC_AREA_CONFIG_T));
	memset(&tFecCellConfig, 0, sizeof(VMF_FEC_CELL_CONFIG_T));
	memset(&tFecLytConfig, 0, sizeof(VMF_FEC_LYT_CONFIG_T));

	setup_fec_layout(&tFecLytConfig, output_index, eCodecType);

	tFecAreaConfig.dwCenterX = g_tLayout.dwVideoWidth/2;
	tFecAreaConfig.dwCenterY = g_tLayout.dwCanvasHeight/2;
	tFecAreaConfig.fZoom = 1;
	tFecAreaConfig.fFocalLength = 0.65;
	tFecAreaConfig.eAppType = VMF_FEC_APP_TABL;
	tFecAreaConfig.eLensType = VMF_FEC_LENS_STEREOGRAPHIC;
	tFecAreaConfig.dwOutRadius = 100;

	tFecCellConfig.eFecMode = VMF_FEC_COEF_MODE_AREA;
	tFecCellConfig.pFecConfig = &tFecAreaConfig;

	VMF_FEC_LYT_Single(g_ptVsrcHandle, &tFecLytConfig, &tFecCellConfig);
	print_msg("[%s] done\n", __func__);

	return 0;
}

int setup_fec_p180(unsigned int output_index, VMF_VENC_CODEC_TYPE eCodecType)
{
	VMF_FEC_P180_CONFIG_T tFecP180Config;
	VMF_FEC_CELL_CONFIG_T tFecCellConfig;
	VMF_FEC_LYT_CONFIG_T  tFecLytConfig;

	memset(&tFecP180Config, 0, sizeof(VMF_FEC_P180_CONFIG_T));
	memset(&tFecCellConfig, 0, sizeof(VMF_FEC_CELL_CONFIG_T));
	memset(&tFecLytConfig, 0, sizeof(VMF_FEC_LYT_CONFIG_T));

	setup_fec_layout(&tFecLytConfig, output_index, eCodecType);

	//! setup P180 cell config parameters
	tFecP180Config.fPan = 0;
	tFecP180Config.fTilt = 0;
	tFecP180Config.fZoom = 1.5;
	tFecP180Config.fFocalLength = 0.7;
	tFecP180Config.fRectCurvature = 0.65;
	tFecP180Config.fRectSlope = 0.8;
	tFecP180Config.eModeType = VMF_FEC_MODE_PANO_180_TWO_DIRECTION;

	tFecCellConfig.eFecMode = VMF_FEC_COEF_MODE_P180;
	tFecCellConfig.pFecConfig = &tFecP180Config;

	//! setup layout
	VMF_FEC_LYT_Single(g_ptVsrcHandle, &tFecLytConfig, &tFecCellConfig);
	print_msg("[%s] done\n", __func__);

	return 0;
}

int setup_fec_p360_separate(unsigned int output_index, VMF_VENC_CODEC_TYPE eCodecType)
{
	VMF_FEC_P360_CONFIG_T tFecP360Config;
	VMF_FEC_CELL_CONFIG_T tFecCellConfig;
	VMF_FEC_LYT_CONFIG_T  tFecLytConfig;

	memset(&tFecP360Config, 0, sizeof(VMF_FEC_P360_CONFIG_T));
	memset(&tFecCellConfig, 0, sizeof(VMF_FEC_CELL_CONFIG_T));
	memset(&tFecLytConfig, 0, sizeof(VMF_FEC_LYT_CONFIG_T));

	setup_fec_layout(&tFecLytConfig, output_index, eCodecType);

	//! setup P360 cell config parameters
	tFecP360Config.fPan = 0;
	tFecP360Config.fTilt = 5;
	tFecP360Config.fZoom = 1.0;
	tFecP360Config.fFocalLength = 0.7;
	tFecP360Config.eLensType = VMF_FEC_LENS_EQUIDISTANT;
	tFecP360Config.eAppType = VMF_FEC_APP_TABL;
	tFecP360Config.eModeType = VMF_FEC_MODE_PANO_360_SEPE;

	tFecCellConfig.eFecMode = VMF_FEC_COEF_MODE_P360;
	tFecCellConfig.pFecConfig = &tFecP360Config;

	//! setup layout
	VMF_FEC_LYT_P360_Separated(g_ptVsrcHandle, &tFecLytConfig, &tFecCellConfig);
	print_msg("[%s] done\n", __func__);

	return 0;
}

void release_eptz_tester(void) 
{
	if (!stop_eptz_thread) {
		stop_eptz_thread = 1;
		pthread_join(eptz_thread, NULL);
	}
}

int setup_fec_mode(unsigned int dwIndex, FEC_MODE mode, unsigned int enable, VMF_VENC_CODEC_TYPE eCodecType)
{
	release_eptz_tester();	

	switch(mode) 
	{
		case FEC_MODE_1R:
			setup_fec_1r(dwIndex, eCodecType);
			break;

		case FEC_MODE_1R_AREA:
			setup_fec_1r_area(dwIndex, eCodecType);
			break;
	
		case FEC_MODE_4R:
			setup_fec_4r_eptz(dwIndex, enable, eCodecType);
			break;
	
		case FEC_MODE_4R_HCUT:
			setup_fec_4r_hcut(dwIndex, eCodecType);
			break;
		
		case FEC_MODE_360:
			setup_fec_p360_separate(dwIndex, eCodecType);
			break;
	
		case FEC_MODE_180:
			setup_fec_p180(dwIndex, eCodecType);
			break;

		case FEC_MODE_PIP:
			setup_fec_pip(dwIndex, eCodecType);
			break;

		default:
			print_msg("[%s] Invalid FEC MODE\n", __func__);
			return -1;
	}

	return 0;
}
