/*
 *******************************************************************************
 *  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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <stdarg.h>
#include <pthread.h>
#include <stdbool.h>

#include "fec_api.h"
#include "lvgl_example.h"
#include "lvgl_example_msg_sender.h"

#include "MemBroker/mem_broker.h"
#include "MsgBroker/msg_broker.h"
#include "vmf/video_source.h"
#include "vmf/video_bind.h"
#include "vmf/video_encoder.h"
#include "vmf/video_display_mechanism.h"
#include "vmf/source_connect.h"
#include "vmf/sync_shared_memory.h"
#include "vmf/config_fec.h"
#include "vmf/vector_dma.h"
#include "comm/video_buf.h"
#include "comm/frame_info.h"
#include "iniparser/iniparser.h"

#include "lvgl/lvgl.h"
#include "lvgl/custom/lv_custom.h"
#include "lv_drivers/display/v4l2dev.h"

#define VENC_VSRC_PIN		"vsrc_ssm"
#define VENC_RESOURCE_DIR	"./Resource/"

#define release_string(X) do { if (X) free(X); } while (0)

typedef enum {
	HIDE = 0,
	SHOW
} TEXT_MODE;

VMF_LAYOUT_T g_tLayout;
VMF_VSRC_HANDLE_T* g_ptVsrcHandle = NULL;

static char *g_szConfig = NULL;
static char *g_szAutoSceneConfig = NULL;
static char *g_szSensorConfig = NULL;
static uint32_t g_dwWidth, g_dwHeight;
static uint32_t g_dwFontSize, g_dwSubIdx;
static VMF_VENC_CODEC_TYPE g_eCodecType = VMF_VENC_CODEC_TYPE_H265;
static int g_bTerminate = 0;
static char text_string[32];

/*gui setting */
static uint32_t pool_size;
static uint32_t gui_width;
static uint32_t gui_height;
static uint32_t gui_x;
static uint32_t gui_y;
static bool yuv_device;

/*Indev button data*/
static const char *btn_txt[2][4] = { 
	{ "page1",    "show_def", "show_ghi", "show_jkl" },
	{ "show_lmn", "show_opq", "show_rst", "return"   }
};
static const char *ovl_txt[2][4] = {
	{ "",   "def", "ghi", "jkl" },
	{"lmn", "opq", "rst", ""    }
};
static lv_point_t positons[4];
static int btn_index = 0;
static bool btn_status = 0;
static int page_index = 0;

static VMF_BIND_CONTEXT_T* g_ptBind = NULL;

extern void dump_yuv(uint8_t *, uint8_t *, uint8_t *, uint32_t, const char *);

static void load_config(void);
static void sig_handler(int);
static void exit_handler(void);
static int init_video_source(void);
static int init_fec_mode(void);
static int init_dma_handle(VMF_DMA_DESCRIPTOR_T **, VMF_DMA_HANDLE_T **);
static int dma_upadate_mem(uint8_t *, uint8_t *, uint32_t, VMF_DMA_DESCRIPTOR_T *, VMF_DMA_HANDLE_T *);
static void vsrc_init_callback(unsigned int, unsigned int);
static void setup_fec(VMF_FEC_ORIG_CONFIG_T *);
static void setup_spec(VMF_VSRC_SPEC_CONFIG_T *, VMF_VENC_CODEC_TYPE);
static void release_video_source(void);
static int init_bind(void);
static int init_video_display(VMF_VDISP_HANDLE_T **);
static void *video_loop(void *);
static void *gui_loop(void *);
static void draw_screen(void);
static bool button_read(lv_indev_drv_t *, lv_indev_data_t *);
static int get_button_index(void);
static bool get_button_status(void);
static void reset_button_status(void);
static void button_event(lv_obj_t *, lv_event_t);
static void release_bind(void);
static void msg_callback(MsgContext *, void *);
static int init_text_overlay(void);
static int set_text_overlay(TEXT_MODE);

void dump_yuv(uint8_t *y, uint8_t *u, uint8_t *v, uint32_t y_size, const char *fmt)
{
	FILE *out = fopen(fmt, "wb");
	fwrite(y, 1, y_size, out);
	fwrite(u, 1, y_size >> 2, out);
	fwrite(v, 1, y_size >> 2, out);
	fclose(out);
}

