#include "SVCVideoStreamParser.h"
#include "SVCVideoStreamFramer.h"

SVCVideoStreamParser::SVCVideoStreamParser(SVCVideoStreamFramer *usingSource, FramedSource *inputSource) : MPEGVideoStreamParser(usingSource, inputSource),
						isFirstNAL(true), handle(NULL), log2_max_frame_num(5), separate_colour_plane_flag(false), frame_mbs_only_flag(true) 
{
}

SVCVideoStreamParser::~SVCVideoStreamParser() 
{
	if (handle != NULL) st_svcext_delete(handle);
}

void SVCVideoStreamParser::removeEmulationBytes(u_int8_t *nalUnitCopy, unsigned int maxSize, unsigned int &nalUnitCopySize) 
{
	u_int8_t *		nalUnitOrig			= fStartOfFrame + 4;
	unsigned const	numBytesInNALunit	= fTo - nalUnitOrig;
	if (numBytesInNALunit > maxSize) return;
	nalUnitCopySize = 0;
	for (unsigned int i = 0; i < numBytesInNALunit; i++) {
		if ((i < numBytesInNALunit - 2) && (nalUnitOrig[i] == 0) && (nalUnitOrig[i+1] == 0) && (nalUnitOrig[i+2] == 3)) {		// 00 00 03
				nalUnitCopy[nalUnitCopySize++] = nalUnitOrig[i++];
				nalUnitCopy[nalUnitCopySize++] = nalUnitOrig[i++];
		} else 
			nalUnitCopy[nalUnitCopySize++] = nalUnitOrig[i];
	} // i
}

void SVCVideoStreamParser::analyze_vui_parameters(BitVector &bv, unsigned int &num_units_in_tick, unsigned int &time_scale, unsigned int &fixed_frame_rate_flag) 
{
	unsigned int aspect_ratio_info_present_flag = bv.get1Bit();
	if (aspect_ratio_info_present_flag) {
		unsigned int aspect_ratio_idc = bv.getBits(8);
	    if (aspect_ratio_idc == 255/*Extended_SAR*/) 
			bv.skipBits(32); // sar_width; sar_height
	}

	unsigned int overscan_info_present_flag = bv.get1Bit();
	if (overscan_info_present_flag) 
		bv.skipBits(1); // overscan_appropriate_flag
  
	unsigned int video_signal_type_present_flag = bv.get1Bit();
	if (video_signal_type_present_flag) {
		bv.skipBits(4); // video_format; video_full_range_flag
		unsigned int colour_description_present_flag = bv.get1Bit();
		if (colour_description_present_flag) 
			bv.skipBits(24); // colour_primaries; transfer_characteristics; matrix_coefficients
	}
  
	unsigned int chroma_loc_info_present_flag = bv.get1Bit();
	if (chroma_loc_info_present_flag) {
		bv.get_expGolomb(); // chroma_sample_loc_type_top_field
		bv.get_expGolomb(); // chroma_sample_loc_type_bottom_field
	}
  
	unsigned int timing_info_present_flag = bv.get1Bit();
	if (timing_info_present_flag) {
		num_units_in_tick = bv.getBits(32);
		time_scale = bv.getBits(32);
		fixed_frame_rate_flag = bv.get1Bit();
	}
}

