import java.awt.*;
import java.util.*;
import java.lang.Math;

public class Soy extends BufferedApplet
{
	private final int COLUMN_X = 0;
	private final int COLUMN_Y = 1;
	
	private final double EPSILON = 1.0 / 20.0;
	private final int NUMPOINTS_TOP = 28;
	private final int NUMPOINTS_BOTTOM = 34;
	private final int NUMPOINTS_MOUTH = 12;
	
	private final int HEIGHT_EYE = 320;
	private final int HEIGHT_EYEBROW = 280;
	
	private int iPointsTop[][] = { {370, 360, 315, 275, 225, 195, 160, 143, 125,
							   130, 132, 153, 158, 181, 155, 173, 171, 210, 237, 
							   271, 308, 340, 370, 400, 405, 415, 390, 465}, 
							   {390, 429, 441, 419, 388, 413, 408, 410, 402, 390,
							   380, 385, 369, 345, 322, 285, 260, 242, 255, 267,
							   268, 257, 240, 254, 283, 330, 360, 427}};

	private int iPointsBottom[][] = { {390, 370, 348, 293, 173, 159, 148, 151,
								154, 158, 152, 115, 97, 91, 115, 133, 151, 188, 
								193, 203, 256, 272, 284, 330, 361, 386, 408, 
								415, 422, 453, 467, 509, 470, 445}, 
								{390, 359, 400, 403, 398, 392, 386, 394, 398, 
								404, 440, 430, 469, 482, 495, 485, 475, 489, 
								512, 531, 541, 523, 507, 500, 515, 524, 505, 
								479, 453, 466, 455, 412, 432, 410}};
								
	private int iPointsMouth[][] = { {400, 380, 315, 252, 190, 160, 170, 
									 194, 200, 197, 242, 322, 383}, 
									 {400, 440, 458, 458, 449, 420, 400, 
									 371, 330, 295, 308, 304, 288} };
	
	private int iMatrixBezier[][] = { {-1, 3, -3, 1}, {3, -6, 3, 0},
									{-3, 3, 0, 0}, {1, 0, 0, 0}};
	private int iWidth, iHeight;
	private boolean bIsInited = false;
	
	private Rectangle EyeLeft = new Rectangle(200, 320, 30, 40);
	private Rectangle EyeRight = new Rectangle(300, 320, 30, 40);
	private double[][] EyebrowLeft = {{-30,-10,1}, {30,-10,1}, {30,10,1}, {-30,10,1}};
	private double[][] EyebrowRight = {{-30,-10,1}, {30,-10,1}, {30,10,1}, {-30,10,1}};

	Matrix2D Matrix = new Matrix2D();
	private double[][] MatrixTransform = new double[3][3];
	
	Slider SldMouth;
	Slider SldEyebrowHeightLeft;
	Slider SldEyebrowHeightRight;
	Slider SldEyebrowRotateLeft;
	Slider SldEyebrowRotateRight;
	
	Slider SldColorR;
	Slider SldColorG;
	Slider SldColorB;
	
	private Color clrSoy = Color.green;
	
