#include "sample.h"

static int write_output_streams(MediaCodecTestContext *ctx, media_codec_buffer_t *outputBuffer)
{
	int32_t ret = 0;

	if (ctx == NULL) {
		return -1;
	}

	if (ctx->context == NULL || ctx->inFile == NULL || ctx->outFile == NULL || outputBuffer == NULL) {
		printf("Invalid parameters.\n");
		return -1;
	}

	fwrite(outputBuffer->vstream_buf.vir_ptr, outputBuffer->vstream_buf.size,
		1, ctx->outFile);

	return ret;
}

static int read_input_frames(MediaCodecTestContext *ctx, media_codec_buffer_t *inputBuffer)
{
	Int32 doRead = TRUE, doRewind = FALSE, ret = 0;
	Uint32 bufIdx = 0, srcIdx = 0;
	void *bufPtr = NULL;
	size_t bufSize = 0;

	if (ctx == NULL) {
		return -1;
	}

	if (ctx->context == NULL || ctx->inFile == NULL ||
		ctx->outFile == NULL || inputBuffer == NULL) {
		printf("Invalid parameters.\n");
		return -1;
	}

	if (ctx->context->video_enc_params.external_frame_buf) {
		// release input buffer and take it as the new input buffer
		for (bufIdx = 0; bufIdx < ctx->context->video_enc_params.frame_buf_count;
			bufIdx++) {
			if (ctx->exFb[bufIdx].valid &&
				ctx->exFb[bufIdx].src_idx == inputBuffer->vframe_buf.src_idx) {
				srcIdx = inputBuffer->vframe_buf.src_idx;
				break;
			}
		}

		if (bufIdx == ctx->context->video_enc_params.frame_buf_count) {
			return -1;
		}
		bufPtr = (void *)ctx->exFb[srcIdx].buf.virt_addr;
		bufSize = inputBuffer->vframe_buf.size;
		inputBuffer->vframe_buf.vir_ptr[0] =
			(hb_u8 *)ctx->exFb[srcIdx].buf.virt_addr;
		inputBuffer->vframe_buf.vir_ptr[1] = (hb_u8 *)
			ctx->exFb[srcIdx].buf.virt_addr + (inputBuffer->vframe_buf.size
			* 2 / 3);
		inputBuffer->vframe_buf.vir_ptr[2] = (hb_u8 *)
			ctx->exFb[srcIdx].buf.virt_addr + (inputBuffer->vframe_buf.size
			* 2 / 3 * 5 / 4);
		inputBuffer->vframe_buf.phy_ptr[0] =
			ctx->exFb[srcIdx].buf.phys_addr;
		inputBuffer->vframe_buf.phy_ptr[1] =
			ctx->exFb[srcIdx].buf.phys_addr + (inputBuffer->vframe_buf.size
			* 2 / 3);
		inputBuffer->vframe_buf.phy_ptr[2] =
			ctx->exFb[srcIdx].buf.phys_addr + (inputBuffer->vframe_buf.size
			* 2 / 3 * 5 / 4);
		inputBuffer->vframe_buf.fd[0] = ctx->exFb[srcIdx].buf.fd;
		if (ctx->readonce != 0) {
			return bufSize;
		}
	} else {
		bufPtr = inputBuffer->vframe_buf.vir_ptr[0];
		bufSize = inputBuffer->vframe_buf.size;
	}

	if (doRead == FALSE) {
		return ret;
	}

	do {
		ret = fread(bufPtr, 1, bufSize, ctx->inFile);
		if (ret <= 0 && doRewind == FALSE) {
			printf("Failed to read input file (size=%lu)\n", bufSize);
		}

		if (ret <= 0 && doRewind == TRUE) {
			if(fseek(ctx->inFile, 0, SEEK_SET)) {
				printf("Failed to rewind input file\n");
				break;
			}
		}
	} while (ret == 0 && doRewind == TRUE);

	if (ctx->qpmap_enable) {
		inputBuffer->vframe_buf.qp_map_valid = 1;
		inputBuffer->vframe_buf.qp_map_array = ctx->qpmap_array;
		inputBuffer->vframe_buf.qp_map_array_count = ctx->qpmap_count;
	}

	return ret;
}

void do_sync_encoding(void *arg)
{
	hb_s32 ret = 0;
	MediaCodecTestContext *ctx = (MediaCodecTestContext *)arg;
	media_codec_context_t *context = NULL;
	media_codec_buffer_t inputBuffer;
	media_codec_buffer_t outputBuffer;
	media_codec_output_buffer_info_t info;

	ret = check_and_init_test(ctx);
	if (ret != 0) {
		printf("check_and_init_test failed(%d).\n", ret);
		return;
	}

	context = ctx->context;
	ret = hb_mm_mc_initialize(context);
	if (ret != 0) {
		printf("hb_mm_mc_initialize failed(%d).\n", ret);
		return;
	}

	// set_message(ctx);

	ret = hb_mm_mc_configure(context);
	if (ret != 0) {
		printf("hb_mm_mc_configure failed(%d).\n", ret);
		hb_mm_mc_release(context);
		return;
	}

	mc_av_codec_startup_params_t startup_params;
	startup_params.video_enc_startup_params.receive_frame_number = 0;
	ret = hb_mm_mc_start(context, &startup_params);
	if (ret != 0) {
		printf("hb_mm_mc_start failed(%d).\n", ret);
		hb_mm_mc_release(context);
		return;
	}

	do {
		// process input buffers
		memset(&inputBuffer, 0x00, sizeof(media_codec_buffer_t));
		ret = hb_mm_mc_dequeue_input_buffer(context, &inputBuffer, 3000);
		if (ret == 0) {
			ret = read_input_frames(ctx, &inputBuffer);
			if (ret <= 0) {
				printf("There is no more input data(ret=%d)!\n", ret);
				inputBuffer.vframe_buf.size = 0;
				inputBuffer.vframe_buf.frame_end = TRUE;
			}

			ctx->input_num++;
			// ASSERT_EQ(do_encode_params_setting(ctx, &inputBuffer), 0);

			ret = hb_mm_mc_queue_input_buffer(context, &inputBuffer, 100);
			if (ret != 0) {
				break;
			}
		} else {
			printf("dequeue input buffer fail(%d).\n", ret);
			if (ret != (int32_t)HB_MEDIA_ERR_WAIT_TIMEOUT) {
				break;
			}
		}

		// process output buffers
		memset(&outputBuffer, 0x00, sizeof(media_codec_buffer_t));
		memset(&info, 0x00, sizeof(media_codec_output_buffer_info_t));
		ret = hb_mm_mc_dequeue_output_buffer(context, &outputBuffer, &info, 3000);
		if (ret == 0) {
			write_output_streams(ctx, &outputBuffer);
			ret = hb_mm_mc_queue_output_buffer(context, &outputBuffer, 100);
			if (outputBuffer.vstream_buf.stream_end) {
				printf("There is no more output data!\n");
				break;
			}
			if (ret) {
				break;
			}
		} else {
			printf("dequeue output buffer fail(ret=0x%x).\n", ret);
			if (ret != (int32_t)HB_MEDIA_ERR_WAIT_TIMEOUT) {
				break;
			}
		}

	} while(TRUE);

	ret = hb_mm_mc_stop(context);
	if (ret != 0) {
		printf("hb_mm_mc_stop failed(%d).\n", ret);
	}

	ret = hb_mm_mc_release(context);
	if (ret != 0) {
		printf("hb_mm_mc_release failed(%d).\n", ret);
	}

	check_and_release_test(ctx);

}
