/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2021 AXELL CORPORATION, all rights reserved.
 */
/*
 * @history 2021_06_30  [SDK3.3] OpenVGサンプルでBMUとGFXの連携に関するAPIの引数と解説コメントを修正 (#3528)
 */
#include <stdint.h>
#include <stdbool.h>
#include <math.h>

#include "EGL/eglext_AG903.h"
#include "EGL/egl.h"
#include "VG/openvg.h"
#include "VG/vgext_AG903.h"
#include "vxlib.h"
#include "gmalloc.h"

#include "dsp/dspmgr.h"
#include "bmu/bmumgr.h"
#include "pgp/pgpmgr.h"
#include "pgp/pgpprm.h"
#include "pgp/pgpmgr.h"
#include "via/viamgr.h"

#include "sample_common.h"
#include "gfx_sample.h"
#include "openvg_port_uc3.h"

#include "bmugfx.h"


#define PRINTLN(FMT, ...)			sys_print(0, FMT"\r\n",##__VA_ARGS__)

/* vgExtOutLevelControlEXTまたはvgExtInWaitControlEXTの引数のビット定義 */
#define SNK0_BIT	(1<<(0+0))	/* BMUのGFX0シンクデバイス */
#define SNK1_BIT	(1<<(1+0))	/* BMUのGFX1シンクデバイス */
#define SNK2_BIT	(1<<(2+0))	/* BMUのGFX2シンクデバイス */
#define SNK3_BIT	(1<<(3+0))	/* BMUのGFX3シンクデバイス */
#define SRC0_BIT	(1<<(0+8))	/* BMUのGFX0ソースデバイス */
#define SRC1_BIT	(1<<(1+8))	/* BMUのGFX1ソースデバイス */
#define SRC2_BIT	(1<<(2+8))	/* BMUのGFX2ソースデバイス */
#define SRC3_BIT	(1<<(3+8))	/* BMUのGFX3ソースデバイス */

#define CYCLE	600	// デモが１周するサイクル数
#define DEG2RAD(D)	(2.0*3.14159265*(D)/360.0)

/**
  キャプチャ画像を描画で変形加工して表示します。
  キャプチャ-描画間と描画-表示間にそれぞれバッファ管理が入り
  ハードウェアでリングバッファのようなデータの待ち合わせを行います。
  ソフトウェア的にはデータフロー制御を気にせず描画にだけ集中することができるようになります。
  簡単かつリアルタイムな制御が可能になります。

  ソース側BMU仕様
  コマ落ちを許します。

  データフロー図
  PGP-BMU(src)-GFX-BMU(sink)-DSP

  バッファ管理のページ数の目安:
  バッファ管理のページ数は供給側と消費側のレートがほぼ同じであれば3で十分です。
  それ以下ではコマ飛びする可能性が出てきますが、両者の位相が揃っているか位相差の範囲で供給が完了する場合は2でも問題ありません。
  消費側のスループットが供給側よりも高い場合はページ数を増やすことでコマ飛びを抑制する効果が期待できます。

  バッファ管理で待たせることができないペリフェラルがあります。
  表示、キャプチャは待たせることができません。このようなペリフェラルに対してバッファ管理の動作モード0-1を使用しても期待どおりに動作させることはできません。

*/




static void TestInit(int width, int height)
{
    VGfloat ClearColor[4]  = {0.5F, 0.5F, 0.5F, 1.0F};

    /* 描画サーフェスのクリアカラーを指定 */
    vgSetfv(VG_CLEAR_COLOR, 4, ClearColor);

    /* 描画サーフェス全体をクリア */
    vgClear(0, 0, width, height);
}

static void TestTerm()
{

}

//---------------- Capture ----------------
#define PIPELINE_NUM			1
#define CVBS_NUM				1

static AG903_PgpMgrPipelineHandle	*pipeline_handle[PIPELINE_NUM] = {NULL};
static AG903_ViaMgrInputHandle		*via_handle[CVBS_NUM] = {NULL};
static int32_t						via_port[CVBS_NUM] = {0}; //={0,1,2,3};
// static AG903_PgpMgrDGCLookupTable	*dgc_tables[DGC_TBL_NUM] = {0};

