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

// Edge detection with Difference Vector adaptive filter operators
public class adapt_edge extends edgeFilter
{
	float min_edge;
	int indent;
	double wnorm[];
	
	// Creates object and initializes data
	public adapt_edge(float min, int size)
	{
		min_edge = min;
		masksize = size;
		maskIndex = new int[masksize*masksize];
		indent = (int) Math.floor(masksize/2);
		array = new double[indent*indent+1];
		wnorm = new double[indent*indent+1];
	}
	
	// Reset the array of values
	public void reset(int size)
	{
		for (int i=0; i<size; i++)
		{
			array[i] = 0;
		}
	}
	
	// Applies weighting factor to the data array
	public void weight(int mask)
	{
		double w[] = new double[mask];
		double wtotal = 0, dtotal = 0;

		// Calculate the sum of distances in the mask
		for(int i=0;i<mask;i++)
		{
			dtotal += array[i];
		}

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

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

	// Runs algorithm
	public void performEffect()
	{
		float max_intensity = 0;
		double d0,d45,d90,d135,sumr,sumg,sumb;
		int pointOffset,x1=0,x2=0,y1=0,y2=0,counter,sumR,sumG,sumB;
		int newPixels[] = new int[width*height];
		double edge[] = new double[width*height];
		
		// 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
				pointOffset = y*width+x;

			// 0 degrees and 180 degrees subwindows

				// Find vector distances for 0 degree region
				for (int i=0;i<=indent;i++)
				{
					x1 = (y-i)*width+x;
					maskIndex[i] = x1;
					for (int j=0;j<=indent;j++)
					{
						x2 = (y-j)*width+x;
						array[i] += vect_dist(pixels[x1],pixels[x2]);
					}
				}
				
				// Sort array by distances
				b_sort(indent+1);
				
				// Assign each pixel a weight
				weight(indent+1);
				
				sumr=0;
				sumg=0;
				sumb=0;
				
				// Using pixel values and weights, calculate output pixel
				for (int i=0; i<=indent; i++)
				{
					sumr += wnorm[i]*((pixels[maskIndex[i]] >> 16)&0xff);
					sumg += wnorm[i]*((pixels[maskIndex[i]] >> 8)&0xff);
					sumb += wnorm[i]*((pixels[maskIndex[i]])&0xff);
				}

				sumR = (int)Math.round(sumr);
				sumG = (int)Math.round(sumg);
				sumB = (int)Math.round(sumb);

				if (sumR > 255) sumR = 255;
				if (sumR < 0) sumR = 0;
				if (sumG > 255) sumG = 255;
				if (sumG < 0) sumG = 0;
				if (sumB > 255) sumB = 255;
				if (sumB < 0) sumB = 0;

				x1 = (255<<24) | sumR<<16 | sumG <<8 | sumB;

				// Reset the array
				reset(indent+1);
				
				// Find RGB vector for 180 degree region
				for (int i=0;i<=indent;i++)
				{
					y1 = (y+i)*width+x;
					maskIndex[i] = y1;
					for (int j=0;j<=indent;j++)
					{
						y2 = (y+j)*width+x;
						array[i] += vect_dist(pixels[y1],pixels[y2]);
					}
				}
				
				// Sort array by distances
				b_sort(indent+1);
				
				// Assign each pixel a weight
				weight(indent+1);
				
				sumr=0;
				sumg=0;
				sumb=0;
				
				// Using pixel values and weights, calculate output pixel
				for (int i=0; i<=indent; i++)
				{
					sumr += wnorm[i]*((pixels[maskIndex[i]] >> 16)&0xff);
					sumg += wnorm[i]*((pixels[maskIndex[i]] >> 8)&0xff);
					sumb += wnorm[i]*((pixels[maskIndex[i]])&0xff);
				}

				sumR = (int)Math.round(sumr);
				sumG = (int)Math.round(sumg);
				sumB = (int)Math.round(sumb);

				if (sumR > 255) sumR = 255;
				if (sumR < 0) sumR = 0;
				if (sumG > 255) sumG = 255;
				if (sumG < 0) sumG = 0;
				if (sumB > 255) sumB = 255;
				if (sumB < 0) sumB = 0;

				y1 = (255<<24) | sumR<<16 | sumG <<8 | sumB;
				
				// Calculate the distance between the two region vectors
				d0 = vect_dist(x1,y1);
								
			// 90 degrees and 270 degrees subwindows

				// Reset the array
				reset(indent+1);
				
				// Find RGB vector for 90 degree region
				for (int i=0;i<=indent;i++)
				{
					x1 = y*width+x-i;
					maskIndex[i] = x1;
					for (int j=0;j<=indent;j++)
					{
						x2 = y*width+x-j;
						array[i] += vect_dist(pixels[x1],pixels[x2]);
					}
				}
				
				// Sort array by distances
				b_sort(indent+1);
				
				// Assign each pixel a weight
				weight(indent+1);
				
				sumr=0;
				sumg=0;
				sumb=0;
				
				// Using pixel values and weights, calculate output pixel
				for (int i=0; i<=indent; i++)
				{
					sumr += wnorm[i]*((pixels[maskIndex[i]] >> 16)&0xff);
					sumg += wnorm[i]*((pixels[maskIndex[i]] >> 8)&0xff);
					sumb += wnorm[i]*((pixels[maskIndex[i]])&0xff);
				}

				sumR = (int)Math.round(sumr);
				sumG = (int)Math.round(sumg);
				sumB = (int)Math.round(sumb);

				if (sumR > 255) sumR = 255;
				if (sumR < 0) sumR = 0;
				if (sumG > 255) sumG = 255;
				if (sumG < 0) sumG = 0;
				if (sumB > 255) sumB = 255;
				if (sumB < 0) sumB = 0;

				x1 = (255<<24) | sumR<<16 | sumG <<8 | sumB;

				// Reset the array
				reset(indent+1);
				
				// Find RGB vector for 270 degree region
				for (int i=0;i<=indent;i++)
				{
					y1 = y*width+x+i;
					maskIndex[i] = y1;
					for (int j=0;j<=indent;j++)
					{
						y2 = y*width+x+j;
						array[i] += vect_dist(pixels[y1],pixels[y2]);
					}
				}
				
				// Sort array by distances
				b_sort(indent+1);
				
				// Assign each pixel a weight
				weight(indent+1);
				
				sumr=0;
				sumg=0;
				sumb=0;
				
				// Using pixel values and weights, calculate output pixel
				for (int i=0; i<=indent; i++)
				{
					sumr += wnorm[i]*((pixels[maskIndex[i]] >> 16)&0xff);
					sumg += wnorm[i]*((pixels[maskIndex[i]] >> 8)&0xff);
					sumb += wnorm[i]*((pixels[maskIndex[i]])&0xff);
				}

				sumR = (int)Math.round(sumr);
				sumG = (int)Math.round(sumg);
				sumB = (int)Math.round(sumb);

				if (sumR > 255) sumR = 255;
				if (sumR < 0) sumR = 0;
				if (sumG > 255) sumG = 255;
				if (sumG < 0) sumG = 0;
				if (sumB > 255) sumB = 255;
				if (sumB < 0) sumB = 0;

				y1 = (255<<24) | sumR<<16 | sumG <<8 | sumB;

				// Calculate the distance between the two region vectors
				d90 = vect_dist(x1,y1);
				
			// 45 degrees and 225 degrees subwindows

				// Reset the array
				reset(indent+1);
				
				counter = 0;
				// Find RGB vector for 45 degree region
				for (int i=0;i<=indent;i++)
				{
					for (int k=0;k<=indent;k++)
					{
						x1 = (y-i)*width+x+k;
						maskIndex[counter] = x1;
						counter++;
						for (int j=0;j<=indent;j++)
						{
							for (int l=0;l<=indent;l++)
							{
								if ((i==0 && j==0) || (i!=0 && j!=0))
								{
									x2 = (y-j)*width+x+l;
									array[i] += vect_dist(pixels[x1],pixels[x2]);
								}
							}
						}
					}
				}
				
				// Sort array by distances
				b_sort(indent*indent+1);
				
				// Assign each pixel a weight
				weight(indent*indent+1);
				
				sumr=0;
				sumg=0;
				sumb=0;
				
				// Using pixel values and weights, calculate output pixel
				for (int i=0; i<=indent*indent; i++)
				{
					sumr += wnorm[i]*((pixels[maskIndex[i]] >> 16)&0xff);
					sumg += wnorm[i]*((pixels[maskIndex[i]] >> 8)&0xff);
					sumb += wnorm[i]*((pixels[maskIndex[i]])&0xff);
				}

				sumR = (int)Math.round(sumr);
				sumG = (int)Math.round(sumg);
				sumB = (int)Math.round(sumb);

				if (sumR > 255) sumR = 255;
				if (sumR < 0) sumR = 0;
				if (sumG > 255) sumG = 255;
				if (sumG < 0) sumG = 0;
				if (sumB > 255) sumB = 255;
				if (sumB < 0) sumB = 0;

				x1 = (255<<24) | sumR<<16 | sumG <<8 | sumB;

				// Reset the array
				reset(indent*indent+1);
				
				counter = 0;
				// Find RGB vector for 225 degree region
				for (int i=0;i<=indent;i++)
				{
					for (int k=0;k<=indent;k++)
					{
						y1 = (y+i)*width+x-k;
						maskIndex[counter] = y1;
						counter++;
						for (int j=0;j<=indent;j++)
						{
							for (int l=0;l<=indent;l++)
							{
								if ((i==0 && j==0) || (i!=0 && j!=0))
								{
									y2 = (y+j)*width+x-l;
									array[i] += vect_dist(pixels[y1],pixels[y2]);
								}
							}
						}
					}
				}

				// Sort array by distances
				b_sort(indent*indent+1);
				
				// Assign each pixel a weight
				weight(indent*indent+1);
				
				sumr=0;
				sumg=0;
				sumb=0;
				
				// Using pixel values and weights, calculate output pixel
				for (int i=0; i<=indent*indent; i++)
				{
					sumr += wnorm[i]*((pixels[maskIndex[i]] >> 16)&0xff);
					sumg += wnorm[i]*((pixels[maskIndex[i]] >> 8)&0xff);
					sumb += wnorm[i]*((pixels[maskIndex[i]])&0xff);
				}

				sumR = (int)Math.round(sumr);
				sumG = (int)Math.round(sumg);
				sumB = (int)Math.round(sumb);

				if (sumR > 255) sumR = 255;
				if (sumR < 0) sumR = 0;
				if (sumG > 255) sumG = 255;
				if (sumG < 0) sumG = 0;
				if (sumB > 255) sumB = 255;
				if (sumB < 0) sumB = 0;

				y1 = (255<<24) | sumR<<16 | sumG <<8 | sumB;

				// Calculate the distance between the two region vectors
				d45 = vect_dist(x1,y1);
				
			// 135 degrees and 315 degrees subwindows

				// Reset the array
				reset(indent*indent+1);
				
				counter = 0;
				// Find RGB vector for 135 degree region
				for (int i=0;i<=indent;i++)
				{
					for (int k=0;k<=indent;k++)
					{
						x1 = (y-i)*width+x-k;
						maskIndex[counter] = x1;
						counter++;
						for (int j=0;j<=indent;j++)
						{
							for (int l=0;l<=indent;l++)
							{
								if ((i==0 && j==0) || (i!=0 && j!=0))
								{
									x2 = (y-j)*width+x-l;
									array[i] += vect_dist(pixels[x1],pixels[x2]);
								}
							}
						}
					}
				}
				
				// Sort array by distances
				b_sort(indent*indent+1);
				
				// Assign each pixel a weight
				weight(indent*indent+1);
				
				sumr=0;
				sumg=0;
				sumb=0;
				
				// Using pixel values and weights, calculate output pixel
				for (int i=0; i<=indent*indent; i++)
				{
					sumr += wnorm[i]*((pixels[maskIndex[i]] >> 16)&0xff);
					sumg += wnorm[i]*((pixels[maskIndex[i]] >> 8)&0xff);
					sumb += wnorm[i]*((pixels[maskIndex[i]])&0xff);
				}

				sumR = (int)Math.round(sumr);
				sumG = (int)Math.round(sumg);
				sumB = (int)Math.round(sumb);

				if (sumR > 255) sumR = 255;
				if (sumR < 0) sumR = 0;
				if (sumG > 255) sumG = 255;
				if (sumG < 0) sumG = 0;
				if (sumB > 255) sumB = 255;
				if (sumB < 0) sumB = 0;

				x1 = (255<<24) | sumR<<16 | sumG <<8 | sumB;

				// Reset the array
				reset(indent*indent+1);
				
				counter = 0;
				// Find RGB vector for 315 degree region
				for (int i=0;i<=indent;i++)
				{
					for (int k=0;k<=indent;k++)
					{
						y1 = (y+i)*width+x+k;
						maskIndex[counter] = y1;
						counter++;
						for (int j=0;j<=indent;j++)
						{
							for (int l=0;l<=indent;l++)
							{
								if ((i==0 && j==0) || (i!=0 && j!=0))
								{
									y2 = (y+j)*width+x+l;
									array[i] += vect_dist(pixels[y1],pixels[y2]);
								}
							}
						}
					}
				}

				// Sort array by distances
				b_sort(indent*indent+1);
				
				// Assign each pixel a weight
				weight(indent*indent+1);
				
				sumr=0;
				sumg=0;
				sumb=0;
				
				// Using pixel values and weights, calculate output pixel
				for (int i=0; i<=indent*indent; i++)
				{
					sumr += wnorm[i]*((pixels[maskIndex[i]] >> 16)&0xff);
					sumg += wnorm[i]*((pixels[maskIndex[i]] >> 8)&0xff);
					sumb += wnorm[i]*((pixels[maskIndex[i]])&0xff);
				}

				sumR = (int)Math.round(sumr);
				sumG = (int)Math.round(sumg);
				sumB = (int)Math.round(sumb);

				if (sumR > 255) sumR = 255;
				if (sumR < 0) sumR = 0;
				if (sumG > 255) sumG = 255;
				if (sumG < 0) sumG = 0;
				if (sumB > 255) sumB = 255;
				if (sumB < 0) sumB = 0;

				y1 = (255<<24) | sumR<<16 | sumG <<8 | sumB;

				// Calculate the distance between the two median vectors
				d135 = vect_dist(x1,y1);
				
			// Finding the Edges
			
				edge[pointOffset] = d0;
				if (d45 > edge[pointOffset])
					edge[pointOffset] = d45;
				if (d90 > edge[pointOffset])
					edge[pointOffset] = d90;
				if (d135 > edge[pointOffset])
					edge[pointOffset] = d135;
				if (edge[pointOffset] > max_intensity)
					max_intensity = (float)edge[pointOffset];
			}
		}

		// Place the edges on an output array
		if (max_intensity > 0.0)
		{
			// Run through image pixel by pixel
			for (int y=0; y<height;y++)
			{
				for (int x=0; x<width; x++)
				{
					// Position of centre pixel
					pointOffset = y*width+x;
					
					edge[pointOffset] = edge[pointOffset]/max_intensity;
					
					// Set to grey if edge
					if (edge[pointOffset] >= min_edge)
					{
						newPixels[pointOffset] = 0xff000000 +
						(0x010101*(255-(int)(edge[pointOffset]*255)));
					}
					// Set to white
					else
					{
						newPixels[pointOffset] = 0x01010101*255;
					}
				}
			}
		}
		this.pixels = newPixels;
	}
}