	public void render(Graphics g, double dTime)
		//will draw the budgers, it will be easy
	{
		if (bIsInited == false)
		{
			iWidth = bounds().width;
			iHeight = bounds().height;
			
			SldMouth = new Slider(5, 5, 100, 20);
			SldMouth.setLabel("Open Mouth");
			SldMouth.setValue(0);
			
			SldEyebrowHeightLeft = new Slider(150, 5, 100, 20);
			SldEyebrowHeightLeft.setLabel("Eyebrow Height");
			SldEyebrowHeightLeft.setValue(1);
			
			SldEyebrowRotateLeft = new Slider(260, 5, 100, 20);
			SldEyebrowRotateLeft.setLabel("Eyebrow Rotate");
			SldEyebrowRotateLeft.setValue(0.5);

			SldEyebrowHeightRight = new Slider(150, 30, 100, 20);
			SldEyebrowHeightRight.setLabel("Eyebrow Height");
			SldEyebrowHeightRight.setValue(1);
			
			SldEyebrowRotateRight = new Slider(260, 30, 100, 20);
			SldEyebrowRotateRight.setLabel("Eyebrow Rotate");
			SldEyebrowRotateRight.setValue(0.5);
			
			SldColorR = new Slider(5, iHeight-25, 100, 20);
			SldColorR.setLabel("Red");
			SldColorR.setValue(0);
			
			SldColorG= new Slider(110, iHeight-25, 100, 20);
			SldColorG.setLabel("Green");
			SldColorG.setValue(1);

			SldColorB = new Slider(215, iHeight-25, 100, 20);
			SldColorB.setLabel("Blue");
			SldColorB.setValue(0);

			bIsInited = true;	
		}
		g.setColor(Color.white);
		g.fillRect(0,0,600,600);
		
		clrSoy = new Color((float)SldColorR.getValue(), (float)SldColorG.getValue(), (float)SldColorB.getValue());
		
		drawShape(g, iPointsMouth, clrSoy.darker().darker());
		drawShape(g, iPointsBottom, clrSoy);
		drawShape(g, iPointsTop, clrSoy);
		
		//update eyes
		EyeLeft.setLocation(EyeLeft.x, (int)(HEIGHT_EYE - 100*SldMouth.getValue()));
		EyeRight.setLocation(EyeRight.x, (int)(HEIGHT_EYE - 100*SldMouth.getValue()));
		//draw eyes
		g.setColor(Color.black);
		g.fillOval(EyeLeft.x, EyeLeft.y, EyeLeft.width, EyeLeft.height);
		g.fillOval(EyeRight.x, EyeRight.y, EyeRight.width, EyeRight.height);
		
		//update left eyebrow
		Matrix.IdentityMatrix(MatrixTransform);
		Matrix.Rotate(MatrixTransform, Math.PI/2 * (0.5 - SldEyebrowRotateLeft.getValue()));
		Matrix.Translate(MatrixTransform, 215, (HEIGHT_EYEBROW - 10) + (30*SldEyebrowHeightLeft.getValue()) - (100*SldMouth.getValue()));
		for (int i=0; i<4; i++)
			Matrix.Transform(MatrixTransform, EyebrowLeft[i]);
		
		g.setColor(Color.black);
		g.fillPolygon(Column(EyebrowLeft, COLUMN_X), Column(EyebrowLeft, COLUMN_Y), 4);
		
		Matrix.IdentityMatrix(MatrixTransform);
		Matrix.Translate(MatrixTransform, -215, -(HEIGHT_EYEBROW - 10) - (30*SldEyebrowHeightLeft.getValue()) + (100*SldMouth.getValue()));
		Matrix.Rotate(MatrixTransform, -Math.PI/2 * (0.5 - SldEyebrowRotateLeft.getValue()));
		for (int i=0; i<4; i++)
			Matrix.Transform(MatrixTransform, EyebrowLeft[i]);
		
		//update right eyebrow
		Matrix.IdentityMatrix(MatrixTransform);
		Matrix.Rotate(MatrixTransform, -Math.PI/2 * (0.5 - SldEyebrowRotateRight.getValue()));
		Matrix.Translate(MatrixTransform, 315, (HEIGHT_EYEBROW - 10) + (30*SldEyebrowHeightRight.getValue()) - (100*SldMouth.getValue()));
		for (int i=0; i<4; i++)
			Matrix.Transform(MatrixTransform, EyebrowRight[i]);
		
		g.setColor(Color.black);
		g.fillPolygon(Column(EyebrowRight, COLUMN_X), Column(EyebrowRight, COLUMN_Y), 4);
		
		Matrix.IdentityMatrix(MatrixTransform);
		Matrix.Translate(MatrixTransform, -315, -(HEIGHT_EYEBROW - 10) - (30*SldEyebrowHeightRight.getValue()) + (100*SldMouth.getValue()));
		Matrix.Rotate(MatrixTransform, Math.PI/2 * (0.5 - SldEyebrowRotateRight.getValue()));
		for (int i=0; i<4; i++)
			Matrix.Transform(MatrixTransform, EyebrowRight[i]);
		
		g.drawString("Left:", 110, 20);
		g.drawString("Right:", 110, 45);
		
		SldMouth.render(g);
		SldEyebrowHeightLeft.render(g);
		SldEyebrowRotateLeft.render(g);
		SldEyebrowHeightRight.render(g);
		SldEyebrowRotateRight.render(g);
		
		SldColorR.render(g);
		SldColorG.render(g);
		SldColorB.render(g);
	}
	
