#include <stdio.h>
#include <math.h>
#include "global.h"


//FYP3098
#define BLK4x4 0
#define BLK16x16 1
#define U_pic	0
#define V_pic	1
#define Gx		0
#define Gy		1
#define COH_THD 0.7
//END FYP3098




//FYP3098: IntraPrediction = 1

void EdgeSobel()
{
	int i,j, BmpH = input->img_height, BmpW = input->img_width; 
	int BmpH_C = BmpH/2, BmpW_C = BmpW/2, uv;
	double x,y,Amplitude, y_over_x;
   

 	for (i=0;i<BmpH;i++)
	{
		for (j=0;j<BmpW;j++) 
		{
			img->Edge_Map_Mode[BLK4x4][i][j] = 9;
			img->Edge_Map_Mode[BLK16x16][i][j] = 9;
			img->Edge_Map_Amp[i][j] = 0;
		}
	}
	
	for (i=1;i<BmpH-1;i++)
		for (j=1;j<BmpW-1;j++) 
		{
			x=imgY_org[i-1][j-1]+imgY_org[i-1][j]+imgY_org[i-1][j]+imgY_org[i-1][j+1]
			 -imgY_org[i+1][j-1]-imgY_org[i+1][j]-imgY_org[i+1][j]-imgY_org[i+1][j+1];

			y=imgY_org[i-1][j+1]+imgY_org[i][j+1]+imgY_org[i][j+1]+imgY_org[i+1][j+1]
			 -imgY_org[i-1][j-1]-imgY_org[i][j-1]-imgY_org[i][j-1]-imgY_org[i+1][j-1];
			Amplitude= fabs(x) + fabs(y);
			img->Edge_Map_Amp[i][j] = (int) Amplitude;
			
			if (x != 0)
				y_over_x = y/x;

				if (fabs(x) < 0.000001)
				img->Edge_Map_Mode[BLK4x4][i][j] = 0;		// Mode 0: Vertical
				else 
				{
					if (fabs(y_over_x) > 5.027339) 
					{
						img->Edge_Map_Mode[BLK4x4][i][j] = 0;	// Mode 0: Vertical
					}
					else if (fabs(y_over_x) < 0.198912) 
					{
						img->Edge_Map_Mode[BLK4x4][i][j] = 1;	// Mode 1: Horizontal
					} 
					else if ((y_over_x>0.198912) && (y_over_x<=0.668179))
					{
						img->Edge_Map_Mode[BLK4x4][i][j] = 6;	// Mode 6:
					}
					else if ((y_over_x>0.668179) && (y_over_x<=1.496606))
					{
						img->Edge_Map_Mode[BLK4x4][i][j] = 4;	// Mode 4:
					}
					else if ((y_over_x>1.496606) && (y_over_x<=5.027339))
					{
						img->Edge_Map_Mode[BLK4x4][i][j] = 5;	// Mode 5:
					}
					else if ((y_over_x>-5.027339) && (y_over_x<=-1.496606))
					{
						img->Edge_Map_Mode[BLK4x4][i][j] = 7;	// Mode 7:
					}
					else if ((y_over_x>-1.496606) && (y_over_x<=-0.668179))
					{
						img->Edge_Map_Mode[BLK4x4][i][j] = 3;	// Mode 3:
					}
					else if ((y_over_x>-0.668179) && (y_over_x<=-0.198912))
					{
						img->Edge_Map_Mode[BLK4x4][i][j] = 8;	// Mode 8:
					}
				}

				if (fabs(x) < 0.000001)
				img->Edge_Map_Mode[BLK16x16][i][j] = 0;		// Mode 0: Vertical
				else 
				{
					if (fabs(y_over_x) > 2.414214) 
					{
						img->Edge_Map_Mode[BLK16x16][i][j] = 0;	// Mode 0: Vertical
					}
					else if (fabs(y_over_x) < 0.414214) 
					{
						img->Edge_Map_Mode[BLK16x16][i][j] = 1;	// Mode 1: Horizontal
					} 
					else
					{
						img->Edge_Map_Mode[BLK16x16][i][j] = 3;	// Mode 3: Plan
					}
				}

		} // for_loop

	for (uv=0;uv<2; uv++)
    {
		for (i=0;i<BmpH_C;i++)
		{
			for (j=0;j<BmpW_C;j++) 
			{
			img->Edge_Map_C_Mode[uv][i][j] = 9;
			img->Edge_Map_C_Amp[uv][i][j] = 0;
			
			}
		}
	
		for (i=1;i<BmpH_C-1;i++)
			for (j=1;j<BmpW_C-1;j++) 
			{
				x=imgUV_org[uv][i-1][j-1]+imgUV_org[uv][i-1][j]+imgUV_org[uv][i-1][j]+imgUV_org[uv][i-1][j+1]
				-imgUV_org[uv][i+1][j-1]-imgUV_org[uv][i+1][j]-imgUV_org[uv][i+1][j]-imgUV_org[uv][i+1][j+1];

				y=imgUV_org[uv][i-1][j+1]+imgUV_org[uv][i][j+1]+imgUV_org[uv][i][j+1]+imgUV_org[uv][i+1][j+1]
				-imgUV_org[uv][i-1][j-1]-imgUV_org[uv][i][j-1]-imgUV_org[uv][i][j-1]-imgUV_org[uv][i+1][j-1];
				Amplitude= fabs(x) + fabs(y);
				img->Edge_Map_C_Amp[uv][i][j] = (int) Amplitude;

			    if (fabs(x) < 0.000001)
				    img->Edge_Map_C_Mode[uv][i][j] = 0;		// Mode 0: Vertical
				else 
				{
					y_over_x = y/x;
					if (fabs(y_over_x) > 2.414214) 
					{
						img->Edge_Map_C_Mode[uv][i][j] = 0;	// Mode 0: Vertical
					}
					else if (fabs(y_over_x) < 0.414214) 
					{
						img->Edge_Map_C_Mode[uv][i][j] = 1;	// Mode 1: Horizontal
					} 
					else
						img->Edge_Map_C_Mode[uv][i][j] = 3;

				}

		
			}
	}
}