void load_config(void)
{
	const char *tmp;
	dictionary *ini;

	if((ini = iniparser_load(g_szConfig)) == NULL) {
		print_msg("Unable to parse file: %s\n", g_szConfig);
		exit(EXIT_FAILURE);
	}

	tmp = iniparser_getstring(ini, "sensor:sensor_config", NULL);
	g_szSensorConfig = strdup(tmp);

	tmp = iniparser_getstring(ini, "sensor:autoscene_config", NULL);
	g_szAutoSceneConfig = strdup(tmp);

	g_dwWidth  = iniparser_getint(ini, "stream:width",  1920);
	g_dwHeight = iniparser_getint(ini, "stream:height", 1080);

	pool_size = iniparser_getint(ini, "gui:pool_size", 0);

	gui_width  = iniparser_getint(ini, "gui:width",  480);
	gui_height = iniparser_getint(ini, "gui:height", 320);

	gui_x = iniparser_getint(ini, "gui:pos_x", 0);
	gui_y = iniparser_getint(ini, "gui:pos_y", 0);

	yuv_device = iniparser_getboolean(ini, "gui:yuv_device", 0);

	g_dwFontSize = iniparser_getint(ini, "text_overlay:font_size", 5);

	iniparser_freedict(ini);
}

void sig_handler(int sig)
{
	print_msg("[%s] receive SIGNAL: %d\n",__func__, sig);
	g_bTerminate = 1;
}

void exit_handler(void)
{
	release_string(g_szConfig);
	release_string(g_szSensorConfig);
	release_string(g_szAutoSceneConfig);
}

int init_video_source(void)
{
	VMF_VSRC_INITOPT_T tInitOpt;
	VMF_VSRC_FRONTEND_CONFIG_T tFrontCfg;
	VMF_FEC_ORIG_CONFIG_T tFecOrgCfg;

	memset(&tInitOpt, 0, sizeof tInitOpt);
	memset(&tFrontCfg, 0, sizeof tFrontCfg);
	memset(&tFecOrgCfg, 0, sizeof tFecOrgCfg);

	setup_fec(&tFecOrgCfg);
	tFrontCfg.tFecInitConfig.ptFecConfig = &tFecOrgCfg;
	tFrontCfg.tFecInitConfig.eCoeffMode = VMF_FEC_COEF_MODE_ORIG;
	tFrontCfg.tFecInitConfig.eFecMethod = VMF_FEC_METHOD_GTR;
	tFrontCfg.tFecInitConfig.eGridSize = VMF_FEC_GRID_8X8;
	tFrontCfg.dwSensorConfigCount = 1;
	tFrontCfg.apszSensorConfig[0] = g_szSensorConfig;

	tInitOpt.dwFrontConfigCount = 1;
	tInitOpt.ptFrontConfig = &tFrontCfg;
	tInitOpt.pszAutoSceneConfig = g_szAutoSceneConfig;
	tInitOpt.pszOutPinPrefix = VENC_VSRC_PIN;
	tInitOpt.bShared = 1;
	tInitOpt.fnInitCallback = vsrc_init_callback;
	tInitOpt.pszResourceDir = VENC_RESOURCE_DIR;
	setup_spec(&tInitOpt.tSpecConfig, g_eCodecType);

	g_ptVsrcHandle =  VMF_VSRC_Init(&tInitOpt);
	if (!g_ptVsrcHandle) {
		print_msg("[%s] VMF_VSRC_Init failed!\n", __func__);
		return -1;
	}

	if (VMF_VSRC_Start(g_ptVsrcHandle, NULL)) {
		print_msg("[%s] VMF_VSRC_Start failed!\n", __func__);
		return -1;
	}
	return 0;
}

int init_fec_mode(void)
{
	if (setup_fec_mode(0, FEC_MODE_1R, false, g_eCodecType)) {
		print_msg("[%s] setup_fec_mode failed!\n", __func__);
		return -1;
	}
	return 0;
}

