/*
 * This is a simple program to test the multi-core microblaze SoC.
 */

#include <stdio.h>

#include "sysace_stdio.h"

#include "DecTest.h"


static int32_t Malloc(void *userData, int32_t size, int32_t attrs) {
    return (int32_t)(malloc(size));
}

static void Free(void *userData, int32_t ptr) {
    free((void *)(ptr));
}


int entropy_count=0;
int mvr_count=0;
int iqit_count_i=0;
int iqit_count_p=0;
int intra_pred_count=0;
int inter_pred_count=0;
int df_count=0;


int single_core()
{

    FILE *in_fd,*out_fd;

    char *fileName = INPUT_FILE_NAME;

    AVCHandle *mHandle=NULL;
	int mSPSSeen,mPPSSeen;
	int H264_Size,H264_Offset;
	uint8_t *H264_File;
    in_fd = sysace_fopen(fileName ,"r" );

	xil_printf("input_filename %s \n\r",fileName);

	if(!in_fd){
		printf("open error\n");
	}

	H264_File = (unsigned char *) malloc (8388608);

	H264_Size = sysace_fread(H264_File,8388608,1,in_fd);

    sysace_fclose(in_fd);

    out_fd= sysace_fopen("mam.yuv","w");

    if(!out_fd)
    	xil_printf("output file fopen fails\n\r");

	mHandle=malloc(sizeof(AVCHandle));

	AVCDecInit( mHandle , &mSPSSeen , &mPPSSeen , &H264_Offset );

	const uint8_t *NALUnit;
	int NALSize,Read_res=0,Dec_res=0;

	/* start timer */
	int start_time = tmr_get_value();


	while(H264_File!=NULL)
	{
		Read_res=ReadNALUnit( H264_File , H264_Offset , H264_Size , &NALUnit , &NALSize );

		if(Read_res==-1)
			break;//fali
		Dec_res=DecNALUnit (mHandle,NALUnit,NALSize,&mSPSSeen,&mPPSSeen);
		if(Dec_res== -1)
		{
			break;//fali
		}else if(Dec_res ==0 ) // Dec_res == 1 , OK
		{				       // next NAL
			if (NALSize + 4 == H264_Size)
			{
				free(H264_File);
				H264_File = NULL;
			} else
			{
				H264_Offset = H264_Offset+NALSize+4;
				H264_Size = H264_Size-NALSize-4;
			}

		}else if (Dec_res ==1) // Dec_res ==1
		{                     // get YUV data to output_filename
			int index;
			int Release;
			AVCFrameIO Output;
			Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
			AVCDec_Status status = PVAVCDecGetOutput(mHandle, &index, &Release, &Output);

#ifdef dumpFile
			sysace_fwrite(Output.YCbCr[0],Output.pitch*Output.height,1,out_fd);
			sysace_fwrite(Output.YCbCr[1],(Output.pitch/2)*(Output.height/2),1,out_fd);
			sysace_fwrite(Output.YCbCr[2],(Output.pitch/2)*(Output.height/2),1,out_fd);
#endif
			if (status != AVCDEC_SUCCESS)
			{
				xil_printf("PVAVCDecGetOutput returned error %d\r\n", status);
				break;
			}
		}
	}

	// get last YUV data
	AVCDec_Status status =AVCDEC_SUCCESS;
	while(status==AVCDEC_SUCCESS){
		int index;
		int Release;
		AVCFrameIO Output;
		Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
		AVCDec_Status status = PVAVCDecGetOutput(mHandle, &index, &Release, &Output);
		if (status != AVCDEC_SUCCESS) {
			break;
		}

#ifdef dumpFile
		sysace_fwrite(Output.YCbCr[0],Output.pitch*Output.height,1,out_fd);
		sysace_fwrite(Output.YCbCr[1],(Output.pitch/2)*(Output.height/2),1,out_fd);
		sysace_fwrite(Output.YCbCr[2],(Output.pitch/2)*(Output.height/2),1,out_fd);
#endif

	}

	/* output decode time */
	printf("time : %.0f\n",(tmr_get_value()-start_time)*CLOCKS_PER_MICROSEC);


	if(H264_File!=NULL)
		free (H264_File);
	sysace_fclose (out_fd);
	system("pause");
	free(mHandle);
	xil_printf("Decode Over \n\r");



    return 0;
}

