/*
 * Author: Paul Koshevoy
 * TV.java  Sat 17 Jan 1998, 13:15:00 1998
 */

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


public class TVtest extends Canvas implements Runnable
{
private int delay = 2;// delay between the redraws of the canvas in milliseconds
private int width;     // width of the canvas
private int height;    // height of the canvas
    
private Image offImage;       // buffer for double buffering
private Graphics offGraphics; // buffer for double buffering
private Thread animatorThread;// thread for double buffering and rotaion
private MediaTracker tracker; // this tracker will look out for the progress on the image load

private Plane mirror[];
private Ray   gun;
private Plane screen;
private int    picture[];
    
private int x;
private int y;
private int sx;
private int sy;
    
private Parameters params;
private Screen     screenCanvas;
private v3x1       C1; // position of first mirror in world coord system

public double alpha1;
public double alpha2;
public double da1;
public double da2;
public Plane m[];
private double TwoPI;
    
public TVtest(int W, int H, Image img, Screen SC, Parameters prms) // constructor
    {
	tracker = new MediaTracker(this);
	tracker.addImage(img, 0);
	try
	    { tracker.waitForID(0); }
	catch(InterruptedException e)
	    { System.out.println("exception happend during an attempt to load an image"); }
	x = 0;
	y = 0;
	sx = img.getWidth(this);
	sy = img.getHeight(this);

	System.out.println(sx);
	System.out.println(sy);
	
	int size = (int)(sx*sy);
	picture = new int[size];
	PixelGrabber PG = new PixelGrabber(img, 0, 0, (int)sx, (int)sy, picture, 0, (int)sx);
	try
	    { PG.grabPixels(); }
	catch(InterruptedException e)
	    { System.out.println("pixel grab failed"); }

	screenCanvas = SC;
	params = prms;
	mirror = new Plane[2];
	m      = new Plane[2];

	width  = W;
	height = H;
	setSize(width, height);

	// transformation from mirror[0] coord system is a simple translation by S2 along y-axis
	mirror[0] = new Plane(-params.R1,  params.R1,  params.R1,
                              -params.R1, -params.R1, -params.R1,
			       params.R1, -params.R1, -params.R1,
			       params.R1,  params.R1,  params.R1);
	mirror[0] = mirror[0].rot(m3x3.rotz(-params.PI/2));
	
	// mirror[1] is located at the origin and does not require transformation
	mirror[1] = new Plane(-params.H2/2, -params.R2, 0,
			       params.H2/2, -params.R2, 0,
			       params.H2/2,  params.R2, 0,
                              -params.H2/2,  params.R2, 0);
	
	gun      = new Ray(new v3x1(0, params.S2, params.D1),
			   new v3x1(0, 0, -1));
	
	screen   = new Plane(-params.WD/2, -params.J, params.I,
			      params.WD/2, -params.J, params.I,
			      params.WD/2,  params.L, params.K,
                             -params.WD/2,  params.L, params.K);
	da1 = params.dP/sx;
	da2 = params.dG/sy;
	alpha1=0;
	alpha2=params.G1;
	C1 = new v3x1(0, params.S2, 0);
	m[0] = (mirror[0].rot(m3x3.rotz(-alpha1))).tran(C1);
	m[1] = mirror[1].rot(m3x3.rotx(-alpha2));
	TwoPI = 2*params.PI;
	start();
    }
    
public void start()
    {
	if(animatorThread == null)
	    animatorThread = new Thread(this); // create a new thread for double buffering
	animatorThread.start();                // start the new thread
    }
    
public void stop()
    {
	if(animatorThread != null)
	    animatorThread.stop(); // stop the double buffer thread
	animatorThread = null;     // destroy the double buffer thread
//	offGraphics = null;        // destroy offscreen buffer
//	offImage = null;           // destroy offscreen buffer
    }
    
    
public void run()
    {
	Thread.currentThread().setPriority(Thread.MAX_PRIORITY); // maximize the priority of the thread
	long startTime = System.currentTimeMillis();             // start time

	while(Thread.currentThread() == animatorThread)          // double buffer thread
	    {
		try
		    {
			startTime += delay;
			Thread.sleep(Math.max(0, startTime - System.currentTimeMillis()));
		    }
		catch(InterruptedException e)
		    {
			break;
		    }
		repaint(); // redraw
	    }
    }
    
    
public void update(Graphics g)
    {
	paint(g);
    }

    
public void paint(Graphics g)
    {
	int cx_1 = (int)(3*width/4);
	int cx_2 = (int)(width/4);
	int cy = (int)(height/2);
	
	if((offGraphics == null))
	    {
		offImage = createImage(width, height);
		offGraphics = offImage.getGraphics();
	    }


	if(alpha1 >= TwoPI)
	    {
		System.out.println("next line");
		alpha1 = 0;
		alpha2 += da2;
		if(alpha2 >= TwoPI)
		    {
			alpha2 = 0;
			System.out.println("done with a frame");
		    }
		m[1] = mirror[1].rot(m3x3.rotx(-alpha2));
	    }


	m[0] = (mirror[0].rot(m3x3.rotz(alpha1))).tran(C1);
	alpha1 += da1;

//	if((alpha2 >= params.G1) && (alpha2 <= params.G2))
//	    if((alpha1 >= params.P2) && (alpha1 <= params.P1))
//		{
		    offGraphics.setColor(Color.black);
		    offGraphics.fillRect(0, 0, width, height);
// draw laser	
		    offGraphics.setColor(Color.green);
		    offGraphics.drawLine(cx_1 + (int)(gun.o.x), cy + (int)(gun.o.y),
					 cx_1 + (int)(gun.o.x + gun.d.x*10), cy + (int)(gun.o.y + gun.d.y*10));
		    offGraphics.setColor(Color.red);
		    offGraphics.drawLine(cx_1 + (int)(gun.o.x), cy + (int)(gun.o.y),
					 cx_1 + (int)(gun.o.x + gun.d.x*2), cy + (int)(gun.o.y + gun.d.y*2));
		    
		    offGraphics.setColor(Color.green);
		    offGraphics.drawLine(cx_2 + (int)(gun.o.z), cy + (int)(gun.o.y),
					 cx_2 + (int)(gun.o.z + gun.d.z*10), cy + (int)(gun.o.y + gun.d.y*10));
		    offGraphics.setColor(Color.red);
		    offGraphics.drawLine(cx_2 + (int)(gun.o.z), cy + (int)(gun.o.y),
					 cx_2 + (int)(gun.o.z + gun.d.z*2), cy + (int)(gun.o.y + gun.d.y*2));
		    
	
		    Triangle t1 = screen.T1;
		    Triangle t2 = screen.T2;
		    
// draw screen
		    offGraphics.setColor(Color.lightGray);
		    offGraphics.drawLine(cx_1 + (int)(t1.v0.x), cy + (int)(t1.v0.y),
					 cx_1 + (int)(t1.v1.x), cy + (int)(t1.v1.y));
		    offGraphics.drawLine(cx_1 + (int)(t1.v0.x), cy + (int)(t1.v0.y),
					 cx_1 + (int)(t1.v2.x), cy + (int)(t1.v2.y));
		    offGraphics.drawLine(cx_1 + (int)(t1.v1.x), cy + (int)(t1.v1.y),
					 cx_1 + (int)(t1.v2.x), cy + (int)(t1.v2.y));
		    
		    offGraphics.drawLine(cx_1 + (int)(t2.v0.x), cy + (int)(t2.v0.y),
					 cx_1 + (int)(t2.v1.x), cy + (int)(t2.v1.y));
		    offGraphics.drawLine(cx_1 + (int)(t2.v0.x), cy + (int)(t2.v0.y),
					 cx_1 + (int)(t2.v2.x), cy + (int)(t2.v2.y));
		    offGraphics.drawLine(cx_1 + (int)(t2.v1.x), cy + (int)(t2.v1.y),
					 cx_1 + (int)(t2.v2.x), cy + (int)(t2.v2.y));
		    
		    offGraphics.drawLine(cx_2 + (int)(t1.v0.z), cy + (int)(t1.v0.y),
					 cx_2 + (int)(t1.v1.z), cy + (int)(t1.v1.y));
		    offGraphics.drawLine(cx_2 + (int)(t1.v0.z), cy + (int)(t1.v0.y),
					 cx_2 + (int)(t1.v2.z), cy + (int)(t1.v2.y));
		    offGraphics.drawLine(cx_2 + (int)(t1.v1.z), cy + (int)(t1.v1.y),
					 cx_2 + (int)(t1.v2.z), cy + (int)(t1.v2.y));
		    
		    offGraphics.drawLine(cx_2 + (int)(t2.v0.z), cy + (int)(t2.v0.y),
					 cx_2 + (int)(t2.v1.z), cy + (int)(t2.v1.y));
		    offGraphics.drawLine(cx_2 + (int)(t2.v0.z), cy + (int)(t2.v0.y),
					 cx_2 + (int)(t2.v2.z), cy + (int)(t2.v2.y));
		    offGraphics.drawLine(cx_2 + (int)(t2.v1.z), cy + (int)(t2.v1.y),
					 cx_2 + (int)(t2.v2.z), cy + (int)(t2.v2.y));
		    
		    
		    Ray ray = gun;
		    for(int j=0; j<2; j++)
			{
//		if(j==1)
//		    {
//			v3x1 ref = ray.d;
//			System.out.println("  reflection 1: [" + ref.x + " " + ref.y + " " + ref.z + "]T");
//		    }
			    ray = CDetector.collision(m[j], ray);
			    if(ray != null)
				{
				    v3x1 hit = ray.o;
				    v3x1 ref = ray.d;
				    offGraphics.setColor(Color.cyan);
				    offGraphics.drawLine(cx_1 + (int)(hit.x), cy + (int)(hit.y),
							 cx_1 + (int)(hit.x + ref.x*100), cy + (int)(hit.y + ref.y*100));
				    
				    offGraphics.drawLine(cx_2 + (int)(hit.z), cy + (int)(hit.y),
							 cx_2 + (int)(hit.z + ref.z*100), cy + (int)(hit.y + ref.y*100));
				    offGraphics.setColor(Color.orange);
				    offGraphics.fillOval(cx_1 + (int)(hit.x), cy + (int)(hit.y), 5, 5);
				    offGraphics.fillOval(cx_2 + (int)(hit.z), cy + (int)(hit.y), 5, 5);
//			if(j==1)
//			    System.out.println("  reflection 2: [" + ref.x + " " + ref.y + " " + ref.z + "]T");
				    
				}
//		else
//		    if(j==1)
//			System.out.println("missed "+(1+j)+" mirror");
			    
			    t1 = m[j].T1;
			    t2 = m[j].T2;
			    if(t1.equal(CDetector.the_hit) || t2.equal(CDetector.the_hit))
				offGraphics.setColor(Color.red);
			    else
				offGraphics.setColor(Color.white);
			    
			    offGraphics.drawLine(cx_1 + (int)(t1.v0.x), cy + (int)(t1.v0.y),
						 cx_1 + (int)(t1.v1.x), cy + (int)(t1.v1.y));
			    offGraphics.drawLine(cx_1 + (int)(t1.v0.x), cy + (int)(t1.v0.y),
						 cx_1 + (int)(t1.v2.x), cy + (int)(t1.v2.y));
			    offGraphics.drawLine(cx_1 + (int)(t1.v1.x), cy + (int)(t1.v1.y),
						 cx_1 + (int)(t1.v2.x), cy + (int)(t1.v2.y));
			    
			    offGraphics.drawLine(cx_1 + (int)(t2.v0.x), cy + (int)(t2.v0.y),
						 cx_1 + (int)(t2.v1.x), cy + (int)(t2.v1.y));
			    offGraphics.drawLine(cx_1 + (int)(t2.v0.x), cy + (int)(t2.v0.y),
						 cx_1 + (int)(t2.v2.x), cy + (int)(t2.v2.y));
			    offGraphics.drawLine(cx_1 + (int)(t2.v1.x), cy + (int)(t2.v1.y),
						 cx_1 + (int)(t2.v2.x), cy + (int)(t2.v2.y));
			    
			    offGraphics.drawLine(cx_2 + (int)(t1.v0.z), cy + (int)(t1.v0.y),
						 cx_2 + (int)(t1.v1.z), cy + (int)(t1.v1.y));
			    offGraphics.drawLine(cx_2 + (int)(t1.v0.z), cy + (int)(t1.v0.y),
						 cx_2 + (int)(t1.v2.z), cy + (int)(t1.v2.y));
			    offGraphics.drawLine(cx_2 + (int)(t1.v1.z), cy + (int)(t1.v1.y),
						 cx_2 + (int)(t1.v2.z), cy + (int)(t1.v2.y));
			    
			    offGraphics.drawLine(cx_2 + (int)(t2.v0.z), cy + (int)(t2.v0.y),
						 cx_2 + (int)(t2.v1.z), cy + (int)(t2.v1.y));
			    offGraphics.drawLine(cx_2 + (int)(t2.v0.z), cy + (int)(t2.v0.y),
						 cx_2 + (int)(t2.v2.z), cy + (int)(t2.v2.y));
			    offGraphics.drawLine(cx_2 + (int)(t2.v1.z), cy + (int)(t2.v1.y),
						 cx_2 + (int)(t2.v2.z), cy + (int)(t2.v2.y));
			}
		    Ray scrn = CDetector.test_hit(screen, ray);
		    if(scrn != null)
			{
			    v3x1 hit = scrn.o;
			    v3x1 ref = scrn.d;
			    offGraphics.setColor(Color.yellow);
			    
			    offGraphics.fillOval(cx_1 + (int)(hit.x), cy + (int)(hit.y), 3, 3);
			    offGraphics.fillOval(cx_2 + (int)(hit.z), cy + (int)(hit.y), 3, 3);
			    
//		offGraphics.drawLine(cx_1 + (int)(hit.x), cy + (int)(hit.y),
//				     cx_1 + (int)(hit.x), cy + (int)(hit.y));
//		offGraphics.drawLine(cx_2 + (int)(hit.z), cy + (int)(hit.y),
//				     cx_2 + (int)(hit.z), cy + (int)(hit.y));
			    x = params.get_x(alpha1, sx);
			    y = params.get_y(alpha2, sy);
		
			    int index = (int)(x+y*sx);
			    screenCanvas.point = hit;
//		screenCanvas.COLOR = new Color(RED(picture[index]), GREEN(picture[index]), BLUE(picture[index]));
			    screenCanvas.COLOR = new Color((int)(picture[index]));
			    screenCanvas.repaint();
			}
//		}
	
	g.drawImage(offImage, 0, 0, this);
    }
}