int init_dma_handle(VMF_DMA_DESCRIPTOR_T **pptDmaDesc, VMF_DMA_HANDLE_T **pptDmaHandle)
{
	VMF_DMA_1D_INIT_T tDmaInit;
	memset(&tDmaInit, 0, sizeof tDmaInit);
	tDmaInit.dwFormatFlag = 0;
	*pptDmaDesc = VMF_DMA_Descriptor_Create(DMA_1D, &tDmaInit);
	if (!*pptDmaDesc) {
		print_msg("[%s] VMF_DMA_Descriptor_Create failed!\n", __func__);
		return -1;
	}
	*pptDmaHandle = VMF_DMA_Init(1, 128);
	if (!*pptDmaHandle) {
		print_msg("[%s] VMF_DMA_Init failed!\n", __func__);
		return -1;
	}
	return 0;
}

int dma_upadate_mem(uint8_t *pbyDst, uint8_t *pbySrc, uint32_t dwSize,
	VMF_DMA_DESCRIPTOR_T *ptDmaDesc, VMF_DMA_HANDLE_T *ptDmaHandle)
{
	VMF_DMA_ADDR_T tDmaAddr;
	tDmaAddr.pbySrcYPhysAddr = pbySrc;
	tDmaAddr.pbyDstYPhysAddr = pbyDst;
	tDmaAddr.dwTransSize = dwSize;
	if (VMF_DMA_Descriptor_Update_Addr(ptDmaDesc, &tDmaAddr)) {
		print_msg("[%s] VMF_DMA_Descriptor_Update_Addr failed!\n", __func__);
		return -1;
	}
	if (VMF_DMA_Setup(ptDmaHandle, &ptDmaDesc, 1)) {
		print_msg("[%s] VMF_DMA_Setup failed!\n", __func__);
		return -1;
	}
	if (VMF_DMA_Process(ptDmaHandle)) {
		print_msg("[%s] VMF_DMA_Process failed!\n", __func__);
		return -1;
	}
	return 0;
}

void vsrc_init_callback(unsigned int dwWidth, unsigned int dwHeight)
{
	memset(&g_tLayout, 0, sizeof g_tLayout);
	g_tLayout.dwCanvasWidth = dwWidth;
	g_tLayout.dwCanvasHeight = dwHeight;
	g_tLayout.dwVideoPosX = 0;
	g_tLayout.dwVideoPosY = 0;
	g_tLayout.dwVideoWidth = dwWidth;
	g_tLayout.dwVideoHeight = dwHeight;
	print_msg("[%s]: width:%u, height:%u\n", __func__, dwHeight, dwHeight);
}

void setup_spec(VMF_VSRC_SPEC_CONFIG_T *ptSpec, VMF_VENC_CODEC_TYPE eCodecType)
{
	ptSpec->bEnableSpec = 1;

	ptSpec->dwIspMode = VMF_ISP_MODE_FEC;

	switch (eCodecType) {
	case VMF_VENC_CODEC_TYPE_H264:
		ptSpec->tIfpEncSpec.bEncH264 = 1;
		ptSpec->tIspEncSpec.bEncH264 = 1;
		break;
	case VMF_VENC_CODEC_TYPE_H265:
		ptSpec->tIfpEncSpec.bEncH265 = 1;
		ptSpec->tIspEncSpec.bEncH265 = 1;
		break;
	case VMF_VENC_CODEC_TYPE_MJPG:
		ptSpec->tIfpEncSpec.bEncJPEG = 1;
		ptSpec->tIspEncSpec.bEncJPEG = 1;
		break;
	default:
		exit(EXIT_FAILURE);
	}
}

void setup_fec(VMF_FEC_ORIG_CONFIG_T *ptFecOrgCfg)
{
	ptFecOrgCfg->eAppType = VMF_FEC_APP_TABL;
	ptFecOrgCfg->fZoom = 1.0;
}

void release_video_source(void)
{
	if (g_ptVsrcHandle) {
		VMF_VSRC_Stop(g_ptVsrcHandle);
		VMF_VSRC_Release(g_ptVsrcHandle);
	}
}

