import java.util.*;
import java.awt.*;
import java.applet.*;
import java.net.*;


public 
class GraphPanel extends ScrollingPanel implements Runnable {

protected Blackboard _bb;
public GraphPanel(Blackboard black) {
	super( (int)black.lx(), (int)black.ly(), (int)black.ux(), (int)black.uy() );
	_bb = black;
}

	// Thread stuff
	//
protected boolean _painted = true;    
protected synchronized void setPainted() {
	_painted = true;
	notifyAll();
}
protected synchronized void waitPainted() {
	while (!_painted)
		try {
			wait();
		} catch (InterruptedException e) {
		}
	_painted = false;
}

protected Thread _updater;
public void run() {
	Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
	while (true) {
		waitPainted();
		if( _bb.catchEmbedderChange() )
			super.focusScrollbars();
		_bb.embedder().Embed();
		repaint();
		setPainted();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			break;
		}
	}
}
public void start() {
	_updater = new Thread(this);
	_updater.start();
}
public void stop() {
	if( _updater != null )
		_updater.stop();
	_updater = null;
}
	

	// the drawing routine
	//
protected Node _pick;                  // Picked node
protected boolean _pickfixed = false;  // Is the picked node fixed?
protected boolean _extending = false;  // Extending edges of the picked node?
protected Image _offscreen;
protected Dimension _offscreensize;
protected Graphics _offgraphics;

protected final void sortByZ( Vector nodes ) {
  // Do insertionsort on the Z depth of the nodes
	int len = nodes.size();
  for( int P = 1; P < len; ++P ) {
    Node tmp = (Node) nodes.elementAt( P );
		double Z = tmp.Z();
    int j;
    for( j = P; j > 0; --j ) {
      Node tmp2 = (Node) nodes.elementAt( j-1 );
      if( Z >= tmp2.Z() ) break;
      nodes.setElementAt( tmp2, j );
		}
		nodes.setElementAt( tmp, j );
	}
}

public void update(Graphics g) {
	Dimension d = size();
	adjustScrollbars( d.width, d.height );
	if ((_offscreen == null) || (d.width != _offscreensize.width) 
			|| (d.height != _offscreensize.height)) {
		_offscreen = createImage(d.width, d.height);
		_offscreensize = d;
		_offgraphics = _offscreen.getGraphics();
		_offgraphics.setFont(getFont());
	}

	_offgraphics.setColor(getBackground());
  _offgraphics.clipRect(0, 0, d.width, d.height);
	_offgraphics.fillRect(0, 0, d.width, d.height);
	_offgraphics.translate( -_sbh.getValue(), -_sbv.getValue() );
	Vector edges = _bb.edges();
	int edgecnt = edges.size();
	for( int i = 0; i < edgecnt; ++i ) {
		Edge e = (Edge) edges.elementAt(i);
    Node to = e.to();
    Node from = e.from();
		if( to != from && to.showing() && from.showing() )
      e.paint( _offgraphics );
	}
	Vector nodes = _bb.nodes();
	sortByZ( nodes );
	int nodecnt = nodes.size();
  for( int i = 0; i < nodecnt; ++i ) {
		Node n = (Node) nodes.elementAt(i);
    if( n.showing() )
       n.paint( _offgraphics );
	}
	g.drawImage( _offscreen, 0, 0, null );
	_offgraphics.translate( _sbh.getValue(), _sbv.getValue() );
}

	// The event handler
	//
