import java.awt.*;
import java.awt.image.*;
import java.io.*;

// Adaptive Hybrid Filter
public class AHF extends vectorFilter
{
	int indent;

	// Creates object and initializes data
	public AHF(int size)
	{
		masksize = size;
		maskIndex = new int[masksize*masksize];
		array = new double[masksize*masksize];
		indent = (int) Math.floor(masksize/2);
	}

	// Runs the algorithm
	public void performEffect()
	{
		int counter, temp_val, temp_loc, R, G, B, amf;
		int out1, out2;
		double sum1, sum2, magV, magB, magA;
		int newPixels[] = new int [width*height];

		// Moves through input array pixel by pixel
		for (int y=indent;y<height-indent;y++)
		{
			for (int x=indent;x<width-indent;x++)
			{
				// Location of centre of mask
				int pointOffset = y*width+x;

			// VMF part

				// Calculate Index for mask
				counter = 0;
				for (int i=-indent;i<=indent;i++)
				{
					for (int j=-indent;j<=indent;j++)
					{
						maskIndex[counter] = (y+i)*width+(x+j);
						counter++;
					}
				}

				// Sorts pixels in mask by sum of distances
				calc_dist();
				b_sort();
				
				// Place VMF output in temp
				temp_val = pixels[maskIndex[0]];
				temp_loc = maskIndex[0];

			// AMF part

				// Reset Index for mask
				counter = 0;
				for (int i=-indent;i<=indent;i++)
				{
					for (int j=-indent;j<=indent;j++)
					{
						maskIndex[counter] = (y+i)*width+(x+j);
						counter++;
					}
				}

				// Calculates mean pixel in mask
				R = 0;
				G = 0;
				B = 0;
				for (int i=0; i<masksize*masksize; i++)
				{
					R += ((pixels[maskIndex[i]])>>16)&0xff;
					G += ((pixels[maskIndex[i]])>>8)&0xff;
					B += (pixels[maskIndex[i]])&0xff;
				}

				R = Math.round(R/(masksize*masksize));
				G = Math.round(G/(masksize*masksize));
				B = Math.round(B/(masksize*masksize));

				if (R > 255) R = 255;
				if (R < 0) R = 0;
				if (G > 255) G = 255;
				if (G < 0) G = 0;
				if (B > 255) B = 255;
				if (B < 0) B = 0;
				
				amf = 255<<24 | R<<16 | G<<8 | B;
				
				// Calculates amf pixel magnitude
				magA = calc_mag(amf);

			// BVDF part

				// Reset Index for mask
				counter = 0;
				for (int i=-indent;i<=indent;i++)
				{
					for (int j=-indent;j<=indent;j++)
					{
						maskIndex[counter] = (y+i)*width+(x+j);
						counter++;
					}
				}
				
				// Sorts pixels in mask by sum of angles
				calc_angle();
				b_sort();
				
			// If VMF = BVDF then output VMF result
				if (maskIndex[0] == temp_loc)
				{
					newPixels[pointOffset] = temp_val;
				}
				else
				{
					// Calc mag of VMF output pixel
					magV = calc_mag(temp_val);

					// Calc mag of BVDF output pixel
					R = (pixels[maskIndex[0]]>>16)&0xff;
					G = (pixels[maskIndex[0]]>>8)&0xff;
					B = (pixels[maskIndex[0]])&0xff;
					magB = calc_mag(pixels[maskIndex[0]]);
					
					// Calc out1
					if (magB!=0)
					{
						R = (int)Math.round((magV/magB)*R);
						G = (int)Math.round((magV/magB)*G);
						B = (int)Math.round((magV/magB)*B);
					}

					if (R > 255) R = 255;
					if (R < 0) R = 0;
					if (G > 255) G = 255;
					if (G < 0) G = 0;
					if (G > 255) B = 255;
					if (G < 0) B = 0;
					
					out1 = 255<<24 | R<<16 | G<<8 | B;

					// Calc out2
					R = (pixels[maskIndex[0]]>>16)&0xff;
					G = (pixels[maskIndex[0]]>>8)&0xff;
					B = (pixels[maskIndex[0]])&0xff;
					
					if (magB!=0)
					{
						R = (int)Math.round((magA/magB)*R);
						G = (int)Math.round((magA/magB)*G);
						B = (int)Math.round((magA/magB)*B);
					}

					if (R > 255) R = 255;
					if (R < 0) R = 0;
					if (G > 255) G = 255;
					if (G < 0) G = 0;
					if (G > 255) B = 255;
					if (G < 0) B = 0;
					
					out2 = 255<<24 | R<<16 | G<<8 | B;
					
					// Reset mask index
					counter = 0;
					for (int i=-indent;i<=indent;i++)
					{
						for (int j=-indent;j<=indent;j++)
						{
							maskIndex[counter] = (y+i)*width+(x+j);
							counter++;
						}
					}
					
					// Determines if output pixel is out1 or out2
					sum1=0;
					sum2=0;
					for (int i=0; i<masksize*masksize; i++)
					{
						sum1 += vect_dist(out1, pixels[maskIndex[i]]);
						sum2 += vect_dist(out2, pixels[maskIndex[i]]);
					}
					
					if (sum1 < sum2)
					{
						newPixels[pointOffset] = out1;
					}
					else
					{
						newPixels[pointOffset] = out2;
					}
				}
			}
		}
		this.pixels = newPixels;
	}
}
