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

public class hsi extends EffectFilter
{
	static int ASSIGNED = 1;
	static int UNASSIGNED = 0;
	static int CHROMATIC = 1;
	static int ACHROMATIC = 0;
	
	int newPixels[];
	int queue[];
	int segFlags[][];
	// segFlags[][0] = 1 if assigned, 0 if unassigned
	// segFlags[][1] = which region it is assigned to
	
	// user can set diff1 and diff2 to control sensitivity of seg
	// user can set achro* to control how achro is determined
	double diffChro,diffAchro;
	double achromSat,achromLowInt,achromHighInt;
	
	int avgr,avgg,avgb;
	int minPixel,maxPixel,numPixels;
	int count = 0;
	int regionCount = 0;
	int max = 0;

	public hsi(double val1,double val2,double val3,double val4,double val5)
	{
		diffChro = val1;
		diffAchro = val2;
		achromSat = val3;
		achromLowInt = val4;
		achromHighInt = val5;
	}
				
	public double hue(int r,int g,int b)
	{
		double temp,temp2;
		temp2 = inten(r,g,b);
		temp = Math.acos(1.0/2.0*(r-g+r-b)/Math.pow((Math.pow(r-g,2)
		+(r-b)*(g-b)),0.5))*180.0/Math.PI;
		
		if (b/temp2 > g/temp2) temp = 360.0 - temp;
		return temp;
	}
	
	public double satur(int r,int g,int b)
	{
		double temp;
		temp = (1.0-3.0/(r+g+b)*(Math.min(r,Math.min(g,b))))*100.0;
		
		return temp;
	}
	
	public double inten(int r,int g,int b)
	{
		double temp;
		temp =  1.0/3.0*(r+g+b)*100.0/255.0;
		
		return temp;
	}
	
	private int chromaType(int pos)
	{
		int r,g,b;
		
		r = (pixels[pos]>>16)&0xff;
		g = (pixels[pos]>>8)&0xff;
		b = (pixels[pos])&0xff;
		
		// determine if pixel is chromatic or achromatic
		if (satur(r,g,b) <= achromSat || inten(r,g,b) <= achromLowInt
			|| inten(r,g,b) >= achromHighInt)
		{
			return ACHROMATIC;
		}
		else
		{
			return CHROMATIC;
		}
	}
	
	public void setRGB()
	{
		int pos;
		avgr = Math.round(avgr/numPixels);
		avgg = Math.round(avgg/numPixels);
		avgb = Math.round(avgb/numPixels);		
		
		for (pos=minPixel;pos<=maxPixel;pos++)
		{
			if (segFlags[pos][1] == regionCount)
			{
				newPixels[pos] = 255<<24 | avgr<<16 | avgg<<8 | avgb;
			}
		}
	}
	
	public void setRegion(int pos,int r,int g,int b,double h,double s,double i,int type)
	{
		double h2,s2,i2;
		double diff_i,diff_c,theta;
		int r2,g2,b2;

		r2 = (pixels[pos] >> 16)&0xff;
		g2 = (pixels[pos] >> 8)&0xff;
		b2 = (pixels[pos])&0xff;
					
		diff_i = Math.abs(i - inten(r2,g2,b2));

		if (type == ACHROMATIC)
		{
			if (diff_i <= diffAchro)
			{
				avgr += r;
				avgg += g;
				avgb += b;
				numPixels++;
				// set min and max pixel position of region
				if (pos > maxPixel) maxPixel = pos;
				if (pos < minPixel) minPixel = pos;

				segFlags[pos][0] = ASSIGNED;
				segFlags[pos][1] = regionCount;
				queue[count] = pos;
				count++;
			}
		
		
		}
		else
		{
			theta = Math.abs(h - hue(r2,g2,b2));
			if (theta > 180.0) theta = 360.0 - theta;
			theta = theta*Math.PI/180.0;
			diff_c = Math.pow((Math.pow(s,2.0)+Math.pow(satur(r2,g2,b2),2.0)
				-2.0*s*satur(r2,g2,b2)*Math.cos(theta)),0.5);
			
			if (Math.pow(Math.pow(diff_i,2.0)+Math.pow(diff_c,2.0),0.5) <= diffChro)
			{
				avgr += r;
				avgg += g;
				avgb += b;
				numPixels++;
				// set min and max pixel position of region
				if (pos > maxPixel) maxPixel = pos;
				if (pos < minPixel) minPixel = pos;

				segFlags[pos][0] = ASSIGNED;
				segFlags[pos][1] = regionCount;
				queue[count] = pos;
				count++;
			}
		}
	}
	
