/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2019 AXELL CORPORATION, all rights reserved.
 */
#include "kernel.h"
#include "ffsys.h"
#include "sample_common.h"
#include "jpg_sample.h"
#include "jpg/jpg.h"

/* プロトタイプ */
static int32_t init_module(void);
static int32_t term_module(void);

extern void encode_main(uint8_t mode, uint32_t param);
extern void decode_main(uint8_t mode, uint32_t param);

/**
 * @brief	メイン処理
 * @param	param [in] 制御パラメータ
 * @return	none
 * @note	none
*/
void JPG_main(uint8_t param)
{
	int32_t		rc = AG903_ENONE;
	uint8_t		mode = 0;
	char		input[2+2];

	((void)param);

	rc = init_module();

	while ((rc == AG903_ENONE) && (mode != 0xFF)) {

		switch (mode) {
			case 0x01: { encode_main(0, 0); break; }
			case 0x02: { encode_main(1, 0); break; }
			case 0x03: { encode_main(2, 0); break; }
			case 0x04: { encode_main(3, 0); break; }

			case 0x05: { decode_main(0, 0); break; }
			case 0x06: { decode_main(1, 0); break; }
			case 0x07: { decode_main(2, 0); break; }
			case 0x00:
			default:
				JPG_Printf("\t# 00 ... Help\r\n");
				JPG_Printf("\t# 01 ... Encode(YUV to JPEG)\r\n");
				JPG_Printf("\t# 02 ... Encode(RGB to JPEG)\r\n");
				JPG_Printf("\t# 03 ... Encode(CAPTURE to JPEG)\r\n");
				JPG_Printf("\t# 04 ... Encode(YUV to JPEG Repeat)\r\n");

				JPG_Printf("\t# 05 ... Decode(JPEG to YUV)\r\n");
				JPG_Printf("\t# 06 ... Decode(Display image)\r\n");
				JPG_Printf("\t# 07 ... Decode(JPEG to YUV Repeat & Display image)\r\n");

				JPG_Printf("\t# FF ... Exit Sample\r\n");
				break;
		}

		if (rc == AG903_ENONE) {
			JPG_Printf(" -- Choose sample number (00h<HELP> to FFh): ");
			COM_GetNum_Wait(input, sizeof(input));

			mode = (ASCtoBIN(input[0]) << 4)
				 | (ASCtoBIN(input[1]) << 0);
		}
	}
	rc = term_module();

	return;
}

/**
 * @brief	サブ処理
 * @param	param [in] 制御パラメータ
 * @return	none
 * @note	none
*/
void JPG_sub(uint32_t param)
{
	((void)param);
	return;
}

/**
 * @brief	CRLF入力待ち
 * @param	none
 * @return	none
 * @note	none
*/
void JPG_wait(void)
{
	char	code[2];

	COM_GetNum_Wait(code, sizeof(code));
	return;
}

/**
 * @brief	モジュール初期化
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
*/
static int32_t init_module(void)
{
	int32_t				rc = AG903_ENONE;

	AG903_JPGInit();
	
	rc = sys_meminit(AG903_SAMPLE_VRAM_ALLOC_SIZE,
				 (void *)AG903_SAMPLE_VRAM_ADDR,
				 SYSMEM_NORMAL_CACHE_OFF);
	if (AG903_ENONE != rc) {
		JPG_ErrPrintf("ERROR Buffer allocation failure\r\n");
	}

	rc = mountp('A', 0, (ID)Fsif_GetDevid(FSIF_DEV_SD));
	if (AG903_ENONE != rc) {
		JPG_ErrPrintf("ERROR Device Mount\r\n");
	}

	return rc;
}

/**
 * @brief	モジュール終了
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @note	none
*/
static int32_t term_module(void)
{
	sys_memfinal();
	umountp(0, (ID)Fsif_GetDevid(FSIF_DEV_SD));

	return AG903_ENONE;
}

/**
 * @brief	ファイルサイズ取得
 * @param	fname  [in]  ファイル名
 * @param	fsize  [out] ファイルサイズ
 * @return	AG903_ENONE		正常終了
 * @return	-AG903_EFAULT	処理失敗
 * @note	対象デバイスはmount済みであること。
*/
int32_t jpg_GetFileSize(char* fname, uint32_t* fsize)
{
	int			ret;
	struct stat	fsts;
	
	/* 既にmount済みである事 */

	(*fsize) = 0;

	ret = stat((const char *)fname, &fsts);
	if(0 != ret) {
		return -AG903_EFAULT;
	}

	(*fsize) = fsts.st_size;

	return AG903_ENONE;
}