int init_bind(void)
{
	VMF_BIND_INITOPT_T tInitOpt;
	memset(&tInitOpt, 0, sizeof tInitOpt);

	tInitOpt.dwSrcOutputIndex = 0;
	tInitOpt.ptSrcHandle = g_ptVsrcHandle;
	tInitOpt.pfnQueryFunc = (VMF_BIND_QUERY_FUNC) VMF_VSRC_GetInfo;
	tInitOpt.pfnIspFunc = (VMF_BIND_CONFIG_ISP_FUNC) VMF_VSRC_ConfigISP;

	g_ptBind = VMF_BIND_Init(&tInitOpt);
	if (!g_ptBind){
		print_msg("[%s] VMF_BIND_Init failed!!\n", __func__);
		return -1;
	}
	return 0;
}

void release_bind(void)
{
	if (g_ptBind)
		VMF_BIND_Release(g_ptBind);
}

int init_video_display(VMF_VDISP_HANDLE_T **pptDispHandle)
{
	VMF_VDISP_INITOPT_T tDisplayOpt;
	memset(&tDisplayOpt, 0, sizeof tDisplayOpt);

	tDisplayOpt.dwInPixFormat = VMF_MAKEFOURCC('Y', 'M', '1', '2');;
	tDisplayOpt.dwMaxInWidth  = VMF_32_ALIGN(g_dwWidth);
	tDisplayOpt.dwMaxInHeight = VMF_16_ALIGN(g_dwHeight);
	if ((*pptDispHandle = VMF_VDISP_Init(&tDisplayOpt)) == NULL) {
		printf("%s VMF_VDISP_Init failed!\n", __func__);
		return -1;
	} 
	return 0;
}

void *video_loop(void *data __attribute__((unused)))
{
	VMF_SRC_CONNECT_INFO_T tSrcCntInfo;
	SSM_HANDLE_T *ptSsmHandle = NULL;
	SSM_BUFFER_T tSsmBuf;
	VMF_FRAME_BUF_T atDispBuf[VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE];
	uint8_t *atPhysAddr[VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE];
	unsigned int dwDispQIdx = 0;
	pthread_t thrd_g;
	unsigned int dwYSize = VMF_32_ALIGN(g_dwWidth)*VMF_16_ALIGN(g_dwHeight), dwYUVSize = dwYSize * 3 >> 1;
	VMF_DMA_DESCRIPTOR_T *ptDmaDesc = NULL;
	VMF_DMA_HANDLE_T *ptDmaHandle = NULL;
	VMF_VDISP_HANDLE_T *ptDispHandle = NULL;

	if (init_video_display(&ptDispHandle)) {
		print_msg("[%s] init_video_display failed!\n", __func__);
		goto RELEASE;
	}

	if (init_dma_handle(&ptDmaDesc, &ptDmaHandle)) {
		print_msg("[%s] init_dma_handle failed!\n", __func__);
		goto RELEASE;
	}

	memset(&tSrcCntInfo, 0, sizeof tSrcCntInfo);
	memset(&tSsmBuf, 0, sizeof tSsmBuf);
	memset(atDispBuf, 0, sizeof atDispBuf);

	for (int i = 0; i < VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE; i++) {
		atDispBuf[i].apdwData[0] = MemBroker_GetMemory(dwYUVSize, VMF_ALIGN_TYPE_8_BYTE);
		if (!atDispBuf[i].apdwData[0]) {
			print_msg("[%s] allocate display buffer failed!\n", __func__);
			goto RELEASE;
		}
		atDispBuf[i].apdwData[1] = atDispBuf[i].apdwData[0] + dwYSize;
		atDispBuf[i].apdwData[2] = atDispBuf[i].apdwData[1] + (dwYSize >> 2);
		atPhysAddr[i] = MemBroker_GetPhysAddr(atDispBuf[i].apdwData[0]);
	}

	tSrcCntInfo.dwCodecType = g_eCodecType;
	if (VMF_BIND_Request(g_ptBind, g_dwWidth, g_dwHeight, 0, 0, &tSrcCntInfo)) {
		print_msg("[%s] VMF_BIND_Request failed!\n", __func__);
		exit(EXIT_FAILURE);
	}
	g_dwSubIdx = tSrcCntInfo.bUseResizedSrc ? 1 : 0;

	ptSsmHandle = SSM_Reader_Init(tSrcCntInfo.szSrcPin, tSrcCntInfo.bIsSsmShared);
	if (!ptSsmHandle) {
		print_msg("[%s] SSM_Reader_Init failed!\n", __func__);
		goto RELEASE;
	}

	if (init_text_overlay()) {
		print_msg("[%s] init_text_overlay failed\n", __func__);
		goto RELEASE;
	}

	pthread_create(&thrd_g, NULL, gui_loop, ptDispHandle);
	pthread_setname_np(thrd_g, "gui_loop");

	while(!g_bTerminate) {
		uint8_t *pbySrc, *pbyDst;
		SSM_Reader_ReturnReceiveBuff(ptSsmHandle, &tSsmBuf);
		pbySrc = MemBroker_GetPhysAddr(tSsmBuf.buffer + VMF_MAX_SSM_HEADER_SIZE);
		pbyDst = atPhysAddr[dwDispQIdx];
		if (dma_upadate_mem(pbyDst, pbySrc, dwYUVSize, ptDmaDesc, ptDmaHandle)) {
			print_msg("[%s] dma_upadate_mem failed!\n", __func__);
			goto RELEASE;
		}
		VMF_VDISP_ProcessOneFrame(ptDispHandle, &atDispBuf[dwDispQIdx], &dwDispQIdx);
	}

RELEASE:
	if (thrd_g) {
		VMF_VDISP_PIP_Stop(ptDispHandle);
		pthread_join(thrd_g, NULL);
	}

	if (ptSsmHandle) {
		SSM_Reader_ReturnBuff(ptSsmHandle, &tSsmBuf);
		SSM_Release(ptSsmHandle);
	}

	if (ptDispHandle)
		VMF_VDISP_Release(ptDispHandle);

	for (int i = 0; i < VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE; i++) {
		if (atDispBuf[i].apdwData[0])
			MemBroker_FreeMemory(atDispBuf[i].apdwData[0]);
	}

	if (ptDmaDesc)
		VMF_DMA_Descriptor_Destroy(ptDmaDesc);

	if (ptDmaHandle)
		VMF_DMA_Release(ptDmaHandle);

	return NULL;
}