	public void drawShape(Graphics g, int[][] iShape, Color clrColor)
	{
		int iX[] = new int[(int)(iShape[0].length*(1/EPSILON))+1];
		int iY[] = new int[(int)(iShape[1].length*(1/EPSILON))+1];
		int iXCoords[] = new int[4];
		int iYCoords[] = new int[4];
		int iCount = 0;
		
		for (int i=0; i<iShape[0].length-1; i+=3)
		{
			for (int j=0; j<4; j++)
			{
				iXCoords[j] = iShape[0][i+j];
				
				if ((iShape[0].length == 28) && ((i+j != 0) && (i+j != iShape[0].length-1) && (i+j != iShape[0].length-2)))
				{
					iYCoords[j] = (int)(iShape[1][i+j]-100*SldMouth.getValue());
				}
				else
					iYCoords[j] = iShape[1][i+j];
			}
			iXCoords = MMultiply(iXCoords);
			iYCoords = MMultiply(iYCoords);
			
			for (double t=0; t<1; t+=EPSILON)
			{
				iX[iCount] = (int)X(t, iXCoords);
				iY[iCount] = (int)(Y(t, iYCoords));				
				iCount++;
			}
		}
		iX[iCount] = iX[0];
		iY[iCount] = iY[0];
		
		g.setColor(clrColor);
		g.fillPolygon(iX, iY, (int)(iShape[0].length*(1/EPSILON))+1);
		
		for (int i=0; i<iX.length; i++)
		{
			if ((i>3) && (i < iCount))
			{
				g.setColor(Color.black);			
				g.drawLine(iX[i], iY[i], iX[i-1], iY[i-1]);
			}
		}
	}

	public int[] MMultiply(int[] iXcoords)
	{
		int iTemp[] = new int[4];
		for (int i=0; i<4; i++)
			iTemp[i] = iXcoords[i];
			
		for (int j=0; j<4; j++)
		{
			iXcoords[j] = (	(iMatrixBezier[j][0] * iTemp[0]) +
							(iMatrixBezier[j][1] * iTemp[1]) +
							(iMatrixBezier[j][2] * iTemp[2]) +
							(iMatrixBezier[j][3] * iTemp[3]) );
		}		
		return iXcoords;
	}
	
	public int[] Column(double[][] iShape, int iColumn)
	{
		int iTemp[] = new int[4];
		for(int i=0; i<4; i++)
			iTemp[i] = (int)iShape[i][iColumn];
			
		return iTemp;
	}
	
	public double X(double t, int[] iCoords)
	{
		return iCoords[0]*(t*t*t) + iCoords[1]*(t*t) + iCoords[2]*(t) + iCoords[3];
	}
	
	public double Y(double t, int[] iCoords)
	{
		return iCoords[0]*(t*t*t) + iCoords[1]*(t*t) + iCoords[2]*(t) + iCoords[3];
	}
	
	public boolean mouseDown(Event e, int x, int y)
	{
		SldMouth.down(x,y);
		SldEyebrowHeightLeft.down(x,y);
		SldEyebrowRotateLeft.down(x,y);
		SldEyebrowHeightRight.down(x,y);
		SldEyebrowRotateRight.down(x,y);
		SldColorR.down(x,y);
		SldColorG.down(x,y);
		SldColorB.down(x,y);
		return true;	
	}

	public boolean mouseDrag(Event e, int x, int y)
	{
		SldMouth.drag(x,y);
		SldEyebrowHeightLeft.drag(x,y);
		SldEyebrowRotateLeft.drag(x,y);
		SldEyebrowHeightRight.drag(x,y);
		SldEyebrowRotateRight.drag(x,y);
		SldColorR.drag(x,y);
		SldColorG.drag(x,y);
		SldColorB.drag(x,y);
		return true;	
	}
}