public synchronized boolean mouseDown(Event evt, int x, int y) {
	x = x + _sbh.getValue();
	y = y + _sbv.getValue();

	int bestdist = Integer.MAX_VALUE;
	Vector nodes = _bb.nodes();
	int nodecnt = nodes.size();
  for( int i = 0; i < nodecnt; ++i ) {
		Node n = (Node) nodes.elementAt(i);
		int dx = n.X()-x;
		int dy = n.Y()-y;
		int dist = dx*dx + dy*dy;
		if (dist < bestdist) {
			_pick = n;
			bestdist = dist;
		}
	}
	_pick.pick( true );
	_pickfixed = _pick.fixed();
	_pick.XY( x, y );

	if( evt.clickCount > 1 ) {
    boolean shift_on = ((evt.modifiers & Event.SHIFT_MASK) != 0);
    if (shift_on) {
      _pickfixed = !_pickfixed;
      _bb.group( _pick );
    }
    else
      _bb.localize( _pick );
	}
  else {
    boolean shift_on = ((evt.modifiers & Event.SHIFT_MASK) != 0);
    if (shift_on) { // Toggle the fix of the picked node
      _pickfixed = !_pickfixed;
    }
    boolean ctrl_on = ((evt.modifiers & Event.CTRL_MASK) != 0); 
    if (ctrl_on) { // Now extending the picked node
      _extending = true;
    }
    boolean alt_on = ((evt.modifiers & Event.ALT_MASK) != 0);
    if( alt_on ) { // Now trying to open the URL of the picked node
      try {
        URL doc = _bb.applet().getDocumentBase();
        AppletContext page = _bb.applet().getAppletContext();
        page.showDocument( new URL(doc, _bb.globals.basedir() + _pick.label()) );
      } catch( MalformedURLException mal ) {
        _bb.applet().showStatus("Couldn't display " + _pick.label());
      }
    }
  }
	waitPainted();
	repaint();
	setPainted();
	return true;
}

public synchronized boolean mouseDrag(Event evt, int x, int y) {
	x = x + _sbh.getValue();
	y = y + _sbv.getValue();
		
	_pick.XY( x, y );
	waitPainted();
	repaint();
	setPainted();
	return true;
}

public synchronized boolean mouseUp(Event evt, int x, int y) {
	x = x + _sbh.getValue();
	y = y + _sbv.getValue();

	_pick.XY( x, y );
	_pick.fix( _pickfixed );
	_pick.pick( false );

	if( _extending ) {
		for (Enumeration i = _pick.edges() ; i.hasMoreElements() ;) {
			Edge e = (Edge) i.nextElement();
			e.updateLength();
		}
	}
	_extending = false;
	_pick = null;

	waitPainted();
	repaint();
	setPainted();
	return true;
}

private final int margin = 200;
protected void addBoundingBoxPoint( int x, int y) {
	if( x > _maxx )      _maxx = x;
	else if( x < _minx ) _minx = x;
	if( y > _maxy )      _maxy = y;
	else if( y < _miny ) _miny = y;
}
protected void adjustScrollbars(int width, int height) {
	int oldminx = _minx;
	int oldmaxx = _maxx;
	int oldminy = _miny;
	int oldmaxy = _maxy;
	_minx = _maxx = _miny = _maxy = 0;
	Vector nodes = _bb.nodes();
	int nodecnt = nodes.size();
  for( int i = 0; i < nodecnt; ++i ) {
		Node n = (Node) nodes.elementAt(i);
		addBoundingBoxPoint( n.X(), n.Y() );
	}
  _iminx = _minx;
  _iminy = _miny;
	int newwidth = _maxx-_minx;
	int newheight = _maxy-_miny;
  _minx -= newwidth;	
  _maxx += newwidth;	
  _miny -= newheight;	
  _maxy += newheight;	
	if( _minx-margin > oldminx )  _minx = oldminx+margin;
	else                          _minx = (_minx<oldminx)?_minx:oldminx; // Min
	if( _maxx+margin < oldmaxx )  _maxx = oldmaxx-margin;
	else                          _maxx = (_maxx>oldmaxx)?_maxx:oldmaxx; // Max
	if( _miny-margin > oldminy )  _miny = oldminy+margin;
	else                          _minx = (_miny<oldminy)?_miny:oldminy; // Min
	if( _maxy+margin < oldmaxy )  _maxy = oldmaxy-margin;
	else                          _maxx = (_maxy>oldmaxy)?_maxy:oldmaxy; // Max
	super.adjustScrollbars( width, height );
}

} // class GraphPanel