	private void growRegion(int origPos,int type,int r,int g,int b,double h,double s,double i)
	{
		double diff;
		int pos,x,y;
		
		if (type == CHROMATIC)
			diff = diffChro;
		else
			diff = diffAchro;
		
		x = origPos % width;
		y = (int) Math.floor(origPos/width);
		// grow region for seed pixel and the grow outwards

		// top left position
		pos = origPos - width - 1;
		if (x != 0 && y != 0 && segFlags[pos][0] == UNASSIGNED && chromaType(pos) == type)
		{
			setRegion(pos,r,g,b,h,s,i,type);
		}
		
		// top position
		pos = origPos - width;
		if (y != 0 && segFlags[pos][0] == UNASSIGNED && chromaType(pos) == type)
		{
			setRegion(pos,r,g,b,h,s,i,type);
		}
			
		// top right position
		pos = origPos - width + 1;
		if (x != width-1 && y != 0 && segFlags[pos][0] == UNASSIGNED && chromaType(pos) == type)
		{
			setRegion(pos,r,g,b,h,s,i,type);
		}

		// left position
		pos = origPos - 1;
		if (x != 0 && segFlags[pos][0] == UNASSIGNED && chromaType(pos) == type)
		{
			setRegion(pos,r,g,b,h,s,i,type);
		}
		
		// right position
		pos = origPos + 1;
		if (x != width-1 && segFlags[pos][0] == UNASSIGNED && chromaType(pos) == type)
		{
			setRegion(pos,r,g,b,h,s,i,type);
		}
		
		// bottom left position
		pos = origPos + width - 1;
		if (x != 0 && y != height-1 && segFlags[pos][0] == UNASSIGNED && chromaType(pos) == type)
		{
			setRegion(pos,r,g,b,h,s,i,type);
		}

		// bottom position
		pos = origPos + width;
		if (y != height-1 && segFlags[pos][0] == UNASSIGNED && chromaType(pos) == type)
		{
			setRegion(pos,r,g,b,h,s,i,type);
		}

		// bottom right position
		pos = origPos + width + 1;
		if (x != width-1 && y != height-1 && segFlags[pos][0] == UNASSIGNED && chromaType(pos) == type)
		{
			setRegion(pos,r,g,b,h,s,i,type);
		}
	}

	private void findRegion(int type)
	{
		int x,y,pointOffset;
		int r,g,b;
		double h,s,i;
		
		for (y=0;y<height;y++)
		{
			for (x=0;x<width;x++)
			{
				pointOffset = y*width+x;
				
				// if unassigned and of right type (chro or achro)
				if (segFlags[pointOffset][0] == UNASSIGNED && chromaType(pointOffset) == type)
				{
					// reset values
					avgr = 0;
					avgg = 0;
					avgb = 0;
					numPixels = 0;
					maxPixel = 0;
					minPixel = width*height;
					count = 0;		
					
					// set pixel to assigned and assign it to first region
					segFlags[pointOffset][0] = ASSIGNED;
					segFlags[pointOffset][1] = regionCount;
	
					r = (pixels[pointOffset] >> 16)&0xff;
					g = (pixels[pointOffset] >> 8)&0xff;
					b = (pixels[pointOffset])&0xff;
					
					i = inten(r,g,b);
					if (type == CHROMATIC)
					{
						h = hue(r,g,b);
						s = satur(r,g,b);
					}
					else
					{
						/* don't need h and s value if achro */
						h = 0.0;
						s = 0.0;
					}
					
					avgr += r;
					avgg += g;
					avgb += b;
					numPixels++;

					// set min and max pixel position of region
					if (pointOffset > maxPixel) maxPixel = pointOffset;
					if (pointOffset < minPixel) minPixel = pointOffset;
					
					// grow region
					growRegion(pointOffset,type,r,g,b,h,s,i);					
					if (count > max) max = count;
					
					while (count > 0)
					{
						count--;
						growRegion(queue[count],type,r,g,b,h,s,i);
						if (count > max) max = count;
					}
					
					setRGB();
					// increment region counter, move to next region
					regionCount++;
				}
			}
		}
	}
	
	public void performEffect()
	{
		newPixels = new int[width*height];
		queue = new int[width*height/2];
		segFlags = new int[width*height][2];
		
		AIPP.statusBar.setText("Finding Chromatic Regions ...");
		findRegion(CHROMATIC);
		AIPP.statusBar.setText("Finding Achromatic Regions ...");
		findRegion(ACHROMATIC);
		AIPP.statusBar.setText("Image segmented: "+regionCount+" Regions");
		this.pixels = newPixels;
	}
}		
