import java.applet.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;

public class CakeMan extends Applet implements Runnable, KeyListener
{
	class myMouse extends MouseAdapter implements MouseMotionListener
	{
	  boolean pressed = false;
	  int x=0, y=0;
	  public void mousePressed(MouseEvent e)
	  {
	    x = e.getX();
	    y = e.getY();
	    pressed = true;
	    runner = null;
	  }
	  public void mouseReleased(MouseEvent e)
	  {
	    pressed = false;
	    start();
	  }
	  public void mouseDragged(MouseEvent e)
	  {
	    if(pressed)
	    {
	      xAngel += x - e.getX();
	      yAngel += y - e.getY();
	      x = e.getX();
	      y = e.getY();
	      repaint();
	    }
	  }
	  public void mouseMoved(MouseEvent e) {}
	}

	public void init()
	{
	  new SinCosTabel();
	  setSize(width, height);
	  i = createImage(width, height);
	  addMouseListener(mm);
	  addMouseMotionListener(mm);
	  halfWidth = width/2;
	  halfHeight = height/2;

	  for(int i = 0; i < vertexes.length; i++)
	  {
	    for(int j = 0; j < vertexes[i].length; j++)
	      vertexes[i][j] *= scale;
	  }
	  addKeyListener(this);
	}
	public void start()
	{
	  runner = new Thread(this);
	  runner.start();
	}
	public void stop()
	{
	  if(runner != null)
	  {
	    runner.interrupt();
	    runner = null;
	  }
	}
	public void keyTyped(java.awt.event.KeyEvent evt) {}
	int saveN = 0;
	public void keyPressed(java.awt.event.KeyEvent evt)
	{ System.out.println(evt.getKeyCode());
	  switch(evt.getKeyCode())
	  {
	    case 82: rotate = !rotate; break;
	    case 70: fly = !fly; break;
	    case 90: if(!zoom) n = saveN; else saveN = n; zoom = !zoom; break;
	    case 67: clear = !clear; break;
	    case 83: scroll = !scroll; break;
	  }
	}
	public void keyReleased(java.awt.event.KeyEvent evt) {}

	static int width = 400;
	static int height = 400;
	int halfWidth = 100;
	int halfHeight = 100;
	int dist = 10;
	int scale = 10;
	int n = 0;
	myMouse mm = new myMouse();
	Image i = null;

	static int FACTOR =200;
	Thread runner = null;
	Color backgroundColor = Color.white;
	boolean fly = false;
	boolean rotate = true;
	boolean zoom = false;
	boolean clear = true;
	boolean scroll = false;

	float vertexes[][] = { {-4, 4, dist}, {4, 4, dist}, {4, -4, dist}, { -4, -4, dist},   //Body
	                       {-3, -4, dist}, {-3, -9, dist}, {-1, -9, dist}, {-1, -4, dist},//Leg
	                       {3, -4, dist}, {3, -9, dist}, {1, -9, dist}, {1, -4, dist},    //Leg
	                       {-2, 4, dist}, {-2, 7, dist}, {2, 7, dist}, {2, 4, dist},      //Head.
	                       {-4, 4, dist}, {-8, 0, dist}, {-6, -2, dist}, {-4, 2, dist}, //arm
	                       {4,4, dist}, {8, 0, dist}, {6, -2, dist}, {4, 2, dist}, //arm
	                       {-1,6,dist}, {-1.5F, 6, dist}, {-1.5F, 6.5F,dist}, {-1, 6.5F,dist},//ųje
	                       {1,6,dist}, {1.5F, 6, dist}, {1.5F, 6.5F,dist}, {1, 6.5F,dist},
	                       {1.5F, 5,dist},{0,5,dist},{-1.5F,5,dist}, {0,4.5F, dist}, //mund
	                       {-0.3F,5.5F,dist}, {-0.3F,6,dist},{0.3F,6,dist}, {0.3F,5.5F,dist},
	                     };
	float convertedVertexes[][] = new float[vertexes.length][3];
	int shapes[][] = { {0, 1, 2, 3}, { 4, 5, 6, 7}, {8,9,10,11}, { 12, 13,14,15}, {16,17,18,19}, {20,21,22,23},
	                   {24,25,26,27}, {28,29,30,31}, { 32, 33,34,35},{36,37,38,39} };
	Color shapesColors[] = { Color.blue, Color.green, Color.green, Color.yellow, Color.red, Color.red, Color.blue,Color.blue,Color.red,Color.green};