/**
 * @brief	ファイルリード (Open,Close処理含む)
 * @param	fname	[in]  ファイル名
 * @param	buf		[out] リードデータ
 * @param	size	[in]  リードサイズ
 * @return	AG903_ENONE		正常終了
 * @return	-AG903_EFAULT	処理失敗
 * @note	対象デバイスはmount済みであること。
 * @note	内部でファイルをOpen、Closeします。
*/
int32_t jpg_OpenRead(char* fname, uint8_t* buf, uint32_t fsize)
{
	FILE		*fp = NULL;
	int			rc;
	size_t		rsize;
	
	/* 既にmount済みである事 */
	
	/* ファイルOpen */
	fp = fopen((const char *)fname, "rb");
	if(NULL == fp) {
		return -AG903_ENOENT;
	}

	/* ファイルRead */
	rsize = fread((void *)buf, 1, fsize, fp);
	if(rsize != fsize) {
		return -AG903_EFAULT;
	}
	
	/* ファイルClose */
	rc = fclose(fp);
	if(0 != rc) {
		return -AG903_EFAULT;
	}

	return AG903_ENONE;
}

/**
 * @brief	ファイルライト (Open,Close処理含む)
 * @param	fname	[in] ファイル名
 * @param	buf		[in] ライトデータ
 * @param	size	[in] ライトサイズ
 * @return	AG903_ENONE		正常終了
 * @return	-AG903_EFAULT	処理失敗
 * @note	対象デバイスはmount済みであること。
 * @note	内部でファイルをOpen、Closeします。
*/
int32_t jpg_OpenWrite(char* fname, uint8_t* buf, uint32_t fsize)
{
	FILE		*fp = NULL;
	int			rc;
	size_t		wsize;

	/* 既にmount済みである事 */

	/* ファイルOpen */
	fp = fopen((const char *)fname, "wb");
	if(NULL == fp) {
		return -AG903_ENOENT;
	}

	/* ファイルWrite */
	wsize = fwrite(buf, 1, fsize, fp);
	if(wsize != fsize) {
		return -AG903_EFAULT;
	}

	/* ファイルClose */
	rc = fclose(fp);
	if(0 != rc) {
		return -AG903_EFAULT;
	}

	return AG903_ENONE;
}

#define FILE_YUV_HEADER_SIZE	(16)
/**
 * @brief	YUVファイルリード
 * @param	fname  [in]  ファイル名
 * @param	buf    [out] リードデータ
 * @param	fsize  [in]  ファイルサイズ
 * @param	width  [out] 幅
 * @param	height [out] 高さ
 * @return	AG903_ENONE		正常終了
 * @return	-AG903_EFAULT	処理失敗
 * @note	対象デバイスはmount済みであること。
*/
int32_t	jpg_ReadYUV(char* fname, uint8_t* buf, uint32_t fsize, uint32_t* width, uint32_t* height)
{
	FILE		*fp;
	int			rc;
	size_t		rdsize;
	size_t		rsize;
	uint32_t	header[FILE_YUV_HEADER_SIZE/sizeof(uint32_t)];
	
	/* 既にmount済みである事 */
	
	/* ファイルOpen */
	fp = fopen((const char *)fname, "rb");
	if(NULL == fp) {
		return -AG903_ENOENT;
	}

	/* YUVファイルRead */
	rsize = fread((void *)header, 1, FILE_YUV_HEADER_SIZE, fp);
	if(FILE_YUV_HEADER_SIZE != rsize) {
		return -AG903_EFAULT;
	}
	rdsize = fsize - FILE_YUV_HEADER_SIZE;
	rsize = fread((void *)buf, 1, rdsize, fp);
	if(rdsize != rsize) {
		return -AG903_EFAULT;
	}

	/* ファイルClose */
	rc = fclose(fp);
	if(0 != rc) {
		return -AG903_EFAULT;
	}

	(*width)   = header[1];
	(*height)  = header[3];

	return AG903_ENONE;
}