int maximum(int *histogram, int m) 
{
	int i, imax, max;
	imax=0;
	max = histogram[0];
	for (i=1; i<m; i++)
		if (max < histogram[i])
		{
			max = histogram[i];
			imax = i;
		}
	return imax;
}

// Mode decision for 16x16 macroblock
void ED_Intra16x16(int pic_x,int pic_y, int mb_available_up, int mb_available_left)
{
	int i, j, mode16;
	int histogram[4];

	if (!mb_available_up)
	{
		if (!mb_available_left)
		{
			img->Intra_Mode16x16[2] = 1;	//DC
			return;
		}
		else
		{
			img->Intra_Mode16x16[1] = 1;	//HOR
			return;
		}
		
	}
	else if (!mb_available_left)
	{
			img->Intra_Mode16x16[0] = 1;	//VERT
			return;
	}
	
	for (i=0; i<4; i++)	
		histogram[i] = 0;

	for (i=0; i<16; i++)
		for (j=0; j<16; j++) 
			histogram[img->Edge_Map_Mode[BLK16x16][pic_y+i][pic_x+j]] += img->Edge_Map_Amp[pic_y+i][pic_x+j];

	mode16=maximum(histogram,4);

	img->Intra_Mode16x16[mode16] = 1;
}


// Mode decision for 4x4 macroblock
void ED_Intra4x4(int pic_x,int pic_y, int block_available_up, int block_available_left)
{
	int i,j;
	int mode4;

	int histogram[9];
	int mode_order[9] = {2,6,8,0,4,3,5,1,7};
	int mode_order_rev[8] = {3,7,0,5,4,6,1,8};
	
	// Initialization for 4x4 mode
	for (i=0; i<9; i++)
	{
		if (i == 2)
			img->Intra_Mode4x4[i] = 1;
		else
			img->Intra_Mode4x4[i] = 0;
	}
		
	if (!block_available_up)
	{
		if (!block_available_left)
		{
			img->Intra_Mode4x4[2] = 1;	//DC
			return;
		}
		else
		{
			img->Intra_Mode4x4[1] = 1;	//HOR
			img->Intra_Mode4x4[8] = 1;  //HOR_UP
			return;
		}			
	}
	else if (!block_available_left)
	{
		img->Intra_Mode4x4[0] = 1;		//VERT
		img->Intra_Mode4x4[7] = 1;		//VERT_LEFT
		img->Intra_Mode4x4[3] = 1;		//DIAG_DOWN_LEFT
		return;
	}

	for (i=0; i<9; i++)
		histogram[i] = 0;

	for (i=0; i<4; i++)
		for (j=0; j<4; j++) 
		{
			histogram[img->Edge_Map_Mode[BLK4x4][pic_y+i][pic_x+j]] += img->Edge_Map_Amp[pic_y+i][pic_x+j];
		}


	mode4=maximum(histogram,9);
	
	img->Intra_Mode4x4[mode4] = 1;
	img->Intra_Mode4x4[mode_order_rev[(mode_order[mode4]+7)%8]] = 1;
	img->Intra_Mode4x4[mode_order_rev[(mode_order[mode4]+1)%8]] = 1;
}