void AVCDecInit(AVCHandle * mHandle,int *mSPSSeen,int *mPPSSeen,int * H264_Offset){

	memset(mHandle, 0, sizeof(AVCHandle));
    mHandle->AVCObject = NULL;
    mHandle->userData = NULL;

    mHandle->CBAVC_Malloc = Malloc;                 // if u want how to implement those function
    mHandle->CBAVC_Free = Free;                     // u can see AVCDecoder.cpp
	*mSPSSeen = 0;
    *mPPSSeen = 0;
	*H264_Offset=0;
}

int ReadNALUnit(const uint8_t * H264_File , int H264_Offset ,int H264_Size ,const uint8_t ** NALUnit ,int *NALSize ){

	const uint8_t *data =
        (const uint8_t *)H264_File + H264_Offset;

	int size = H264_Size;

	if(size < 4){
		return -1 ; //fail
	}
	if(memcmp(kStartCode, data, 4)){
		return -1 ; //fail
	}

	int offset = 4;
    while (offset + 3 < size && memcmp(kStartCode, &data[offset], 4)) {//find next NAL startcode
        ++offset;
    }

    *NALUnit = &data[4];
    if (offset + 3 >= size) {
        *NALSize = size - 4;
    } else {
        *NALSize = offset - 4;
    }
	return 1 ;

}

int DecNALUnit (AVCHandle *mHandle,const uint8_t *NALUnit,const int NALSize,int *mSPSSeen,int *mPPSSeen)
{
    int nalType;
    int nalRefIdc;
    AVCDec_Status res =PVAVCDecGetNALType((uint8_t *)(NALUnit), NALSize,&nalType, &nalRefIdc);

    if (res != AVCDEC_SUCCESS) {
		xil_printf("cannot determine nal type\r\n");
    } else if (nalType == AVC_NALTYPE_SPS || nalType == AVC_NALTYPE_PPS|| (*mSPSSeen &&* mPPSSeen)) {
        switch (nalType) {
            case AVC_NALTYPE_SPS:
            {
                *mSPSSeen = 1;
                res = PVAVCDecSeqParamSet(
                        mHandle, (uint8_t *)(NALUnit),
                        NALSize);

                if (res != AVCDEC_SUCCESS) {
					xil_printf("PVAVCDecSeqParamSet returned error %d\n\r", res);
                    return -1;//fail
                }

                break;
            }

            case AVC_NALTYPE_PPS:
            {
				*mPPSSeen = 1;
                res = PVAVCDecPicParamSet(
                        mHandle, (uint8_t *)(NALUnit),
                        NALSize);

                if (res != AVCDEC_SUCCESS) {
					xil_printf("PVAVCDecPicParamSet returned error %d\n\r", res);
                   return -1;
                }
				return 0;//ok,but no data
                break;
            }

            case AVC_NALTYPE_SLICE:
            case AVC_NALTYPE_IDR:
            {
			     res = PVAVCDecodeSlice(
                        mHandle, (uint8_t *)(NALUnit),
                        NALSize);

                if (res == AVCDEC_PICTURE_OUTPUT_READY) {
                   // Do _not_ release input buffer yet.
					return 1;
                    break;
                }

                if (res == AVCDEC_PICTURE_READY || res == AVCDEC_SUCCESS) {

                } else {
					xil_printf("PVAVCDecodeSlice returned error %d\n\r", res);
					return -1;
				}
                break;
            }

            case AVC_NALTYPE_SEI:
            {

                if (res != AVCDEC_SUCCESS) {
                    return -1;
                }

                break;
            }

            case AVC_NALTYPE_AUD:
            case AVC_NALTYPE_FILL:
            case AVC_NALTYPE_EOSEQ:
			{

                break;
            }

            default:
            {
              //  LOGE("Should not be here, unknown nalType %d", nalType);
              //  CHECK(!"Should not be here");
				return -1;
                break;
            }
        }
    } else {
        // We haven't seen SPS or PPS yet.
    }
	return 0;//ok,but no data

}
