/*******************************************************************
                picture decoding (reconstract) module
 *******************************************************************/

#define PICTURE_C
#include "picture.h"

int decode_picture(VIDEO_STREAM *in, MC_BUFFER *out, DECODE_PICTURE_PARAMETER *prm);

static void add_block_data_to_frame(short *in, FRAME *out, READ_BLOCK_OPTION *opt, int x, int y, int block_number);

int decode_picture(VIDEO_STREAM *in, MC_BUFFER *out, DECODE_PICTURE_PARAMETER *prm)
{
	int i;

	SLICE_HEADER sh;
	MACROBLOCK   mb;

	short 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_structure != 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)*16) == (address % width) ){
            inc = 1;
        }else{
            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_structure != 3){
				y *= 2;
			}
			mc(out, &(prm->mc_parameter), x, y);

			for(i=0;i<mb.block_count;i++){
				if(mb.pattern_code[i]){
					if(!read_block(in, block, i, &(prm->block_option))){
                        return 0;
                    }
					prm->idct_func(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_structure != 3){
					y *= 2;
				}
				mc(out, &(prm->mc_parameter), x, y);

				address += 16;
			}

		}while(1);
	}

	return 1;
}

static void add_block_data_to_frame(short *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;
        break;
    default:
        return;    
	};

	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 + x_offset[block_number];
			}else{
				step = out->width;
				p += (y + y_offset[block_number]) * out->width;
				p += x + x_offset[block_number];
			}
		}else{
			step = out->width * 2;
			p += (y + (y_offset[block_number] * 2) + (opt->picture_structure >> 1)) * out->width;
			p += x + x_offset[block_number];
		}
	}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 + x_offset[block_number];
			}else{
				step = out->width;
				p += (y + y_offset[block_number]) * out->width;
				p += x + x_offset[block_number];
			}
		}else{
			step = out->width * 2;
			p += (y + (y_offset[block_number] * 2) + (opt->picture_structure >> 1)) * out->width;
			p += x + x_offset[block_number];
		}
	}

	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+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;
		}
	}
}