/********************************************************************************
 *																				*
 * This code has been developed by Project Mayo. This software is an			*
 * implementation of a part of one or more MPEG-4 Video tools as				*
 * specified in ISO/IEC 14496-2 standard.  Those intending to use this			*
 * software module in hardware or software products are advised that its		*
 * use may infringe existing patents or copyrights, and any such use			*
 * would be at such party's own risk.  The original developer of this			*
 * software module and his/her company, and subsequent editors and their		*
 * companies (including Project Mayo), will have no liability for use of		*
 * this software or modifications or derivatives thereof.						*
 *																				*
 ********************************************************************************
 *																				*	
 * This program is free software; you can redistribute it and/or modify			*
 * it under the terms of the GNU General Public License as published by			*
 * the Free Software Foundation; either version 2 of the License, or			*
 * (at your option) any later version.											*
 *																				*
 * The GPL can be found at: http://www.gnu.org/copyleft/gpl.html				*
 *																				*
 * Authors:																		*
 *																				*
 *	Andrea Graziani	(Ag):														*
 *		- Original source code (Open Divx Decoder 0.4a).						*		
 *																				*
 *	Pedro Mateu		(Pm):														*
 *		- Modified and optimized code											*
 *																				*
 ********************************************************************************/


// mp4_predict.c //

#include "global.h"
#include "mp4_predict.h"

/**/

void dc_recon(int block_num, idct_block_t * dc_value);
void ac_recon(int block_num, idct_block_t * psBlock);
void rescue_predict();

/*
	B - C
	|   |
	A - x
*/

void dc_recon(int block_num, idct_block_t * dc_value)
{
	int b_xpos,b_ypos;
	int	Fa,Fb,Fc;
	int DC_pred;

	if (block_num < 4)
	{
		b_xpos = (mp4_hdr.mb_xpos << 1) + (block_num & 1);
		b_ypos = (mp4_hdr.mb_ypos << 1) + ((block_num & 2) >> 1);
	
		// set prediction direction

		if (!b_ypos){
			Fb=Fc=1024;
			Fa=coeff_pred.dc_lum[b_xpos][0];
		}
		else {
			Fb=coeff_pred.dc_lum[b_xpos][(b_ypos-1)&3];
			Fc=coeff_pred.dc_lum[b_xpos+1][(b_ypos-1)&3];
			Fa=coeff_pred.dc_lum[b_xpos][b_ypos&3];
		}

		if (abs(Fb - Fa) < abs(Fb - Fc))
			{
				coeff_pred.predict_dir = TOP;
				DC_pred = Fc;
			}
		else
			{
				coeff_pred.predict_dir = LEFT;
				DC_pred = Fa;
			}

		dc_value[0] += _div_div(DC_pred, mp4_hdr.dc_scaler);
		dc_value[0] *= mp4_hdr.dc_scaler;

		// store dc value
		
		coeff_pred.dc_lum[b_xpos+1][b_ypos&3] = dc_value[0];
	}
	else // chrominance blocks
	{
		block_num = block_num - 4;
		b_xpos = mp4_hdr.mb_xpos;
		b_ypos = mp4_hdr.mb_ypos;
		
		if (b_ypos==0){
			Fb=Fc=1024;
			Fa=coeff_pred.dc_chr[block_num][b_xpos][0];
		}
		else {
			Fb=coeff_pred.dc_chr[block_num][b_xpos][(b_ypos-1)&1];
			Fc=coeff_pred.dc_chr[block_num][b_xpos+1][(b_ypos-1)&1];
			Fa=coeff_pred.dc_chr[block_num][b_xpos][b_ypos&1];
		}
		// set prediction direction
		if (abs(Fb - Fa) < abs(Fb - Fc))
			{
				coeff_pred.predict_dir = TOP;
				DC_pred = Fc;
			}
		else
			{
				coeff_pred.predict_dir = LEFT;
				DC_pred = Fa;
			}

		dc_value[0] += _div_div(DC_pred, mp4_hdr.dc_scaler);
		dc_value[0] *= mp4_hdr.dc_scaler;

		// store dc value
		coeff_pred.dc_chr[block_num][b_xpos+1][b_ypos&1] = dc_value[0];
	}
}