void *gui_loop(void *data)
{
	VMF_VDISP_HANDLE_T *disp_handle = data;
	unsigned int disp_buf_size;
	lv_color_t *buf;
	lv_disp_buf_t disp_buf;
	lv_disp_drv_t disp_drv;
	lv_indev_drv_t indev_drv;
	lv_indev_t *my_indev;

	/*LittlevGL init, allocate memory space as pool*/
	lv_init(pool_size);

	/*Setup screen parameters*/
	lv_scr_setup(disp_handle, gui_width, gui_height, gui_x, gui_y, yuv_device);

	/*Linux v4l2 dev init*/
	v4l2dev_init();

	/*Init display buffer, allocate buffer for screen-flushing*/
	disp_buf_size = 10 * lv_scr_get_hor_res();
	buf = malloc(disp_buf_size * sizeof *buf);

	/*Initialize a descriptor for the buffer*/
	lv_disp_buf_init(&disp_buf, buf, NULL, disp_buf_size);

	/*Initialize and register a display driver*/
	lv_disp_drv_init(&disp_drv);
	disp_drv.buffer = &disp_buf;
	disp_drv.flush_cb = v4l2dev_flush;
	lv_disp_drv_register(&disp_drv);

	/*Draw the menu on screen*/
	draw_screen();

	/*Initialize and register a input driver*/
	lv_indev_drv_init(&indev_drv);
	indev_drv.type = LV_INDEV_TYPE_BUTTON;
	indev_drv.read_cb = button_read;
	my_indev = lv_indev_drv_register(&indev_drv);
	lv_indev_set_button_points(my_indev, positons);

	/*Flush screen*/
	while (!g_bTerminate) {
		lv_task_handler();
		usleep(5000);
	}

	/*Release display buffer*/
	free(buf);

	/*Linux v4l2 dev release*/
	v4l2dev_release();

	/*LittlevGL release*/
	lv_release();

	return NULL;
}