	int zAngel=0, yAngel=0, xAngel=0;

	public void run()
	{
	  mm.pressed = false;
	  while(!runner.interrupted() && !mm.pressed)
	  {
	    if(zoom)
	    {
	      if((n/50)%2 == 0)
	        dist++;
	      else
	        dist--;
	      changeDistance(dist);
	    }
	    n++;
	    if(rotate)
	    {
	      if((n/100)%2 == 0)
	      { xAngel++;
	        zAngel++;
	      }
	      else
	      { yAngel++;
	        zAngel++;
	      }
	    }
	    repaint();
	    try{ runner.sleep(10); }catch(InterruptedException e) {}
	  }
	}
	void changeDistance(int distance)
	{
	  if(!fly)
	    distance *= scale;
	  for(int i = 0; i < vertexes.length; i++)
        vertexes[i][2] = distance;
	}
	void rotate(int nr)
	{
	  float x = vertexes[nr][0];
	  float y = vertexes[nr][1];
	  float z = vertexes[nr][2]-(dist*scale);

	  float newX = (float)(vertexes[nr][0] * SinCosTabel.cos[zAngel] - y * SinCosTabel.sin[zAngel]);
      float newY = (float)(x * SinCosTabel.sin[zAngel] + y * SinCosTabel.cos[zAngel]);
      convertedVertexes[nr][0] = (float)(newX * SinCosTabel.cos[yAngel] - z * SinCosTabel.sin[yAngel]);
      float newZ = (float)(newX * SinCosTabel.sin[yAngel] + z * SinCosTabel.cos[yAngel]);
      convertedVertexes[nr][1] = (float)(newY * SinCosTabel.cos[xAngel] - newZ * SinCosTabel.sin[xAngel]);
      convertedVertexes[nr][2] = (float)(newY * SinCosTabel.sin[xAngel] + newZ * SinCosTabel.cos[xAngel]);
	  convertedVertexes[nr][2] += scale*dist;
	}
	public void update(Graphics g)
	{
	  Graphics2D g2D = (Graphics2D)i.getGraphics();

	  if(clear)
	  {
	    g2D.setColor(backgroundColor);
	    g2D.clearRect(0, 0, width, height);
	  }
	  if(scroll)
	  {
	    g2D.copyArea(0, 1, width, height-1, 0,-1);
	  }

	  if(yAngel>=360) yAngel=0;
	  if(zAngel>=360) zAngel=0;
	  if(xAngel>=360) xAngel=0;
	  if(yAngel< 0) yAngel = 359;
	  if(zAngel < 0) zAngel = 359;
	  if(xAngel <0) xAngel = 359;

	  for(int i = 0; i < vertexes.length; i++)
	  {
	    rotate(i);
	    //Next convert the 3D point to a 2D point.
	    if(convertedVertexes[i][2] != 0)
	    {
	      convertedVertexes[i][0] = FACTOR * convertedVertexes[i][0]/ convertedVertexes[i][2];
	      convertedVertexes[i][1] = FACTOR * convertedVertexes[i][1]/ convertedVertexes[i][2];
	    }
	    else
	    {
	      convertedVertexes[i][0] =FACTOR * convertedVertexes[i][0];
	      convertedVertexes[i][1] = FACTOR * convertedVertexes[i][1];
	    }

	    //Next translate the coord so 0.0 is at the right corner.
	    convertedVertexes[i][0] += halfWidth;
	    convertedVertexes[i][1] += halfHeight;
	  }

	  //Draw the shapes on the image.
	  for(int i = 0; i < shapes.length; i++)
	  {
	    GeneralPath gp = new GeneralPath();
	    int nr = shapes[i][0];
	    gp.moveTo(convertedVertexes[nr][0], convertedVertexes[nr][1]);
	    for(int j = 1; j < shapes[i].length; j++)
	    {
	      nr = shapes[i][j];
	      gp.lineTo(convertedVertexes[nr][0], convertedVertexes[nr][1]);
	    }
	    gp.closePath();
	    g2D.setColor(shapesColors[i]);

	    g2D.fill(gp);
	  }
	  ((Graphics2D)g).drawImage(i,0,0,this);

	}
}
class SinCosTabel
{
	public static double sin[] = new double[361];
	public static double cos[] = new double[361];
    SinCosTabel()
    {
      for(int i = 0; i < 361; i++)
      {
        sin[i] = Math.sin(Math.toRadians(i));
        cos[i] = Math.cos(Math.toRadians(i));
      }
    }
}