/**/

void ac_recon(unsigned char block_num, idct_block_t * psBlock)
{
	int b_xpos, b_ypos;
	int i;

	if (block_num < 4) 
	{
		b_xpos = (mp4_hdr.mb_xpos << 1) + (block_num & 1);
		b_ypos = (block_num & 2) >> 1;
		if (mp4_hdr.ac_pred_flag) 
		{	
			if (coeff_pred.predict_dir == TOP)
			{
				if (mp4_hdr.mb_ypos|b_ypos){
					for (i = 1; i < 8; i++) 
						psBlock[i] += coeff_pred.ac_top_lum[b_xpos][i];
				}
			}
			else // left prediction
			{
				if (b_xpos){
					for (i = 1; i < 8; i++)
						psBlock[i<<3] += coeff_pred.ac_left_lum[b_ypos][i];
				}
			}
		}
		// store coefficients

		for (i = 1; i < 8; i++) 
			coeff_pred.ac_top_lum[b_xpos][i] = psBlock[i];
		
		for (i = 1; i < 8; i++) 
			coeff_pred.ac_left_lum[b_ypos][i] = psBlock[i<<3];
	}
	
	else {

		block_num -= 4;

		b_xpos = mp4_hdr.mb_xpos;

		if (mp4_hdr.ac_pred_flag) 
		{	
			if (coeff_pred.predict_dir == TOP)
			{
				if (mp4_hdr.mb_ypos){
					for (i = 1; i < 8; i++)
						psBlock[i] += coeff_pred.ac_top_chr[b_xpos][block_num][i];
				}
			}
			else // left prediction
			{
				if (b_xpos!=0){
					for (i = 1; i < 8; i++)
						psBlock[i<<3] += coeff_pred.ac_left_chr[block_num][i];
				}
			}
		}

		// store coefficients

		for (i = 1; i < 8; i++) 
			coeff_pred.ac_top_chr[b_xpos][block_num][i] = psBlock[i];
		for (i = 1; i < 8; i++) 
			coeff_pred.ac_left_chr[block_num][i] = psBlock[i<<3];
	}

	
}

/**/

#define _IsIntra(mb) ((modemap[mb] == INTRA) || \
	(modemap[mb] == INTRA_Q))
#define MIPS_Reset_AC(a) __asm("sdr $0,0($4);" \
	"sdr	$0,8($4);"	\
	"sdr	$0,16($4);"	\
	"sdr	$0,24($4);",a);