void SVCVideoStreamParser::analyze_seq_parameter_set_data(unsigned int &num_units_in_tick, unsigned int &time_scale, unsigned int &fixed_frame_rate_flag) 
{
	num_units_in_tick = time_scale = fixed_frame_rate_flag = 0;			// default values

	// Begin by making a copy of the NAL unit data, removing any 'emulation prevention' bytes:
	u_int8_t		sps[SPS_MAX_SIZE];
	unsigned int	spsSize;
	removeEmulationBytes(sps, sizeof sps, spsSize);

	BitVector		bv(sps, 0, 8*spsSize);

	bv.skipBits(8);														// forbidden_zero_bit; nal_ref_idc; nal_unit_type
	unsigned int	profile_idc				= bv.getBits(8);
	unsigned int	constraint_setN_flag	= bv.getBits(8);			// also "reserved_zero_2bits" at end
	unsigned int	level_idc				= bv.getBits(8);
	unsigned int	seq_parameter_set_id	= bv.get_expGolomb();
	if ((profile_idc == 100) || (profile_idc == 110) || (profile_idc == 122) || (profile_idc == 244) || (profile_idc == 44) || (profile_idc == 83) || (profile_idc == 86) || (profile_idc == 118) || (profile_idc == 128) ) {
		unsigned int chroma_format_idc = bv.get_expGolomb();
		if (chroma_format_idc == 3) 
			separate_colour_plane_flag = bv.get1Bit();
		bv.get_expGolomb();												// bit_depth_luma_minus8
		bv.get_expGolomb();												// bit_depth_chroma_minus8
		bv.skipBits(1);													// qpprime_y_zero_transform_bypass_flag
		
		unsigned int seq_scaling_matrix_present_flag = bv.get1Bit();
		if (seq_scaling_matrix_present_flag) 
			for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); i++) {
				unsigned int seq_scaling_list_present_flag = bv.get1Bit();
				if (seq_scaling_list_present_flag) {
					unsigned int sizeOfScalingList = i < 6 ? 16 : 64;
					unsigned int lastScale = 8;
					unsigned int nextScale = 8;
					for (unsigned int j = 0; j < sizeOfScalingList; j++) {
					    if (nextScale != 0) {
							unsigned delta_scale = bv.get_expGolomb();
							nextScale = (lastScale + delta_scale + 256) % 256;
						}
					lastScale = (nextScale == 0) ? lastScale : nextScale;
					} // j
				}
			} // i
	}
  
	unsigned int log2_max_frame_num_minus4 = bv.get_expGolomb();
	log2_max_frame_num = log2_max_frame_num_minus4 + 4;
	unsigned int pic_order_cnt_type = bv.get_expGolomb();
	if (pic_order_cnt_type == 0) {
		unsigned int log2_max_pic_order_cnt_lsb_minus4 = bv.get_expGolomb();
	} else if (pic_order_cnt_type == 1) {
		bv.skipBits(1);														// delta_pic_order_always_zero_flag
		bv.get_expGolomb();													// offset_for_non_ref_pic
		bv.get_expGolomb();													// offset_for_top_to_bottom_field
		
		unsigned int num_ref_frames_in_pic_order_cnt_cycle = bv.get_expGolomb();
		for (unsigned int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) 
			bv.get_expGolomb();												// offset_for_ref_frame[i]
	}
  
	unsigned int max_num_ref_frames						= bv.get_expGolomb();
	unsigned int gaps_in_frame_num_value_allowed_flag	= bv.get1Bit();
	unsigned int pic_width_in_mbs_minus1				= bv.get_expGolomb();
	unsigned int pic_height_in_map_units_minus1			= bv.get_expGolomb();
	
	frame_mbs_only_flag = bv.get1Bit();
	if (!frame_mbs_only_flag) 
		bv.skipBits(1);														// mb_adaptive_frame_field_flag
  
	bv.skipBits(1);															// direct_8x8_inference_flag
	unsigned int frame_cropping_flag					= bv.get1Bit();
	if (frame_cropping_flag) {
		(void)bv.get_expGolomb();											// frame_crop_left_offset
		(void)bv.get_expGolomb();											// frame_crop_right_offset
		(void)bv.get_expGolomb();											// frame_crop_top_offset
		(void)bv.get_expGolomb();											// frame_crop_bottom_offset
	}
	
	unsigned int vui_parameters_present_flag			= bv.get1Bit();
	if (vui_parameters_present_flag) 
		analyze_vui_parameters(bv,num_units_in_tick, time_scale, fixed_frame_rate_flag);
}