void ED_Intra8x8(int pic_x,int pic_y, int mb_available_up, int mb_available_left)
{
	int i, j, mode8, uv;
	int histogram[4];

	if (!mb_available_up)
	{
		if (!mb_available_left)
		{
			img->Intra_Mode8x8[2] = 1;	//DC
			return;
		}
		else
		{
			img->Intra_Mode8x8[1] = 1;	//HOR
			return;
		}
	}
	else if (!mb_available_left)
	{
		img->Intra_Mode8x8[0] = 1;	//VERT
		return;
	}

	for (uv = 0; uv < 2; uv++)
	{
		for (i=0; i<4; i++)
			histogram[i] = 0;

		for (i=0; i<8; i++)
			for (j=0; j<8; j++) 
			{
				histogram[img->Edge_Map_C_Mode[uv][pic_y+i][pic_x+j]] += img->Edge_Map_C_Amp[uv][pic_y+i][pic_x+j];
			}

		mode8=maximum(histogram,4);

		img->Intra_Mode8x8[mode8] = 1;
	}
}

//FYP3098: IntraPrediction = 2
void DF_gradient()
{
  int i, j, k, bmpH, bmpW, c_bmpH, c_bmpW;

  bmpH = input->img_height;
  bmpW = input->img_width;
  c_bmpH = bmpH/2;
  c_bmpW = bmpW/2;

  // Y picture
  for (k=0; k<2; k++)
  {
	 for (i=0; i<bmpH; i++)
       img->gradientY[k][i][0] = 0;
	 for (j=0; j<bmpW; j++)
	   img->gradientY[k][0][j] = 0;
  }

  for (i=1; i<bmpH; i++)
	for (j=1; j<bmpW; j++)
	{
	  img->gradientY[Gy][i][j] = imgY_org[i][j] - imgY_org[i][j-1];
	  img->gradientY[Gx][i][j] = imgY_org[i][j] - imgY_org[i-1][j];
	}


  // U&V pictures
  for (k=0; k<2; k++)
  {
	 for (i=0; i<c_bmpH; i++)
	 {
       img->gradientUV[U_pic][k][i][0] = 0;
	   img->gradientUV[V_pic][k][i][0] = 0;
	 }
	 for (j=0; j<c_bmpW; j++)
	 {
	   img->gradientUV[U_pic][k][0][j] = 0;
	   img->gradientUV[V_pic][k][0][j] = 0;
	 }
  }

  for (k=0; k<2; k++)
  {
	  for (i=1; i<c_bmpH; i++)
		for (j=1; j<c_bmpW; j++)
		{
		  img->gradientUV[k][Gy][i][j] = imgUV_org[k][i][j] - imgUV_org[k][i][j-1];
		  img->gradientUV[k][Gx][i][j] = imgUV_org[k][i][j] - imgUV_org[k][i-1][j];
		}
  }
}

