
/**
 * HeartImpl.java
 *
 *
 * Created: Thu Jul 15 15:57:47 1999
 *
 * @author Jan Newmarch
 * @version 1.0
 */

package heart;

import java.io.*;
import java.net.*;
import java.awt.*;

public class HeartImpl implements Heart {
    
    protected String url;

    /*
     * If we want to run it standalone we can use this
     */
    public static void main(String argv[]) {

	HeartImpl impl =
	    new HeartImpl("file:/home/jan/projects/jini/doc/heart/TECG3.ecg");
	impl.show();
    }

    public HeartImpl(String u) {
	url = u;
    }

    double[] points = null;
    Painter painter = null;

    String heartRate = "--";


    public void setHeartRate(int rate) {
	if (rate > 20 && rate <= 250) {
	    heartRate = "Heart Rate: " + rate;
	} else {
	    heartRate = "Heart Rate: --";
	}
	// ? ask for repaint? 
    }

    public void quit(Exception e, String s) {
	System.err.println(s);
	e.printStackTrace();
	System.exit(1);
    }

    public void show() {
	int SAMPLE_SIZE = 300 / Toolkit.getDefaultToolkit().
	                                            getScreenResolution();
	Dimension size = Toolkit.getDefaultToolkit().
	                         getScreenSize();
	int width = (int) size.getWidth();
	// capture points in an array, for redrawing in app if needed
	points = new double[width * SAMPLE_SIZE];
	for (int n = 0; n < width; n++) {
	    points[n] = -1;
	}


	URL dataUrl = null;
	InputStream in = null;

	try {
	    dataUrl = new URL(url);
	    in = dataUrl.openStream();
	} catch (Exception ex) {
	    quit(ex, "connecting to ECG server");
	    return;
	}

	Frame frame = new Frame("Heart monitor");
	frame.setSize((int) size.getWidth()/2, (int) size.getHeight()/2);
	try {
	    painter = new Painter(this, frame, in);
	    painter.start();
	} catch (Exception ex) {
	    quit(ex, "fetching data from ECG server");
	    return;
	}
	frame.setVisible(true);
    }
} // HeartImpl

class Painter extends Thread {

    static final int DEFAULT_SLEEP_TIME = 25; // milliseconds
    static final int CLEAR_AHEAD = 15;
    static final int MAX = 255;
    static final int MIN = 0;
    final int READ_SIZE = 10;

    protected HeartImpl app;
    protected Frame frame;

    protected InputStream in;
    protected final int RESOLUTION = Toolkit.getDefaultToolkit().
	                                            getScreenResolution();
    protected final int UNITS_PER_INCH = 125;
    protected final int SAMPLE_SIZE = 300 / RESOLUTION;
    protected int sleepTime = DEFAULT_SLEEP_TIME;
    
    public Painter(HeartImpl app, Frame frame, InputStream in) throws Exception {
	this.app = app;
	this.frame = frame;
	this.in = in;
    }

    public void run() {

	while (!frame.isVisible()) {
	   try {
	       Thread.sleep(1000);
	   } catch(Exception e) {
	       // ignore
	   }
	}

	int height = frame.getSize().height;
	int width = frame.getSize().width;
	int x = 1; // start at 1 rather than 0 to avoid drawing initial line
	           // from -128 .. 127
	int magnitude;
	int nread;
	int max = MIN; // top bound of magnitude
	int min = MAX;  // bottom bound of magnitude
	int oldMax = MAX + 1;
	byte[] data = new byte[READ_SIZE];
	Graphics g = frame.getGraphics();
	g.setColor(Color.red);
	try {
	    Font f = new Font("Serif", Font.BOLD, 20);
	    g.setFont(f);
	} catch (Exception ex) {
	    // ....
	}



	try {
            boolean expectHR = false;   // true ==> next byte is heartrate

	    while ((nread = in.read(data)) != -1) {
		for (int n = 0; n < nread; n++) {
		    int thisByte = data[n] & 0xFF;
		    if (expectHR) {
			expectHR = false;
			app.setHeartRate(thisByte);
			continue;
		    } else if (thisByte == 255) {
			expectHR = true;
			continue;
		    }
		    
		    // we are reading bytes, from -127..128
		    // conver to unsigned
		    magnitude = thisByte;

		    // then convert to correct scale
		    magnitude -= 128;
		    // scale and convert to window coord from the top downwards
		    int y = ((128 - magnitude) * RESOLUTION) / UNITS_PER_INCH;
		    app.points[x] = y;

		    // draw only on multiples of sample size
		    if (x % SAMPLE_SIZE == 0) {
			// delay to draw at a reasonable rate
			Thread.sleep(sleepTime);

			int x0 = x / SAMPLE_SIZE;
			g.clearRect(x0, 0, CLEAR_AHEAD, height);
			if (oldMax != MAX + 1) {
			    g.drawLine(x0-1, oldMax, x0, min); 
			}
			g.drawLine(x0, min, x0, max);
			oldMax = max;
			min = 1000;
			max = -1000;
			if (app.heartRate != null) {
			    g.setColor(Color.black);
			    g.clearRect(0, 180, 200, 100);
			    g.drawString(app.heartRate, 0, 220);
			    g.setColor(Color.red);
			}
		    } else {
			if (y > max) max = y;
			if (y < min) min = y;
		    }
		    if (++x >= width * SAMPLE_SIZE) {
			x = 0;
		    }
		}
	    }
	} catch(Exception ex) {
	    if (! (ex instanceof SocketException)) {
		System.out.println("Applet quit -- got " + ex);
	    }
	} finally {
	    try {
		if (in != null) {
		    in.close();
		    in = null;
		}
	    } catch (Exception ex) {
		// hide it
	    }
	}
    }
}