void SVCVideoStreamParser::analyze_slice_header(u_int8_t *start, u_int8_t *end, u_int8_t nal_unit_type, unsigned int &frame_num, unsigned int &pic_parameter_set_id, unsigned int &idr_pic_id, bool &field_pic_flag, bool &bottom_field_flag) 
{
	BitVector bv(start, 0, 8*(end-start));

	// Some of the result parameters might not be present in the header; set them to default values:
	field_pic_flag = bottom_field_flag = 0;

  // Note: We assume that there aren't any 'emulation prevention' bytes here to worry about...
	bv.skipBits(8);										// forbidden_zero_bit; nal_ref_idc; nal_unit_type
	unsigned int first_mb_in_slice	= bv.get_expGolomb();
	unsigned int slice_type			= bv.get_expGolomb();
	pic_parameter_set_id = bv.get_expGolomb();
	if (separate_colour_plane_flag) 
		bv.skipBits(2);									// colour_plane_id
	frame_num = bv.getBits(log2_max_frame_num);
	if (!frame_mbs_only_flag) {
		field_pic_flag = bv.get1Bit();
	    if (field_pic_flag) 
			bottom_field_flag = bv.get1Bit();
	}
	bool IdrPicFlag = nal_unit_type == 5;
  
	if (IdrPicFlag) 
	    idr_pic_id = bv.get_expGolomb();
	// We don't parse any more of the "slice_header", because we don't need the remaining fields (for our purpose)
}