/**
 * @brief	YUVファイルライト
 * @param	fname  [in] ファイル名
 * @param	buf    [in] ライトデータ
 * @param	fsize  [in] ファイルサイズ
 * @param	width  [in] 幅
 * @param	height [in] 高さ
 * @return	AG903_ENONE		正常終了
 * @return	-AG903_EFAULT	処理失敗
 * @note	対象デバイスはmount済みであること。
*/
int32_t	jpg_WriteYUV(char* fname, uint8_t* buf, uint32_t fsize, uint32_t width, uint32_t height)
{
	FILE		*fp;
	int			rc;
	size_t		wsize;
	uint32_t	header[FILE_YUV_HEADER_SIZE/sizeof(uint32_t)] = {0};
	
	/* 既にmount済みである事 */
	
	/* ファイルOpen */
	fp = fopen((const char *)fname, "wb");
	if(NULL == fp) {
		return -AG903_ENOENT;
	}

	/* ファイルヘッダ作成 */
	header[0] = 0x6E656C78; /* "xlen" */
	header[1] = width;
	header[2] = 0x6E656C79; /* "ylen" */
	header[3] = height;
	wsize = fwrite((void*)header, 1, FILE_YUV_HEADER_SIZE, fp);
	if(wsize != FILE_YUV_HEADER_SIZE) {
		return -AG903_EFAULT;
	}

	/* YUVデータWrite */
	wsize = fwrite((void *)buf, 1, fsize, fp);
	if(wsize != fsize) {
		return -AG903_EFAULT;
	}

	/* ファイルClose */
	rc = fclose(fp);
	if(0 != rc) {
		return -AG903_EFAULT;
	}

	return AG903_ENONE;
}


#define FILE_BMP_HEADER_SIZE	(14)
#define FILE_BMP_INFO_SIZE		(40)
typedef struct {
	unsigned char r;
	unsigned char g;
	unsigned char b;
}	RGB;
/**
 * @brief	Bitmapファイルリード
 * @param	fname  [in]  ファイル名
 * @param	buf    [out] リードデータ
 * @param	bufsz  [in]  バッファサイズ
 * @param	dsize  [out] データサイズ
 * @param	width  [out] 幅
 * @param	height [out] 高さ
 * @return	AG903_ENONE		正常終了
 * @return	-AG903_EFAULT	処理失敗
 * @note	対象デバイスはmount済みであること。
 * @note	Windows Bitmapの24bitを対象としたサンプル。
*/
int32_t	jpg_ReadBitmap(char* fname, uint8_t* buf, uint32_t bufsz, uint32_t* dsize, uint32_t* width, uint32_t* height)
{
	FILE		*fp;
	uint8_t		*buff;
	uint8_t		*tmp;
	int			rc;
	size_t		rsize;
	uint32_t	offset, stride;
	uint32_t	ii, jj;
	uint32_t	info[FILE_BMP_INFO_SIZE/sizeof(uint32_t)];
	uint16_t	bitnum;
	uint8_t		header[FILE_BMP_HEADER_SIZE];
	
	/* 既にmount済みである事 */
	
	/* ファイルOpen */
	fp = fopen((const char *)fname, "rb");
	if(NULL == fp) {
		return -AG903_ENOENT;
	}

	/* BitmapヘッダRead */
	rsize = fread((void *)header, 1, FILE_BMP_HEADER_SIZE, fp);
	if(FILE_BMP_HEADER_SIZE != rsize) {
		return -AG903_EFAULT;
	}
	offset  = header[10];
	offset |= header[11] <<  8;
	offset |= header[12] << 16;
	offset |= header[13] << 24;
	
	/* Bitmap情報Read */
	rsize = fread((void *)info, 1, FILE_BMP_INFO_SIZE, fp);
	if(FILE_BMP_INFO_SIZE != rsize) {
		return -AG903_EFAULT;
	}
	bitnum = (uint16_t)(info[3]>>16);
	if(24 != bitnum) {
		return -AG903_EFAULT;
	}
	(*width)  = info[1];
	(*height) = info[2];
	(*dsize)  = info[1] * info[2] * 3;
	stride = (*width)*3 + (*width)%4;
	if(bufsz < ((*dsize)+stride)) {
		return -AG903_EFAULT;
	}

	/* Bitmap画像データRead */
	rc = fseek(fp, offset, FFS_SEEK_SET);
	if(0 != rc) {
		return -AG903_EFAULT;
	}
	/*--- 上下反転 ---*/
	buff = buf;
	tmp  = (buf + (*dsize));
	for(ii = 1;ii <= (*height); ii++) {
		rsize = fread(tmp, 1, stride, fp);
		if(stride != rsize) {
			return -AG903_EFAULT;
		}
		for (jj = 0;jj < (*width); jj++) {
			buff[3*((*height - ii) * *width + jj)+0] = tmp[jj * 3 + 0];
			buff[3*((*height - ii) * *width + jj)+1] = tmp[jj * 3 + 1];
			buff[3*((*height - ii) * *width + jj)+2] = tmp[jj * 3 + 2];
		}
	}

	/* ファイルClose */
	rc = fclose(fp);
	if(0 != rc) {
		return -AG903_EFAULT;
	}

	return AG903_ENONE;
}
