import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;




public class Sock extends Applet implements Runnable{
    static int portnum = 7654;
	static String hostname = "pcbunn.cithep.caltech.edu";
	static InetAddress host;
    byte imagebytes[];
    int numbytes;
	int width = 160;
	int height = 120;
	Image image=null;
	DatagramSocket DataSocket;
	DatagramPacket Dout;
	DatagramPacket Din;
	ImageCanvas cImage;
	Label lMessage;	
	Thread T;
	java.awt.image.IndexColorModel model;



	public void init() {

		setLayout(new GridLayout(2,1));
		cImage = new ImageCanvas(null);
		cImage.resize(width,height);
		add(cImage);
		lMessage = new Label("Initialising...");
		lMessage.resize(width,20);
		add(lMessage);
        this.resize(width,height+20);
		show();


		try {		
            host = InetAddress.getByName(hostname);
			DataSocket = new DatagramSocket();
            lMessage.setText("Socket connected on "+DataSocket.getLocalPort());
		} catch (IOException e) {
            lMessage.setText("IO Error: "+e.getClass()+e.getMessage());
            return;
		}

        numbytes = width*height;
        imagebytes = new byte[numbytes];
// define a colour map with 256 greys
		byte rmap[] = new byte[256];
		byte gmap[] = new byte[256];
		byte bmap[] = new byte[256];
		for(int i=0;i<256;i++) {
			rmap[i] = bmap[i] = gmap[i] = (byte) (255-i);
		}
		int bits = (int)Math.ceil(Math.log(256)/Math.log(2));
        model = new java.awt.image.IndexColorModel(bits,256,rmap,gmap,bmap);
		lMessage.setText("Initialised");
	}

	public void start() {
		if(T==null) {
			T = new Thread(this);
			T.start();
		}
	}
	public void stop() {
		if(T!=null) {
			T.stop();
			T = null;
		}
	}
	public void run() {
		int lenbuffer=65000;
		String HostMessage;
		byte HostBytes[];
		int fps=0;
		long time,timestart;
		int maxframes=20;
		int nframes=0;
		lMessage.setText("Running");
	    timestart = System.currentTimeMillis();

        try {
   			 HostMessage = new String("start\0");
             HostBytes = new byte[HostMessage.length()];
			 HostMessage.getBytes(0,HostMessage.length(),HostBytes,0);
       		 Dout = new DatagramPacket(HostBytes,HostBytes.length,host,portnum);
	         DataSocket.send(Dout);
             while(true) {
				if(nframes>=maxframes) {
                    time =System.currentTimeMillis();
					if(time!=timestart) fps = (1000*nframes)/(int)(time-timestart);
					HostMessage = new String(fps+"fps\0");
					lMessage.setText(HostMessage);
                    HostBytes = new byte[HostMessage.length()];
			        HostMessage.getBytes(0,HostMessage.length(),HostBytes,0);
       		        Dout = new DatagramPacket(HostBytes,HostBytes.length,host,portnum);
			        DataSocket.send(Dout);
                    timestart = time;
                    nframes=0;
				} else {
					nframes++;
				}
				Din = new DatagramPacket(new byte[lenbuffer],lenbuffer);

				DataSocket.receive(Din);
				imagebytes = Din.getData();
				// First 8 bytes of message are offsets and size of this image segment
				int xoffset = (0xFF & imagebytes[0]) + (0xFF & imagebytes[1])*256;
				int xwidth = (0xFF & imagebytes[2]) + (0xFF & imagebytes[3])*256;
				int yoffset = (0xFF & imagebytes[4]) + (0xFF & imagebytes[5])*256;
				int ywidth = (0xFF & imagebytes[6]) + (0xFF & imagebytes[7])*256;
                int imagesize = xwidth*ywidth;
//				lMessage.setText("xoff "+xoffset+
//				" yoff "+yoffset+" xwid "+xwidth+" ywid "+ywidth+" size "+imagesize+" data "+imagebytes[2000]);
//				imagesize = numbytes;
				System.arraycopy(imagebytes,8,imagebytes,0,imagesize);
                cImage.setImage(createImage(new java.awt.image.MemoryImageSource(xwidth, 
	                    ywidth, model, imagebytes, 0, xwidth)),xoffset,yoffset,xwidth,ywidth);
				cImage.repaint();
            }
		 }
         catch (IOException e) {
            lMessage.setText("IO Error:"+e.getClass()+e.getMessage());
            return;
		 }
	}
}

class ImageCanvas extends Canvas {
	Image thisImage;
	Image subImage;
	int xposition=0;
	int yposition=0;
	int width = 0;
	int height = 0;
	public ImageCanvas(Image img) {
        thisImage = img;
		subImage = img;
	}
	public void setImage(Image img, int xpos, int ypos, int xwid, int ywid) {
        subImage = img;
        xposition = xpos;
		yposition = ypos;
		width = xwid;
		height = ywid;
//		getGraphics().clipRect(xpos,ypos,xwid,ywid);
//		getGraphics().drawImage(img,xpos,ypos,this);
	}
	public void update(Graphics g) {
//		g.clipRect(xposition,yposition,width,height);
		g.drawImage(subImage,xposition,yposition,this);
	}
}