void draw_screen(void)
{
	lv_obj_t *scr = NULL;
	static lv_style_t button_style_rel;
	static lv_style_t button_style_pre;
	uint32_t btn_w = lv_scr_get_hor_res() / 2;
	uint32_t btn_h = lv_scr_get_ver_res() / 8;

	/*Init screen*/
	scr = lv_disp_get_scr_act(NULL);
	lv_obj_clean(scr);
	lv_obj_set_style(scr, &lv_style_transp);

	/*Init button styles*/
	lv_style_copy(&button_style_rel, &lv_style_btn_rel);
	lv_style_copy(&button_style_pre, &lv_style_btn_pr);
	button_style_rel.body.opa = LV_OPA_TRANSP;
	button_style_rel.body.border.opa = LV_OPA_TRANSP;
	button_style_rel.text.color = LV_COLOR_WHITE;
	button_style_pre.body.opa = LV_OPA_TRANSP;
	button_style_pre.body.border.opa = LV_OPA_TRANSP;
	button_style_pre.text.color = LV_COLOR_YELLOW;
//	button_style_pre.text.font = &lv_font_roboto_16;

	for (int i = 0; i < 4; i++) {
		lv_obj_t *btn;
		char name[32];
		lv_obj_t *label;
		uint32_t offset = btn_h * 2 + i * btn_h;
		lv_area_t coords;
		/*Init buttons*/
		btn = lv_btn_create(scr, NULL);
		lv_btn_set_style(btn, LV_BTN_STYLE_REL, &button_style_rel);
		lv_btn_set_style(btn, LV_BTN_STYLE_PR,  &button_style_pre);
		lv_obj_set_size(btn, btn_w, btn_h);
		lv_obj_set_event_cb(btn, button_event);
		lv_obj_align(btn, NULL, LV_ALIGN_IN_TOP_MID, 0, offset);
		lv_obj_get_coords(btn, &coords);
		positons[i].x = (coords.x1 + coords.x2) >> 1;
		positons[i].y = (coords.y1 + coords.y2) >> 1;
		/*Init labels*/
		strncpy(name, btn_txt[page_index][i], 32);
		label = lv_label_create(btn, NULL);
		lv_label_set_text(label, name);
	}
}


bool button_read(lv_indev_drv_t *drv __attribute__((unused)), lv_indev_data_t *data)
{
	static uint32_t last_btn = 0; /*Store the last indicated button*/
	int curr_btn = get_button_index();
	if (curr_btn >= 0) { /*Is there a button indicated? (-1 means no button was indicated)*/
		last_btn = curr_btn; 
		data->state = LV_INDEV_STATE_PR;
	} else {
		data->state = LV_INDEV_STATE_REL;
	}
	data->btn_id = last_btn;
	return false;
}


/* * This fuction reads external hardware button ID (0, 1, 2, ...)
 * * msg_callback is used to simulate external buttons*/
int get_button_index(void)
{
	return btn_index;
}

/* * This fuction reads external hardware button status (pressed/unpressed).
 * * msg_callback is used to simulate external buttons*/
bool get_button_status(void)
{
	return btn_status;
}


/* * This fuction resets external hardware button to unpressed. */
void reset_button_status(void)
{
	btn_status = false;
}

void button_event(lv_obj_t *obj __attribute__((unused)), lv_event_t event)
{
	if (get_button_status() && event == LV_EVENT_PRESSING){
		int ret = 0;
		int curr_idx = get_button_index();
		int swap_idx = (page_index == 0) ? 0 : 3;
		/*Do something*/
		if (curr_idx == swap_idx) {
			page_index = (page_index == 0) ? 1 : 0;
			draw_screen();
			ret |= set_text_overlay(HIDE);
			//	release_text_overlay();
		} else {
			strncpy(text_string, ovl_txt[page_index][curr_idx], 32);
			ret |= set_text_overlay(SHOW);
		}
		print_msg("Button %d is selected and pressed.\n", curr_idx);
		reset_button_status();
	}
}

void msg_callback(MsgContext *msg_context, void *user_data __attribute__((unused)))
{
	print_msg("msg_context->pszHost=%s, msg_context->pszCmd=%s \n", 
			msg_context->pszHost, msg_context->pszCmd);

	if (!strcasecmp(msg_context->pszHost, MODULE_NAME)) {
		if (!strcasecmp(msg_context->pszCmd, MSG_LVGL_BTN)) {
			btn_index = *(int *) msg_context->pbyData;
			btn_status = false;
		} else if (!strcasecmp(msg_context->pszCmd, MSG_LVGL_ENT)) {
			btn_status = true;
		} else if (!strcasecmp(msg_context->pszCmd, MSG_LVGL_RST)) {
			btn_index = -1;
		}
	}

	if (msg_context->bHasResponse) {
		msg_context->dwDataSize = 0;
	}
}