static void pgp_intr_vram(int32_t id, void* handle)
{
	// VRAM出力未完了割り込みのときにパイプラインをリセットして復旧します
	// この割り込みが発生する要因には次のようなものがあります。
	// ・内蔵DRAMの帯域不足
	// ・ビデオ信号の乱れや中断
	// ・無効状態のBMUへの出力
	
	AG903_PgpMgrPipelineHandle	*pgp_handle;
	int32_t						pipeline_no;
	(void)id;

	pgp_handle = (AG903_PgpMgrPipelineHandle*)handle;
	pipeline_no = pgp_handle->pipeline_no;

	AG903_PgpMgrExecPipeline(pgp_handle, AG903_PGP_MGR_CMD_FORCE_QUIT);
	AG903_PgpMgrReleaseAssignment(pgp_handle);

	AG903_PgpMgrSetInputPort(pgp_handle, via_handle[pipeline_no]);
	AG903_PgpMgrForceAssignPipeline(pgp_handle, pipeline_no);
	AG903_PgpMgrExecPipeline(pgp_handle, AG903_PGP_MGR_CMD_EXEC_CONTINUE);
}

int32_t CaptureInit(void)
{
    /* アナログ入力の初期化 */
	Board_SetupCvbs(TEST_CVBS_NTSC_BT);
	AG903_ViaMgrInit();

    /* パイプラインハンドル、アナログビデオ入力ハンドルの取得 */
    AG903_PgpMgrGetPipelineHandle(PIPELINE_NUM, pipeline_handle);
    AG903_ViaMgrGetInputHandle(via_port, CVBS_NUM, via_handle);

    /* アナログビデオ入力の設定 */
	AG903_ViaMgrInputHandle *via0 = via_handle[0];
    AG903_ViaMgrSetInputParameter(via0, AG903_VIA_MGR_FORMAT_NTSC_BT, AG903_VIA_MGR_MODE_COLOR);
	//AG903_ViaMgrSetColorCorrection(via0, 0);

    /* パイプラインの設定 */
	AG903_PgpMgrPipelineHandle	*pgp0 = pipeline_handle[0];
    AG903_PgpMgrSetParamOutputFormat(pgp0,AG903_PGP_MGR_OUTPUT_FMT_YCBCR444_TO_YCBCR422);
	AG903_PgpMgrSetParamInputFormat(pgp0, AG903_PGP_MGR_INPUT_FMT_YCBCR422_W_IP);
	AG903_PgpMgrSetConfigOutputBaseAddr(pgp0, (void *)gfx_bg_srcadr);
    AG903_PgpMgrSetParamOutputAddrDim(pgp0, AG903_PGP_MGR_ADDR_DIMENTION_2);
    AG903_PgpMgrSetParamOutputStride(pgp0, BG_WIDTH*BG_BPP/8);
	AG903_PgpMgrSetParamInputPosition(pgp0, 12, 120); // カメラ依存による微調整が必要
    AG903_PgpMgrSetParamInputSize(pgp0, BG_HEIGHT/2, BG_WIDTH);

	/* イベントトリガ有効 (VLineI, VSyncI, VLineO, VSyncO) */
	// AG903_PgpMgrSetParamInputMode(pgp0, AG903_PGP_MGR_INPUT_MD_VSYNC);
	AG903_PgpMgrSetParamOutputBMU(pgp0, AG903_PGP_MGR_OUTPUT_BMU_ENABLE);
	// AG903_PgpMgrEnableParamTrigCtrl(pgp0, false, false, false, true);
	// AG903_PgpMgrEnableParamTrigCtrl(pgp0, true, true, true, true);	

	// 割り込み
	AG903_PgpMgrSetIntrCallbackFunc(pgp0, AG903_PGP_MGR_INTR_VRAM_ERROR, pgp_intr_vram);

    /* パイプラインの入力ポートへ接続 */
    AG903_PgpMgrSetInputPort(pgp0, via0);
    /* パイプライン割り当て */
	AG903_PgpMgrForceAssignPipeline(pgp0, 0);

	// Start!
	AG903_PgpMgrExecPipeline(pgp0, AG903_PGP_MGR_CMD_EXEC_CONTINUE);

    return 0;
}

static int32_t CaptureTerm(void)
{
    uint32_t status;
	AG903_ViaMgrInputHandle *via0 = via_handle[0];
	AG903_PgpMgrPipelineHandle	*pgp0 = pipeline_handle[0];

    /* 連続キャプチャの停止 */
    AG903_PgpMgrExecPipeline(pgp0, AG903_PGP_MGR_CMD_IDLE);

    /* パイプラインがIDLEになるまで待ち合わせる */
    while (1) {
        AG903_PgpMgrGetPipelineStatus(pgp0, NULL, NULL, NULL, NULL, &status);
        if (status == 0) {
            break;
        }
    }

    /* ハンドル解放 */
    AG903_PgpMgrReleasePipelineHandle(pgp0);
    AG903_ViaMgrReleaseInputHandle(via0);

    return 0;
}

