/*******************************************************************
                picture decoding (reconstract) module
 *******************************************************************/

#define PICTURE_C
#include "picture.h"

int decode_picture(MPEG_IO *in, MC_BUFFER *out, DECODE_PICTURE_PARAMETER *prm);

static void add_block_data_to_frame(int *in, FRAME *out, READ_BLOCK_OPTION *opt, int x, int y, int block_number);

int decode_picture(MPEG_IO *in, MC_BUFFER *out, DECODE_PICTURE_PARAMETER *prm)
{
	int i,n;
	int code;
	
	SLICE_HEADER sh;
	MACROBLOCK   mb;

	int block[64];
	
	int x, y, address, inc, width, max;

	address = 0;
	width = out->current->width;
	max = width * out->current->height / 16;
	if(prm->mc_parameter.picture_coding_type != 3){
		max /= 2;
	}
	
	while(next_start_code(in)){
		
		if(!read_slice_header(in, &sh, &(prm->slice_option))){
			return 0;
		}
		
		slice_header_to_read_block_option(&sh, &(prm->block_option));

		reset_motion_vector_predictor(&mb);
		
		inc = get_macroblock_address_increment(in);
		if(inc != 1){
			return 0;
		}

		do{
			read_macroblock(in, &mb, &(prm->macroblock_option));
			macroblock_to_mc_parameter(&mb, &(prm->mc_parameter));
			macroblock_to_read_block_option(&mb, &(prm->block_option));

			x = address % width;
			y = address / width * 16;
			if(prm->mc_parameter.picture_coding_type != 3){
				y *= 2;
			}
			mc(out, &(prm->mc_parameter), x, y);

			for(i=0;i<mb.block_count;i++){
				if(mb.pattern_code[i]){
					read_block(in, block, i, &(prm->block_option));
					idct_int32(block);
					add_block_data_to_frame(block, out->current, &(prm->block_option), x, y, i);
				}
			}
			address += 16;
			
			if(address >= max){ /* all macroblocks have decoded */
				return 1;
			}
			
			inc = get_macroblock_address_increment(in);
			if(inc == 0){ /* goto next slice */
				break;
			}

			if(inc != 1){
				reset_dc_dct_predictor(&(prm->block_option));
				if(prm->macroblock_option.picture_coding_type == 2){
					reset_motion_vector_predictor(&mb);
					macroblock_to_mc_parameter(&mb, &(prm->mc_parameter));
				}
				if(prm->mc_parameter.picture_structure == 3){
					prm->mc_parameter.prediction_type = PREDICTION_TYPE_FRAME_BASED;
				}else{
					prm->mc_parameter.prediction_type = PREDICTION_TYPE_FIELD_BASED;
					prm->mc_parameter.motion_vertical_field_select[0][1] = (prm->mc_parameter.picture_structure == 2);
					prm->mc_parameter.motion_vertical_field_select[0][1] = (prm->mc_parameter.picture_structure == 2);
				}
			}
			
			for(i=0;i<inc-1;i++){
				
				x = address % width;
				y = address / width * 16;
				if(prm->mc_parameter.picture_coding_type != 3){
					y *= 2;
				}
				mc(out, &(prm->mc_parameter), x, y);
				
				address += 16;
			}
			
		}while(1);
	}

	return 1;
}

static void add_block_data_to_frame(int *in, FRAME *out, READ_BLOCK_OPTION *opt, int x, int y, int block_number)
{
	int i,j;
	int step;
	unsigned char *p;
	
	static const int cc[] = {
		0, 0, 0, 0, 1, 2, 1, 2, 1, 2, 1, 2,
	};

	static const int x_offset[] = {
		0, 8, 0, 8, 0, 0, 0, 0, 8, 8, 8, 8,
	};

	static const int y_offset[] = {
		0, 0, 8, 8, 0, 0, 8, 8, 0, 0, 8, 8,
	};

	switch(cc[block_number]){
	case 0:
		p = out->y;
		break;
	case 1:
		p = out->u;
		break;
	case 2:
		p = out->v;
	};

	if(cc[block_number]){
		if(opt->chroma_format != 3){
			x /= 2;
		}
		if(opt->chroma_format == 1){
			y /= 2;
		}
		if(opt->picture_structure == 3){
			if(opt->dct_type && (opt->chroma_format != 1)){
				step = out->width * 2;
				p += (y + y_offset[block_number] / 8) * out->width;
				p += x;
			}else{
				step = out->width;
				p += (y + y_offset[block_number]) * out->width;
				p += x;
			}
		}else{
			step = out->width * 2;
			p += (y + y_offset[block_number] + (opt->picture_structure >> 1)) * out->width;
			p += x;
		}
	}else{
		if(opt->picture_structure == 3){
			if(opt->dct_type){
				step = out->width * 2;
				p += (y + y_offset[block_number] / 8) * out->width;
				p += x;
			}else{
				step = out->width;
				p += (y + y_offset[block_number]) * out->width;
				p += x;
			}
		}else{
			step = out->width * 2;
			p += (y + y_offset[block_number] + (opt->picture_structure >> 1)) * out->width;
			p += x;
		}
	}

	if(opt->macroblock_intra){
		for(i=0;i<8;i++){
			for(j=0;j<8;j++){
				p[j] = uchar_clip_table[UCHAR_CLIP_TABLE_OFFSET+128+in[j]];
			}
			p += step;
			in += 8;
		}
	}else{
		for(i=0;i<8;i++){
			for(j=0;j<8;j++){
				p[j] = uchar_clip_table[UCHAR_CLIP_TABLE_OFFSET+p[j]+in[j]];
			}
			p += step;
			in += 8;
		}
	}
}