void DF_Intra16x16(int pic_x, int pic_y, int mb_available_up, int mb_available_left)
{
  int i, j, mode16;
  int Gxx = 0;
  int Gyy = 0;
  int Gxy = 0;
  double tg;

	if (!mb_available_up)
	{
		if (!mb_available_left)
		{
			img->Intra_Mode16x16[2] = 1;	//DC
			return;
		}
		else
		{
			img->Intra_Mode16x16[1] = 1;	//HOR
			return;
		}
		
	}
	else if (!mb_available_left)
	{
			img->Intra_Mode16x16[0] = 1;	//VERT
			return;
	}

  for (i=0; i<16; i++)
	for (j=0; j<16; j++)
	{
	  Gxx += img->gradientY[Gx][pic_y+i][pic_x+j]*img->gradientY[Gx][pic_y+i][pic_x+j];
	  Gyy += img->gradientY[Gy][pic_y+i][pic_x+j]*img->gradientY[Gy][pic_y+i][pic_x+j];
	  Gxy += img->gradientY[Gx][pic_y+i][pic_x+j]*img->gradientY[Gy][pic_y+i][pic_x+j];
	}
	
//    coh = sqrt(((double)(Gxx - Gyy))*(Gxx - Gyy)+4*Gxy*Gxy)/(Gxx+Gyy);
	//coh = (fabs((double)(Gxx - Gyy)) +fabs((double)(2*Gxy)))/(double)(Gxx+Gyy);
//  if (coh<COH_THD)
//	return 2;

  if (fabs(Gxx-Gyy)<0.000001)
		mode16 = 3;
  else
  {
	  tg = ((double) 2*Gxy)/(Gxx-Gyy);
	  if (-1<tg<1)
	  {
		if (Gxx-Gyy<0)
		  mode16 = 0;
		else
		  mode16 = 1;
	  }
	  else
		  mode16 = 3;
  }
  img->Intra_Mode16x16[mode16] = 1;

}

