#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "cJSON.h"
#include "utils.h"

#define GET_JSON_VALUE(n, f) \
    ((node = cJSON_GetObjectItem(n, #f)) == NULL) ? 0 : node->valueint

#define GET_JSON_STRING(n, f) \
    ((node = cJSON_GetObjectItem(n, #f)) == NULL) ? 0 : node->valuestring

#define STRCPY_JSON_STRING(n, f, out) \
    ((node = cJSON_GetObjectItem(n, #f)) == NULL) ? 0 : strcpy(out, node->valuestring)

static inline void get_cfg_string(char *dst, char *src)
{
	if (src == NULL)
		return;
	strcpy(dst, src);
}

int read_yuv420_file(const char *filename, char *addr0, char *addr1, uint32_t y_size)
{
	FILE *Fd = NULL;
	if (filename == NULL || addr0 == NULL || y_size == 0) {
		printf("ERR(%s):null param.\n", __func__);
		return -1;
	}

	Fd = fopen(filename, "r");
	char *buffer = NULL;

	if (Fd == NULL) {
		printf("ERR(%s):open(%s) fail\n", __func__, filename);
		return -1;
	}

	buffer = (char *)malloc(y_size + y_size / 2);
	if (fread(buffer, 1, y_size, Fd) != y_size) {
		printf("read bin(%s) to addr fail\n", filename);
		return -1;
	}

	if (fread(buffer + y_size, 1, y_size / 2, Fd) != y_size / 2) {
		printf("read bin(%s) to addr fail\n", filename);
		return -1;
	}

	memcpy(addr0, buffer, y_size);
	memcpy(addr1, buffer + y_size, y_size / 2);

	fflush(Fd);

	if (Fd)
		fclose(Fd);
	if (buffer) {
		free(buffer);
	}

	return 0;
}

int32_t load_file_2_buff(const char *path, char *filebuff, int32_t size)
{
	FILE *file = NULL;
	struct stat statbuf;
	uint32_t size_read = 0;

	file = fopen(path, "r");
	if (NULL == file) {
		printf("file %s open failed\n", path);
		return -1;
	}
	stat(path, &statbuf);
	if (0 == statbuf.st_size) {
		printf("read file size is zero:%s, %ld, %d\n", path, statbuf.st_size, size);
		fclose(file);
		return -1;
	}
	size_read = fread(filebuff, 1, size, file);
	fclose(file);
	return size_read;
}


int dumpToFile2plane(char *filename, char *srcBuf, char *srcBuf1, unsigned int size, unsigned int size1)
{
	FILE *yuvFd = NULL;
	char *buffer = NULL;

	yuvFd = fopen(filename, "w+");

	if (yuvFd == NULL) {
		printf("open(%s) fail", filename);
		return -1;
	}

	buffer = (char *)malloc(size + size1);

	if (buffer == NULL) {
		printf("ERR:malloc file");
		fclose(yuvFd);
		return -1;
	}

	memcpy(buffer, srcBuf, size);
	memcpy(buffer + size, srcBuf1, size1);

	fflush(stdout);
	fwrite(buffer, 1, size + size1, yuvFd);
	fflush(yuvFd);

	if (yuvFd)
		fclose(yuvFd);
	if (buffer)
		free(buffer);

	return 0;
}


int stitch_cfg_parser(const char *cfg_file, stitch_test_ctx_t *res_cfg)
{
	struct stat statbuf;
	char *filebuf = NULL;
	FILE *p_file = NULL;

	cJSON *root = NULL;
	cJSON *node = NULL;
	cJSON *base_cfg = NULL;
	cJSON *roi_region = NULL;
	cJSON *p_node_roi = NULL;

	if (cfg_file == NULL || res_cfg == NULL) {
		printf("stitch_cfg_parser -> null param set\n");
		return -1;
	}

	p_file = fopen(cfg_file, "r");

	if (p_file == NULL) {
		printf("stitch_cfg_parser open %s fail!!", cfg_file);
		return -1;
	}

	stat(cfg_file, &statbuf);

	if (0 == statbuf.st_size) {
		printf("cfg_file size is zero !!\n");
		fclose(p_file);
		return -1;
	}

	filebuf = (char *)malloc(statbuf.st_size);
	if (NULL == filebuf) {
		printf("vio malloc buff fail !!");
		fclose(p_file);
		return -1;
	}
	memset(filebuf, 0, statbuf.st_size);

	fread(filebuf, statbuf.st_size, 1, p_file);

	root = cJSON_Parse((const char *)filebuf);
	if (NULL == root) {
		printf("Parse stitch_cfg_parser json failed!");
		fclose(p_file);
		free(filebuf);
		return -1;
	}

	base_cfg = cJSON_GetObjectItem((cJSON *)root, "base");
	if (NULL == base_cfg) {
		printf("parse base cfg fail\n");
		fclose(p_file);
		free(filebuf);
		cJSON_Delete(root);
		return -1;
	} else {
		res_cfg->src_nums = GET_JSON_VALUE(base_cfg, src_nums);
		res_cfg->src_w[0] = GET_JSON_VALUE(base_cfg, src0_w);
		res_cfg->src_h[0] = GET_JSON_VALUE(base_cfg, src0_h);

		res_cfg->src_w[1] = GET_JSON_VALUE(base_cfg, src1_w);
		res_cfg->src_h[1] = GET_JSON_VALUE(base_cfg, src1_h);

		res_cfg->src_w[2] = GET_JSON_VALUE(base_cfg, src2_w);
		res_cfg->src_h[2] = GET_JSON_VALUE(base_cfg, src2_h);

		res_cfg->src_w[3] = GET_JSON_VALUE(base_cfg, src3_w);
		res_cfg->src_h[3] = GET_JSON_VALUE(base_cfg, src3_h);

		res_cfg->dst_w = GET_JSON_VALUE(base_cfg, dst_w);
		res_cfg->dst_h = GET_JSON_VALUE(base_cfg, dst_h);
		res_cfg->mode_ctrl.obuf_num = GET_JSON_VALUE(base_cfg, output_buf_num);
		get_cfg_string(res_cfg->test_image_file[0], GET_JSON_STRING(base_cfg, src0_file));
		get_cfg_string(res_cfg->test_image_file[1], GET_JSON_STRING(base_cfg, src1_file));
		get_cfg_string(res_cfg->test_image_file[2], GET_JSON_STRING(base_cfg, src2_file));
		get_cfg_string(res_cfg->test_image_file[3], GET_JSON_STRING(base_cfg, src3_file));
		get_cfg_string(res_cfg->alpha_file, GET_JSON_STRING(base_cfg, alpha_file));
		get_cfg_string(res_cfg->beta_file, GET_JSON_STRING(base_cfg, beta_file));
		res_cfg->alpha_file_size = GET_JSON_VALUE(base_cfg, alpha_file_size);
		res_cfg->beta_file_size = GET_JSON_VALUE(base_cfg, beta_file_size);
	}

	for (int i = 0; i < res_cfg->src_nums; i++) {
		if (res_cfg->src_w[i] == 0 || res_cfg->src_h[i] == 0) {
			printf("invaild stitch res_cfg->src_w[%d] = %d res_cfg->src_h[%d] = %d\n", i,
					res_cfg->src_w[i], i, res_cfg->src_h[i]);
			fclose(p_file);
			free(filebuf);
			cJSON_Delete(root);
			return -1;
		}
	}

	roi_region = cJSON_GetObjectItem((cJSON *)root, "roi_region");
	if (NULL == roi_region) {
		printf("parse roi_region fail\n");
		fclose(p_file);
		free(filebuf);
		cJSON_Delete(root);
		return -1;
	}
	cJSON *p_node_array = cJSON_GetObjectItem(roi_region, "roi");
	int count = cJSON_GetArraySize(p_node_array);

	if (NULL == p_node_array) {
		printf("no roi output node found");
		fclose(p_file);
		free(filebuf);
		cJSON_Delete(root);
		return -1;
	}

	res_cfg->roi_num = count;

	if (res_cfg->roi_num <= 0 || res_cfg->roi_num > 12 /*STITCH_MAX_ROI*/) {
		printf("invaild stitch roi num %d for (0, %u]]\n", res_cfg->roi_num, 12 /*STITCH_MAX_ROI*/);
		fclose(p_file);
		free(filebuf);
		cJSON_Delete(root);
		return -1;
	}
	for (int i = 0; i < count; i++) {
		p_node_roi = cJSON_GetArrayItem(p_node_array, i);
		res_cfg->roi_cfg[i].index = i;

		//src0 info
		res_cfg->roi_cfg[i].src_rect.src0_idx = GET_JSON_VALUE(p_node_roi, src0_idx);
		if (res_cfg->roi_cfg[i].src_rect.src0_idx >= 4 /*STITCH_SRC_IMAGE_CH*/) {
			printf("invaild res_cfg->roi_cfg[%d].src_rect.src0_idx = %d \n", i,
					res_cfg->roi_cfg[i].src_rect.src0_idx);
			fclose(p_file);
			free(filebuf);
			cJSON_Delete(root);
			return -1;
		}
		res_cfg->roi_cfg[i].src_rect.src0_w = res_cfg->src_w[res_cfg->roi_cfg[i].src_rect.src0_idx];
		res_cfg->roi_cfg[i].src_rect.src0_h = res_cfg->src_h[res_cfg->roi_cfg[i].src_rect.src0_idx];
		res_cfg->roi_cfg[i].src_rect.src0_x = GET_JSON_VALUE(p_node_roi, src0_x);
		res_cfg->roi_cfg[i].src_rect.src0_y = GET_JSON_VALUE(p_node_roi, src0_y);

		//src1 info
		res_cfg->roi_cfg[i].src_rect.src1_idx = GET_JSON_VALUE(p_node_roi, src1_idx);
		if (res_cfg->roi_cfg[i].src_rect.src1_idx >= 4 /*STITCH_SRC_IMAGE_CH*/) {
			printf("invaild res_cfg->roi_cfg[%d].src_rect.src1_idx = %d \n", i,
					res_cfg->roi_cfg[i].src_rect.src1_idx);
			{
				fclose(p_file);
				free(filebuf);
				cJSON_Delete(root);
				return -1;
			}
		}
		res_cfg->roi_cfg[i].src_rect.src1_w = res_cfg->src_w[res_cfg->roi_cfg[i].src_rect.src1_idx];
		res_cfg->roi_cfg[i].src_rect.src1_h = res_cfg->src_h[res_cfg->roi_cfg[i].src_rect.src1_idx];
		res_cfg->roi_cfg[i].src_rect.src1_x = GET_JSON_VALUE(p_node_roi, src1_x);
		res_cfg->roi_cfg[i].src_rect.src1_y = GET_JSON_VALUE(p_node_roi, src1_y);

		//dts info
		res_cfg->roi_cfg[i].dst_rect.dst_xpos = GET_JSON_VALUE(p_node_roi, dst_x);
		res_cfg->roi_cfg[i].dst_rect.dst_ypos = GET_JSON_VALUE(p_node_roi, dst_y);
		res_cfg->roi_cfg[i].dst_rect.roi_w = GET_JSON_VALUE(p_node_roi, width);
		res_cfg->roi_cfg[i].dst_rect.roi_h = GET_JSON_VALUE(p_node_roi, height);
		res_cfg->roi_cfg[i].dst_rect.dst_w = res_cfg->dst_w;
		res_cfg->roi_cfg[i].dst_rect.dst_h = res_cfg->dst_h;

		//ctl info
		res_cfg->roi_cfg[i].ctrl.mode = GET_JSON_VALUE(p_node_roi, mode);
		res_cfg->roi_cfg[i].ctrl.uven = GET_JSON_VALUE(p_node_roi, UVen);
		res_cfg->roi_cfg[i].ctrl.direct = GET_JSON_VALUE(p_node_roi, direct);
		res_cfg->roi_cfg[i].ctrl.margin = GET_JSON_VALUE(p_node_roi, margin);
		res_cfg->roi_cfg[i].ctrl.margin_inv = GET_JSON_VALUE(p_node_roi, margin_inv);
		;

		//src 0 1 yuv gain info
		res_cfg->roi_cfg[i].src_gain.src0_gain_y = GET_JSON_VALUE(p_node_roi, src0_gain_y);
		res_cfg->roi_cfg[i].src_gain.src1_gain_y = GET_JSON_VALUE(p_node_roi, src1_gain_y);

		res_cfg->roi_cfg[i].src_gain.src0_gain_u = GET_JSON_VALUE(p_node_roi, src0_gain_u);
		res_cfg->roi_cfg[i].src_gain.src1_gain_u = GET_JSON_VALUE(p_node_roi, src1_gain_u);

		res_cfg->roi_cfg[i].src_gain.src0_gain_v = GET_JSON_VALUE(p_node_roi, src0_gain_v);
		res_cfg->roi_cfg[i].src_gain.src1_gain_v = GET_JSON_VALUE(p_node_roi, src1_gain_v);
	}

	fclose(p_file);
	free(filebuf);
	cJSON_Delete(root);
	return 0;
}

void show_cfg(stitch_test_ctx_t *res_cfg)
{
	int32_t i;

	for (i = 0; i < res_cfg->roi_num; i++) {
		printf("roi_cfg[%d].src_rect.src0_idx = %d \n", i, res_cfg->roi_cfg[i].src_rect.src0_idx);
		printf("roi_cfg[%d].src_rect.src0_w = %d \n", i, res_cfg->roi_cfg[i].src_rect.src0_w);
		printf("roi_cfg[%d].src_rect.src0_h = %d \n", i, res_cfg->roi_cfg[i].src_rect.src0_h);
		printf("roi_cfg[%d].src_rect.src0_x = %d \n", i, res_cfg->roi_cfg[i].src_rect.src0_x);
		printf("roi_cfg[%d].src_rect.src0_y = %d \n", i, res_cfg->roi_cfg[i].src_rect.src0_y);

		printf("roi_cfg[%d].src_rect.src1_idx = %d \n", i, res_cfg->roi_cfg[i].src_rect.src1_idx);
		printf("roi_cfg[%d].src_rect.src1_w = %d \n", i, res_cfg->roi_cfg[i].src_rect.src1_w);
		printf("roi_cfg[%d].src_rect.src1_h = %d \n", i, res_cfg->roi_cfg[i].src_rect.src1_h);
		printf("roi_cfg[%d].src_rect.src1_x = %d \n", i, res_cfg->roi_cfg[i].src_rect.src1_x);
		printf("roi_cfg[%d].src_rect.src1_y = %d \n", i, res_cfg->roi_cfg[i].src_rect.src0_y);

		printf("roi_cfg[%d].dst_rect.dst_xpos = %d \n", i, res_cfg->roi_cfg[i].dst_rect.dst_xpos);
		printf("roi_cfg[%d].dst_rect.dst_ypos = %d \n", i, res_cfg->roi_cfg[i].dst_rect.dst_ypos);
		printf("roi_cfg[%d].dst_rect.roi_w = %d \n", i, res_cfg->roi_cfg[i].dst_rect.roi_w);
		printf("roi_cfg[%d].dst_rect.roi_h = %d \n", i, res_cfg->roi_cfg[i].dst_rect.roi_h);
		printf("roi_cfg[%d].dst_rect.dst_w = %d \n", i, res_cfg->roi_cfg[i].dst_rect.dst_w);
		printf("roi_cfg[%d].dst_rect.dst_h = %d \n", i, res_cfg->roi_cfg[i].dst_rect.dst_h);

		printf("roi_cfg[%d].ctrl.mode = %d \n", i, res_cfg->roi_cfg[i].ctrl.mode);
		printf("roi_cfg[%d].ctrl.uven = %d \n", i, res_cfg->roi_cfg[i].ctrl.uven);
		printf("roi_cfg[%d].ctrl.direct = %d \n", i, res_cfg->roi_cfg[i].ctrl.direct);
	}
}