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

// Bayesian Adaptive Filter with Median and Mean Sub-Filters
public class BFMA extends vectorFilter
{
	double wnorm[];
	int indent;
	double dis[] = new double[3];

	// Create object and initialize data
	public BFMA(int size)
	{
		masksize = size;
		maskIndex = new int[masksize*masksize];
		wnorm = new double[3];
		array = new double[masksize*masksize];
		indent = (int) Math.floor(masksize/2);
	}

	public void fuzzy_weight(int mask)
	{
		double w[] = new double[mask];
		double wtotal = 0;

		for(int i=0;i<mask;i++)
		{
			// w[i] is the fuzzy weight for each vector
			if (dis[i] == 0)
			{
				w[i] = 0;
			}
			else
			{
				w[i] = 1/(dis[i]*dis[i]);
			}
			wtotal += w[i];
		}

		for(int i=0;i<mask;i++)
		{
			// Normalize the fuzzy weights
			if (wtotal == 0)
			{
				wnorm[i] = 0;
			}
			else
			{
				wnorm[i] = w[i]/wtotal;
			}
		}
	}

	// Runs algorithm
	public void performEffect()
	{
		int counter, r, g, b;	
		int aPixels[] = new int [width*height];
		int vPixels[] = new int [width*height];
		int newPixels[] = new int [width*height];
		double temp;
		double worig[] = new double[3];
		
	// AMF filter
		// 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;
				
				// 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++;
					}
				}
				
				r = 0;
				g = 0;
				b = 0;

				// Calculates the mean pixel within the mask
				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;

				aPixels[pointOffset] = (255<<24) | r<<16 | g<<8 | b;
			}
		}
		
	// VMF Filter
		// Moves through the 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;

				// 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();
				
				// Outputs pixel with lowest sum of distances
				vPixels[pointOffset] = pixels[maskIndex[0]];
			}
		}
		
	// BFMA Begins
		// 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;
				
				// 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++;
					}
				}

				dis[0]=0;
				dis[1]=0;
				dis[2]=0;
				
				// Distance between amf centre pixel and all other pixels
				// in input mask
				for (int i=0; i<masksize*masksize; i++)
				{
					dis[0] += vect_dist(aPixels[pointOffset],pixels[maskIndex[i]]);
				}

				// Distance between vmf centre pixel and all other pixels
				// in input mask
				for (int i=0; i<masksize*masksize; i++)
				{
					dis[1] += vect_dist(vPixels[pointOffset],pixels[maskIndex[i]]);
				}

				// Distance between centre pixel of input mask and all
				// other pixels in input mask
				calc_centre();
				for (int i=0; i<masksize*masksize; i++)
				{
					dis[2] += array[i];
				}
				
				// Apply fuzzy weighting factor
				fuzzy_weight(3);
				worig = wnorm;

				// Sort the weights
				for (int i=0;i<3;i++)
				{
					for (int j=2; j>i; --j)
					{
						if (wnorm[j-1] > wnorm[j])
						{
							temp = wnorm[j-1];
							wnorm[j-1] = wnorm[j];
							wnorm[j] = temp;
						}
					}
				}
				// Output result of filter based on weighting
				if (wnorm[0] == worig[0])
				{
					newPixels[pointOffset] = aPixels[pointOffset];
				}
				else if (wnorm[0] == worig[1])
				{
					newPixels[pointOffset] = vPixels[pointOffset];
				}
				else
				{
					newPixels[pointOffset] = pixels[pointOffset];
				}
			}
		}
		this.pixels = newPixels;
	}
}
