/* Applet to put a rotatable image cube into a page
   The rotatable image class is RotateImage.java
   */
   
import java.applet.Applet;
import java.awt.Image;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Color;
import java.util.Vector;
import java.io.StreamTokenizer;
import java.awt.MediaTracker;
import java.awt.image.IndexColorModel;
import java.awt.image.ColorModel;
import java.awt.image.MemoryImageSource;

public class RotateImageCube extends Applet implements Runnable {
   RotateImage rotImage[];
   Image gotimg;
   boolean painted = true;
   int prevx, prevy;
   float xtheta, ytheta;
   Matrix3D pixmat[];
   Image backBuffer;
   Graphics backGC;
   MediaTracker tracker;
   Thread rotCubeThread;
   int W, H, D;
   int width[], height[], depth[];
   int imgnum = 0;
   boolean dragged = false;
   
   public void init() {
      resize( size().width, size().height );
      tracker = new MediaTracker(this);
      W = Integer.parseInt(getParameter("width"));
      H = Integer.parseInt(getParameter("height"));
      D = Integer.parseInt(getParameter("depth"));
      
      Vector rotImgVec = new Vector();
      Vector WHDVec = new Vector();
      Vector MatrixVec = new Vector();
      RotateImage trot;
      WHD twhd;
      Matrix3D tmat;

      try {
         String imgname = getParameter("frontimg");
         gotimg = getImage(getDocumentBase(), imgname);
         tracker.addImage(gotimg, 0);
         tracker.waitForID(0);
         }
      catch (Exception e) {
         System.out.println("error :" + e);
         e.printStackTrace();
         }
      trot = new RotateImage(gotimg, this, W, H);
      rotImgVec.addElement(trot);
      twhd = new WHD(W, H, D/2);
      WHDVec.addElement(twhd);
      tmat = new Matrix3D();
      tmat.unit();
      MatrixVec.addElement(tmat);
      ++imgnum;

      try {
         String imgname = getParameter("topimg");
         gotimg = getImage(getDocumentBase(), imgname);
         tracker.addImage(gotimg, 0);
         tracker.waitForID(0);
         }
      catch (Exception e) {
         System.out.println("error :" + e);
         e.printStackTrace();
         }
      trot = new RotateImage(gotimg, this, W, D);
      rotImgVec.addElement(trot);
      twhd = new WHD(W, D, H/2);
      WHDVec.addElement(twhd);
      tmat = new Matrix3D();
      tmat.xrot(90);
      MatrixVec.addElement(tmat);
      ++imgnum;

      try {
         String imgname = getParameter("bottomimg");
         gotimg = getImage(getDocumentBase(), imgname);
         tracker.addImage(gotimg, 0);
         tracker.waitForID(0);
         }
      catch (Exception e) {
         System.out.println("error :" + e);
         e.printStackTrace();
         }
      trot = new RotateImage(gotimg, this, W, D);
      rotImgVec.addElement(trot);
      twhd = new WHD(W, D, H/2);
      WHDVec.addElement(twhd);
      tmat = new Matrix3D();
      tmat.xrot(-90);
      MatrixVec.addElement(tmat);
      ++imgnum;

      try {
         String imgname = getParameter("leftimg");
         gotimg = getImage(getDocumentBase(), imgname);
         tracker.addImage(gotimg, 0);
         tracker.waitForID(0);
         }
      catch (Exception e) {
         System.out.println("error :" + e);
         e.printStackTrace();
         }
      trot = new RotateImage(gotimg, this, D, H);
      rotImgVec.addElement(trot);
      twhd = new WHD(D, H, W/2);
      WHDVec.addElement(twhd);
      tmat = new Matrix3D();
      tmat.yrot(-90);
      MatrixVec.addElement(tmat);
      ++imgnum;

      try {
         String imgname = getParameter("rightimg");
         gotimg = getImage(getDocumentBase(), imgname);
         tracker.addImage(gotimg, 0);
         tracker.waitForID(0);
         }
      catch (Exception e) {
         System.out.println("error :" + e);
         e.printStackTrace();
         }
      trot = new RotateImage(gotimg, this, D, H);
      rotImgVec.addElement(trot);
      twhd = new WHD(D, H, W/2);
      WHDVec.addElement(twhd);
      tmat = new Matrix3D();
      tmat.yrot(90);
      MatrixVec.addElement(tmat);
      ++imgnum;

      try {
         String imgname = getParameter("backimg");
         gotimg = getImage(getDocumentBase(), imgname);
         tracker.addImage(gotimg, 0);
         tracker.waitForID(0);
         }
      catch (Exception e) {
         System.out.println("error :" + e);
         e.printStackTrace();
         }
      trot = new RotateImage(gotimg, this, W, H);
      rotImgVec.addElement(trot);
      twhd = new WHD(W, H, D/2);
      WHDVec.addElement(twhd);
      tmat = new Matrix3D();
      tmat.yrot(180);
      MatrixVec.addElement(tmat);
      ++imgnum;

      rotImage = new RotateImage[rotImgVec.size()];
      rotImgVec.copyInto(rotImage);
      width = new int[WHDVec.size()];
      height = new int[WHDVec.size()];
      depth = new int[WHDVec.size()];
      for (int i = 0; i<WHDVec.size(); ++i) {
         twhd = (WHD) WHDVec.elementAt(i);
         width[i] = twhd.width;
         height[i] = twhd.height;
         depth[i] = twhd.depth;
         }
      pixmat = new Matrix3D[MatrixVec.size()];
      MatrixVec.copyInto(pixmat);      
      backBuffer = createImage(size().width, size().height);
      backGC = backBuffer.getGraphics();
      setBackground(Color.lightGray);
      }
      