void DF_Intra4x4(int pic_x, int pic_y, int block_available_up, int block_available_left)
{
  int i, j, mode4;
  int Gxx = 0;
  int Gyy = 0;
  int Gxy = 0;
  double tg;
  int mode_order[9] = {2,6,8,0,4,3,5,1,7};
  int mode_order_rev[8] = {3,7,0,5,4,6,1,8};

  // Initialization for 4x4 mode
  for (i=0; i<9; i++)
  {
	if (i == 2)
		img->Intra_Mode4x4[i] = 1;
	else
		img->Intra_Mode4x4[i] = 0;
  }


	if (!block_available_up)
	{
		if (!block_available_left)
		{
			img->Intra_Mode4x4[2] = 1;	//DC
			return;
		}
		else
		{
			img->Intra_Mode4x4[1] = 1;	//HOR
			img->Intra_Mode4x4[8] = 1;  //HOR_UP
			return;
		}			
	}
	else if (!block_available_left)
	{
		img->Intra_Mode4x4[0] = 1;		//VERT
		img->Intra_Mode4x4[7] = 1;		//VERT_LEFT
		img->Intra_Mode4x4[3] = 1;		//DIAG_DOWN_LEFT
		return;
	}

  for (i=0; i<4; i++)
	for (j=0; j<4; j++)
	{
	  Gxx += img->gradientY[Gx][pic_y+i][pic_x+j]*img->gradientY[Gx][pic_y+i][pic_x+j];
	  Gyy += img->gradientY[Gy][pic_y+i][pic_x+j]*img->gradientY[Gy][pic_y+i][pic_x+j];
	  Gxy += img->gradientY[Gx][pic_y+i][pic_x+j]*img->gradientY[Gy][pic_y+i][pic_x+j];
	}
	
//    coh = sqrt(((double)(Gxx - Gyy))*(Gxx - Gyy)+4*Gxy*Gxy)/(Gxx+Gyy);
	//coh = (fabs((double)(Gxx - Gyy)) +fabs((double)(2*Gxy)))/(double)(Gxx+Gyy);
//  if (coh<COH_THD)
//	return 2;

  if (fabs(Gxx-Gyy)<0.000001)
	mode4 = 3;
  else
  {
	  tg = ((double) 2*Gxy)/(Gxx-Gyy);
	  if (Gxx-Gyy<0)
	  {
		if (-0.41421356<=tg<0.41421356)
		  mode4 = 0;
		else if (0.41421356<=tg<2.41421356)
		  mode4 = 5;
		else if (tg>=2.41421356)
		  mode4 = 4;
		else if (tg <=-2.41421356)
		  mode4 = 3;
		else if (-2.41421356<=tg<-0.41421356)
		  mode4 = 7;

	  }
	  else
	  {
		if (-0.41421356<=tg<0.41421356)
		  mode4 = 1;
		else if (0.41421356<=tg<2.41421356)
		  mode4 = 8;
		else if (tg>=2.41421356)
		  mode4 = 3;
		else if (tg <=-2.41421356)
		  mode4 = 4;
		else if (-2.41421356<=tg<-0.41421356)
		  mode4 = 6;
	  }
		  
  }

  img->Intra_Mode4x4[mode4] = 1;
  img->Intra_Mode4x4[mode_order_rev[(mode_order[mode4]+7)%8]] = 1;
  img->Intra_Mode4x4[mode_order_rev[(mode_order[mode4]+1)%8]] = 1;

}

void DF_Intra8x8(int pic_x, int pic_y, int mb_available_up, int mb_available_left)
{
  int i, j, uv, mode8;
  int Gxx = 0;
  int Gyy = 0;
  int Gxy = 0;
  double tg;

	if (!mb_available_up)
	{
		if (!mb_available_left)
		{
			img->Intra_Mode8x8[2] = 1;	//DC
			return;
		}
		else
		{
			img->Intra_Mode8x8[1] = 1;	//HOR
			return;
		}
	}
	else if (!mb_available_left)
	{
		img->Intra_Mode8x8[0] = 1;	//VERT
		return;
	}

  	for (uv = 0; uv < 2; uv++)
	{
		for (i=0; i<8; i++)
			for (j=0; j<8; j++) 
			{
				Gxx += img->gradientUV[uv][Gx][pic_y+i][pic_x+j]*img->gradientUV[uv][Gx][pic_y+i][pic_x+j];
				Gyy += img->gradientUV[uv][Gy][pic_y+i][pic_x+j]*img->gradientUV[uv][Gy][pic_y+i][pic_x+j];
				Gxy += img->gradientUV[uv][Gx][pic_y+i][pic_x+j]*img->gradientUV[uv][Gy][pic_y+i][pic_x+j];
			}

		if (fabs(Gxx-Gyy)<0.000001)
			mode8 = 3;
		else
		{
			tg = ((double) 2*Gxy)/(Gxx-Gyy);
			if (-1<tg<1)
			{
				if (Gxx-Gyy<0)
					mode8 = 0;
				else
					mode8 = 1;
			}
			else
				mode8 = 3;
		}
		Gxx = Gyy = Gxy = 0;
		img->Intra_Mode8x8[mode8] = 1;
	}

	
//    coh = sqrt(((double)(Gxx - Gyy))*(Gxx - Gyy)+4*Gxy*Gxy)/(Gxx+Gyy);
	//coh = (fabs((double)(Gxx - Gyy)) +fabs((double)(2*Gxy)))/(double)(Gxx+Gyy);
//  if (coh<COH_THD)
//	return 2;
} 
//END FYP3098