void rescue_predict() 
{
	int i;
	int current_pos = mp4_hdr.mba;
	int former_pos = mp4_hdr.mba-mp4_hdr.mb_xsize;
	int mb_ypos = mp4_hdr.mb_ypos;
	int mb_xpos = mp4_hdr.mb_xpos;
	if (! _IsIntra(former_pos-1)) {
		// rescue -A- DC value
		coeff_pred.dc_lum[2*mb_xpos][(2*mb_ypos-1)&3] = 1024;
		coeff_pred.dc_chr[0][mb_xpos][(mb_ypos-1)&1] = 1024;
		coeff_pred.dc_chr[1][mb_xpos][(mb_ypos-1)&1] = 1024;
	}
	// left
	if (! _IsIntra(current_pos-1)) {
		// rescue -B- DC values
			coeff_pred.dc_lum[2*mb_xpos][(2*mb_ypos)&3] = 1024;
			coeff_pred.dc_lum[2*mb_xpos][(2*mb_ypos+1)&3] = 1024;
			
			#ifdef MIPS_ASM_64
			MIPS_Reset_AC(&coeff_pred.ac_left_lum[0][0]);
			#elif defined ARM_VARS
//			memset (&coeff_pred.ac_left_lum[0][0],0,64);
			for(i = 1; i < 8; i++) coeff_pred.ac_left_lum[0][i] = 0;
			for(i = 1; i < 8; i++) coeff_pred.ac_left_lum[1][i] = 0;
			#else
//			memset (&coeff_pred.ac_left_lum[0][0],0,32);
			for(i = 1; i < 8; i++) coeff_pred.ac_left_lum[0][i] = 0;
			for(i = 1; i < 8; i++) coeff_pred.ac_left_lum[1][i] = 0;
			#endif
			coeff_pred.dc_chr[0][mb_xpos][mb_ypos&1] = 1024;
			coeff_pred.dc_chr[1][mb_xpos][mb_ypos&1] = 1024;
			//  rescue -B- AC values
			#ifdef MIPS_ASM_64
			MIPS_Reset_AC(&coeff_pred.ac_left_chr[0][0]);
			#elif defined ARM_VARS
			//memset (&coeff_pred.ac_left_chr[0][0],0,64);
			for(i = 1; i < 8; i++) coeff_pred.ac_left_chr[0][i] = 0;
			for(i = 1; i < 8; i++) coeff_pred.ac_left_chr[1][i] = 0;
			#else
//			memset (&coeff_pred.ac_left_chr[0][0],0,32);
			for(i = 1; i < 8; i++) coeff_pred.ac_left_chr[0][i] = 0;
			for(i = 1; i < 8; i++) coeff_pred.ac_left_chr[1][i] = 0;
			#endif
	}
	// top
	if (! _IsIntra(former_pos)) {
		
		// rescue -C- DC values
		
		coeff_pred.dc_lum[2*mb_xpos+1][(2*mb_ypos-1)&3] = 1024;
		coeff_pred.dc_lum[2*mb_xpos+2][(2*mb_ypos-1)&3] = 1024;
		
		#ifdef MIPS_ASM_64
		MIPS_Reset_AC(&coeff_pred.ac_top_lum[2*mb_xpos][0]);
		#elif defined ARM_VARS
		//memset (&coeff_pred.ac_top_lum[2*mb_xpos][0],0,64);
		for(i = 1; i < 8; i++)	coeff_pred.ac_top_lum[2*mb_xpos][i] = 0;
		for(i = 1; i < 8; i++)	coeff_pred.ac_top_lum[2*mb_xpos+1][i] = 0;
		#else
		for(i = 1; i < 8; i++)	coeff_pred.ac_top_lum[2*mb_xpos][i] = 0;
		for(i = 1; i < 8; i++)	coeff_pred.ac_top_lum[2*mb_xpos+1][i] = 0;
//		memset (&coeff_pred.ac_top_lum[2*mb_xpos][0],0,32);
		#endif
		coeff_pred.dc_chr[0][mb_xpos+1][(mb_ypos-1)&1] = 1024;
		coeff_pred.dc_chr[1][mb_xpos+1][(mb_ypos-1)&1] = 1024;
		
		// rescue -C- AC values
		#ifdef MIPS_ASM_64
		MIPS_Reset_AC(&coeff_pred.ac_top_chr[mb_xpos][0][0]);
		#elif defined ARM_VARS
		for(i = 1; i < 8; i++)	coeff_pred.ac_top_chr[0][mb_xpos][i] = 0;
		for(i = 1; i < 8; i++)	coeff_pred.ac_top_chr[1][mb_xpos][i] = 0;
//		memset (&coeff_pred.ac_top_chr[mb_xpos][0][0],0,64);
		#else
		for(i = 1; i < 8; i++)	coeff_pred.ac_top_chr[0][mb_xpos][i] = 0;
		for(i = 1; i < 8; i++)	coeff_pred.ac_top_chr[1][mb_xpos][i] = 0;
//		memset (&coeff_pred.ac_top_chr[mb_xpos][0][0],0,32);
		#endif
	}
}