   public void run() {
      repaint();
      }
      
   public void start() {
      if (rotCubeThread == null) {
         rotCubeThread = new Thread(this);
         rotCubeThread.start();
         }
      }
      
   public void stop() {
      rotCubeThread = null;
      }
      
   public void update(Graphics g) {
      if (backBuffer == null) {
         g.clearRect(0, 0, size().width, size().height);
         return;
         }
      if (!dragged) {
         for (int i = 0; i<imgnum; ++i) {
            rotImage[i].transformed = true;
            rotImage[i].paint(backGC);
            }
         g.drawImage(backBuffer, 0, 0, this);
         setPainted();
         }
      else {
         paint(g);
         }
      }
      
   public void paint(Graphics g) {
      if (backBuffer != null) {
         backGC.setColor(getBackground());
         backGC.fillRect(0, 0, size().width, size().height);
         }
      else 
         return;
      for (int i = 0; i<imgnum; ++i) {
         rotImage[i].mat.unit();
         rotImage[i].mat.translate( -(width[i]/2), -(height[i]/2), depth[i]);
         rotImage[i].mat.mult(pixmat[i]);
         rotImage[i].mat.translate( size().width/2, size().height/2, 0);
         rotImage[i].transformed = false;
         rotImage[i].paint(backGC);
         }
      g.drawImage(backBuffer, 0, 0, this);
      setPainted();
      dragged = false;
      }   

   public boolean mouseDown(Event e, int x, int y) {
      prevx = x;
      prevy = y;
      return true;
      }
   
   public boolean mouseDrag(Event e, int x, int y) {
      Matrix3D tmat = new Matrix3D();
      tmat.unit();
      float xtheta = (y - prevy) * (360.0f / size().width);
      float ytheta = (x - prevx) * (360.0f / size().height);
      tmat.xrot(xtheta);
      tmat.yrot(ytheta);
      for (int i = 0; i<imgnum; ++i)
         pixmat[i].mult(tmat);
      if (painted) {
         painted = false;
         dragged = true;
         repaint();
      }
      prevx = x;
      prevy = y;
      return true;
      }
 
   private synchronized void setPainted() {
      painted = true;
      notifyAll();
      }

   private synchronized void waitPainted() {
      //System.out.println("inside");
      while (!painted) {
         try {
            wait();
            }
         catch (InterruptedException e) {}
         }
      painted = false;
      }
   }
   
class WHD {
   int width;
   int height;
   int depth;
   
   WHD(int w, int h, int d) {
      width = w;
      height = h;
      depth = d;
      }
   }