void BmuGfxMain(void)
{
    /* 初期化処理 */
	CaptureInit();
    TestInit(GFX_WIDTH, GFX_HEIGHT);

	// キャプチャ画像とVGImageのデータ構造は格納するラインの順序が上下逆です。
	VGImage directImage =
		vgCreateDirectImageEXT(VG_EXT_YUV422,
							   BG_WIDTH,
							   BG_HEIGHT,
							   VG_IMAGE_QUALITY_FASTER,
							   (const void *)gfx_bg_snkadr,
							   BG_WIDTH*BG_BPP/8);

	// YUV-RGB変換の係数
	static VGint yuv_rgb_coef[9] = {
		0x12A, 0x000, 0x199,
		0x12A, 0x79C, 0x730,
		0x12A, 0x204, 0x000
	}; /* BT.601 Limit */
	vgSetivEXT(VG_EXT_YUV_RGB_COEFFICIENT, 9, yuv_rgb_coef);
	
	// RGB-YUV変換の係数
	// static VGint rgb_yuv_coef[9] = {
	// 	0x107, 0x204, 0x064,
	// 	0x768, 0x6D6, 0x1C1,
	// 	0x1C1, 0x687, 0x7B6
	// }; /* BT.601 Limit */
	// vgSetivEXT(VG_EXT_RGB_YUV_COEFFICIENT, 9, rgb_yuv_coef);

	vgSetiEXT(VG_EXT_YUV_RGB_CONVERSION, (VG_EXT_RGB2YUV_B3_Y1|VG_EXT_RGB2YUV_B2_V|VG_EXT_RGB2YUV_B1_Y0|VG_EXT_RGB2YUV_B0_U|VG_EXT_YUV2RGB_Y0_B1|VG_EXT_YUV2RGB_Y1_B3|VG_EXT_YUV2RGB_V_B2|VG_EXT_YUV2RGB_U_B0));
	
	int fr = 0;
	while (fr <= CYCLE) {
		VGfloat ClearColor[3][4]  = {
			[0] ={0.1F, 0.1F, 0.1F, 1.0F},
			[1] ={0.5F, 0.5F, 0.5F, 1.0F},
			[2] ={0.9F, 0.9F, 0.9F, 1.0F},
		};

		uint16_t bmureq;
		bmureq = 0x0000;

		// DS-1. 川下のBMUへSrcReqを出力します
		bmureq |= SRC0_BIT;
		vgExtOutLevelControlEXT(bmureq, VG_TRUE);
		// DS-2. 川下のBMUからのSrcAckを待ちます
		vgExtInWaitControlEXT(SRC0_BIT);

		// DS-3. 川下のBMUでアドレス変換の準備ができたので内蔵DRAMへ描画可能です

		// US-1. 川上のBMUへSnkReqを出力します(川下へのSrcReqも継続)
		bmureq |= SNK0_BIT;
		vgExtOutLevelControlEXT(bmureq, VG_TRUE);
		// US-2. 川上のBMUからのSnkAckを待ちます
		vgExtInWaitControlEXT(SNK0_BIT);

		// US-3. 川上のBMUでアドレス変換の準備ができたので内蔵DRAMを参照可能です

		// DirectImageの上下を正して少し回転
		vgSetfv(VG_CLEAR_COLOR, 4, ClearColor[0]);
		vgClear(0, 0, GB_WIDTH, GB_HEIGHT);

		vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
		vgLoadIdentity();

		vgScale(1.0, -1.0); // X軸で回転して上下を正す
		vgTranslate(0.0, (VGfloat)-BG_HEIGHT); // その結果、画面の外へ出た画像を引き上げる

		VGfloat deg = 45.0f * sinf(DEG2RAD(360.0f) * (VGfloat)fr/(VGfloat)CYCLE);
		vgRotate(deg);

		vgDrawImage(directImage);
		vgFinish();
		
		// DS-4. 内蔵DRAMへの描画がすべて終了したら川下のへのSrcReqを下げます
		bmureq &= ~SRC0_BIT;
		vgExtOutLevelControlEXT(bmureq, VG_TRUE);

		// US-4. 内蔵DRAMへの描画がすべて終了したら川上へのSnkReqを下げます
		bmureq &= ~SNK0_BIT;
		vgExtOutLevelControlEXT(bmureq, VG_TRUE);


		/* ビデオ信号入力の有無を確認(キャプチャが安定するまで500ms程度必要) */
		if (fr == 60/2) {
			if (!AG903_ViaMgrIsInputValid(via_handle[0])) {
				PRINTLN("No video input signal");
			}
		}

		fr++;
	}

	vgDestroyImage(directImage);

    // /* 終了処理 */
	CaptureTerm();
	TestTerm();
}