unsigned int SVCVideoStreamParser::parse() 
{
	try {
		u_int8_t *data = new u_int8_t[NAL_MAX_SIZE];
		int n = 0;
		for (int i = 0; i < 4; i++) data[n++] = get1Byte();							// Getting the startCode 0x00000001

		// Then save everything up until the next 0x00000001 (4 bytes) or 0x00000100 (4 bytes).
		// Also make note of the first byte, because it contains the "nal_unit_type": 
		u_int32_t	next4Bytes		= test4Bytes();									// aa bb cc dd
		u_int8_t	firstByte		= next4Bytes >> 24;								// 00 00 00 aa
		u_int8_t	nal_ref_idc		= (firstByte & 0x60) >> 5;
		u_int8_t	nal_unit_type	= firstByte & 0x1F;

		// Store the packet to the buffer		
		while (next4Bytes != 0x00000001) {		
			data[n++] = get1Byte();															// aa
			next4Bytes = test4Bytes();
		}

		if (isFirstNAL) {
			svc_params	info;
			st_svcext_get_sei_nal_info(reinterpret_cast<char *> (data), n, reinterpret_cast<st_svcext_svc_info_t *>(&info));
			usingSource()->fFrameRate = info.layer_info[8].frame_rate;
			
			st_svcext_config_t config;
			config.avc = 1;
			config.debug = 0;					// DEBUG
			config.insert_dummy_frame = 0;
			config.spatial_layer = 2;			//spatial_layer;
			config.temporal_level = 2;			//temporal_layer;

			st_svcext_init(&handle, &config);
			if (handle == NULL) exit(-1);

			isFirstNAL = false;
		}

		st_svcext_preview_info_t	preview_info;
		st_svcext_error_e			err;

        err = st_svcext_preview(handle, reinterpret_cast<char *>(data), &preview_info);
        if (err) {
            printf("st_svcext_preview(): error (%d) \n", err);
            return 0;
        }

		if (preview_info.action == ST_SVCEXT_NAL_SKIP) {;
			delete [] data;
			setParseState();
			return 0; 
		}

		memcpy(fTo, data, n);					// TODO: chack overflow
		fTo += n;
		delete [] data;

		usingSource()->setPresentationTime();	//  fPresentationTime = nextPresentationTime;

		// If this NAL unit is a VCL NAL unit, we also scan the start of the next NAL unit, to determine whether this NAL unit
		// ends the current 'access unit'.  We need this information to figure out when to increment "fPresentationTime".
		// (RTP streamers also need to know this in order to figure out whether or not to set the "M" bit.)
		bool		thisNALUnitEndsAccessUnit	= false;										// until we learn otherwise 
		bool const	isVCL						= (nal_unit_type <= 5) && (nal_unit_type > 0);	// Would need to include type 20 for SVC and MVC #####
		if (isVCL) {
			u_int32_t	first4BytesOfNextNALUnit	= test4Bytes();
			u_int8_t	firstByteOfNextNALUnit		= (first4BytesOfNextNALUnit >> 24);
			u_int8_t	next_nal_ref_idc			= (firstByteOfNextNALUnit & 0x60) >> 5;
			u_int8_t	next_nal_unit_type			= (firstByteOfNextNALUnit & 0x1F);
			if (next_nal_unit_type >= 6) 									// The next NAL unit is not a VCL; therefore, we assume that this NAL unit ends the current 'access unit':
				thisNALUnitEndsAccessUnit = true;
			else {
				// The next NAL unit is also a VLC.  We need to examine it a little to figure out if it's a different 'access unit'.
				// (We use many of the criteria described in section 7.4.1.2.4 of the H.264 specification.)
				bool IdrPicFlag			= (nal_unit_type == 5);
				bool next_IdrPicFlag	= (next_nal_unit_type == 5);
				if (next_IdrPicFlag != IdrPicFlag)							// IdrPicFlag differs in value
					thisNALUnitEndsAccessUnit = true;
				else if ((next_nal_ref_idc != nal_ref_idc) && (next_nal_ref_idc * nal_ref_idc == 0))	 // nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0
					thisNALUnitEndsAccessUnit = true;
				else if ((nal_unit_type == 1 || nal_unit_type == 2 || nal_unit_type == 5) && (next_nal_unit_type == 1 || next_nal_unit_type == 2 || next_nal_unit_type == 5)) {
					// Both this and the next NAL units begin with a "slice_header".
					// Parse this (for each), to get parameters that we can compare:
					// Current NAL unit's "slice_header":
					unsigned int	frame_num, pic_parameter_set_id, idr_pic_id;
					bool			field_pic_flag, bottom_field_flag;
					analyze_slice_header(fStartOfFrame + 4, fTo, nal_unit_type, frame_num, pic_parameter_set_id, idr_pic_id, field_pic_flag, bottom_field_flag);

					// Next NAL unit's "slice_header":
					u_int8_t next_slice_header[NUM_NEXT_SLICE_HEADER_BYTES_TO_ANALYZE];
					testBytes(next_slice_header, sizeof next_slice_header);
					unsigned int next_frame_num, next_pic_parameter_set_id, next_idr_pic_id;
					bool next_field_pic_flag, next_bottom_field_flag;
					analyze_slice_header(next_slice_header, &next_slice_header[sizeof next_slice_header], next_nal_unit_type, next_frame_num, next_pic_parameter_set_id, next_idr_pic_id, next_field_pic_flag, next_bottom_field_flag);

					if (next_frame_num != frame_num)								thisNALUnitEndsAccessUnit = true;			// frame_num differs in value
					else if (next_pic_parameter_set_id != pic_parameter_set_id)		thisNALUnitEndsAccessUnit = true;			// pic_parameter_set_id differs in value
					else if (next_field_pic_flag != field_pic_flag)					thisNALUnitEndsAccessUnit = true;			// field_pic_flag differs in value
					else if (next_bottom_field_flag != bottom_field_flag)			thisNALUnitEndsAccessUnit = true;			// bottom_field_flag differs in value
					else if (next_IdrPicFlag == 1 && next_idr_pic_id != idr_pic_id) thisNALUnitEndsAccessUnit = true;			// IdrPicFlag is equal to 1 for both and idr_pic_id differs in value // Note: We already know that IdrPicFlag is the same for both.
				}
			}
			if (thisNALUnitEndsAccessUnit) {
				usingSource()->fPictureEndMarker = true;
				usingSource()->fPictureCount++;
				
				// Note that the presentation time for the next NAL unit will be different:
				struct timeval& nextPT = usingSource()->nextPresentationTime; // alias
				nextPT = usingSource()->fPresentationTime;
				double nextFraction = nextPT.tv_usec/1000000.0 + 1/usingSource()->fFrameRate;
				unsigned int nextSecsIncrement = (long)nextFraction;
				nextPT.tv_sec += (long)nextSecsIncrement;
				nextPT.tv_usec = (long)((nextFraction - nextSecsIncrement)*1000000);
			}
		}
		setParseState();
		return curFrameSize();
	} 
	catch (int ) {
		return 0;  // the parsing got interrupted
	}
}