int set_text_overlay(TEXT_MODE eMode)
{
	VMF_OVERLAY_TEXT_CONFIG_T tTxtConfig;
	VMF_OVERLAY_CONFIG_T tOvlConfig;

	memset(&tTxtConfig, 0, sizeof tTxtConfig);
	memset(&tOvlConfig, 0, sizeof tOvlConfig);
	
	tTxtConfig.dwPosX = 0;
	tTxtConfig.dwPosY = 0;
	tTxtConfig.dwAlign = VMF_TEXT_ALIGN_TOP | VMF_TEXT_ALIGN_RIGHT;
	tTxtConfig.pszText = text_string;

	//tOvlConfig->eMode = VMF_OVERLAY_MONO;
	tOvlConfig.eMode = VMF_OVERLAY_MONO;
	tOvlConfig.pszDatetimeFormat =  NULL;
	tOvlConfig.dwDatetimePosX = 0;
	tOvlConfig.dwDatetimePosY = 0;
	tOvlConfig.dwDateTimeAlign = VMF_TEXT_ALIGN_TOP | VMF_TEXT_ALIGN_LEFT;
	tOvlConfig.dwTextCount = eMode;
	tOvlConfig.ptTextArray = &tTxtConfig;

	return VMF_VSRC_ConfigOverlay(g_ptVsrcHandle, 0, g_dwSubIdx, &tOvlConfig);
}


int init_text_overlay(void)
{
	FONT_INFO_T tInfo;
	memset(&tInfo, 0, sizeof tInfo);
	memset(text_string, 0, sizeof text_string);
	tInfo.pszFontPath = "DejaVuSans-Bold.ttf";
	tInfo.nFontSize = g_dwFontSize;
	tInfo.fOutlineWidth = 2; // set 0, if outline is not needed
	tInfo.tColorInfo.dwFontColor = 255 | (128<<8) | (128<<16);// Byte 0:y, 1:u, 2:v
	tInfo.tColorInfo.dwBorderColor = 0 | (128<<8) | (128<<16); // Byte 0:y, 1:u, 2:v
	tInfo.tColorInfo.dwBackColor = 0; // Byte 0:y, 1:u, 2:v, 3:alpha, set 0 as transparent background
	return VMF_VSRC_SetFont(g_ptVsrcHandle, 0, g_dwSubIdx, &tInfo);
}

int main(int argc, char **argv)
{
	int ch;
	pthread_t thrd_v;
	
	atexit(exit_handler);
	signal(SIGTERM, sig_handler);
	signal(SIGINT, sig_handler);

	while ((ch = getopt(argc, argv, "c:h")) != -1) {
		switch(ch) {
		case 'c':
			g_szConfig = strdup(optarg);
			break;
		case 'h':
		default:
			print_msg("Usage:%s [-c<config_file)][-h]\n", argv[0]);
			return -1;
		}
	}

	load_config();

	if (init_video_source()) {
		print_msg("[%s] init_video_source failed!\n", __func__);
		goto RELEASE;
	}

	if (0 && init_fec_mode()) {
		print_msg("[%s] init_fec_mode failed!\n", __func__);
		goto RELEASE;
	}

	if (init_bind()) {
		print_msg("[%s] init_bind failed!\n", __func__);
		goto RELEASE;
	}

	pthread_create(&thrd_v, NULL, video_loop, NULL);
	pthread_setname_np(thrd_v, "video_loop");

	MsgBroker_RegisterMsg(LVGL_CMD_FIFO);
	MsgBroker_Run(LVGL_CMD_FIFO, msg_callback, NULL, &g_bTerminate);
	MsgBroker_UnRegisterMsg();

	pthread_join(thrd_v, NULL);

RELEASE:
	release_bind();

	release_video_source();

	return 0;
}
