import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Set;
import java.util.HashMap;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Calendar;
import java.util.TimerTask;
import java.util.Timer;
import java.text.SimpleDateFormat;
import java.lang.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import javax.swing.border.*;
import javax.swing.filechooser.*;

// Copyright (C) 2003  Eric McCreath
// A simple logic simulator

//    This program is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.

//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.




class CircuitFileFilter extends FileFilter {

    public CircuitFileFilter () {
    }

    public boolean accept(File f) {
        String n;
        n = f.getName();
        return true; //n.endsWith(".cir");
    }

    public String getDescription () {
        return "*";
    }
}


class Connection implements Serializable {
    char val;
    double xpos, ypos;
    
    public Connection(char v, double x, double y) {
	val = v;
	xpos = x;
	ypos = y;
    }
}

class Connections extends Vector implements Serializable {
}



abstract class LComponent implements Serializable {
    abstract ImageIcon getImage();
    abstract Connections getConnections();
    abstract void evaluate(Connections con);
    abstract String name();
    abstract LComponent copy();
}


class LPart implements Serializable  {
    public LComponent comp;
    public double xpos;
    public double ypos;
    public Connections ports;


    public LPart(LComponent c, double x, double y) {
	comp = c;
	xpos = x;
	ypos = y;
	ports = c.getConnections();
    }

    

}


class LPC implements Serializable {
    public LPart p;
    public int con;
    public LPC(LPart pa, int cone) {
	p = pa;
	con = cone;
    }
}

class LWire implements Serializable {
    public LPart p1,p2;
    public int con1, con2;

    public LWire(LPart pa1, int cone1, LPart pa2, int cone2 ) {
	p1 = pa1;
	p2 = pa2;
	con1 = cone1;
	con2 = cone2;
    }
    public LWire(LPC lpc1, LPC lpc2) {
	p1 = lpc1.p;
	p2 = lpc2.p;
	con1 = lpc1.con;
	con2 = lpc2.con;
    }

}



class Composite extends LComponent implements Serializable {
    Vector parts;
    Vector wires;
    Connections connections;
    String name;
    ImageIcon image;


    public Composite () {
	parts = new Vector();
	wires = new Vector();
	connections = new Connections();
	image = null;
	name = "new";
    }

    ImageIcon getImage() {
	return image;
    }
    
    String name() {
	return name;
    }

    Connections getConnections() {
	return connections;
    }

    void delete(LPart p) {
 	Enumeration e2;
	LPart lp;
	LWire lw;

	e2 = wires.elements();
	wires = new Vector();
	while (e2.hasMoreElements()) {
	    lw = (LWire) e2.nextElement();
	    if (lw == null) { 
		    System.out.println("null wire ??");
	    } else {
		if (lw.p1 == p || lw.p2 == p) {
		    //System.out.println("remove");
		} else {
		    wires.add(lw);
		}
	    } 
	}
	parts.remove(p);
    }

    
    void evaluate(Connections con) {
 	Enumeration e1, e2;
	LPart lp;
	LWire lw;
	Connection c1, c2;
	boolean change;
	int k;


	e1 = parts.elements();
	while (e1.hasMoreElements()) {
	    lp = (LPart) e1.nextElement();
	    lp.comp.evaluate(lp.ports);
	}

	change = true;
	k = 0;
	while (change && k < 1000) {
	    change = false;
	    k++;
	    e2 = wires.elements();
	    while (e2.hasMoreElements()) {
		lw = (LWire) e2.nextElement();
		if (lw == null) System.out.println("null wire ??"); 
		c1 = (Connection) lw.p1.ports.get(lw.con1);
		c2 = (Connection) lw.p2.ports.get(lw.con2);
		if ( c1.val != c2.val && 
		     c1.val == '?') {
		    c1.val = c2.val;
		    change = true;
		} else if (c1.val != c2.val &&
                           c2.val == '?') {
		    c2.val = c1.val;
		    change = true;
		} else if (c1.val != c2.val) {
		    c1.val = 'X';
		    c2.val = 'X';
		    change = true;
		}
	    }
	}
    }
    
    LComponent copy() {
	Composite c;
	c = new Composite();
	c.parts = parts;
	c.wires = wires;
	c.image = image;
	c.name = name;
	return c;
    }
    
}



class Nand extends LComponent implements Serializable {
    
    char in1, in2, out;

    public Nand() {
	in1 = '?';
	in2 = '?';
        out = '?';
    }

    ImageIcon getImage() {
	Image image;
	Graphics g;
	image = new BufferedImage(40,40,BufferedImage.TYPE_INT_RGB);
	g = image.getGraphics();
	g.setColor(Color.white);
	g.fillRect(0,0,40,40);
	g.setColor(Color.black);
	g.drawLine(0,10,5,10);
	g.drawLine(0,30,5,30);

	g.drawLine(5,5,20,5);
	g.drawLine(5,35,20,35);
	g.drawLine(5,5,5,35);

	g.drawLine(35,20,40,20);

	g.drawArc(31,18,4,4,0,360);
	g.drawArc(7,5,23,30,270,180);
	return (new ImageIcon(image));
    }
    
    String name() {
	return "NAND";
    }

    Connections getConnections() {
	Connections c = new Connections();

	c.add(new Connection('?',0.0, 0.25));
	c.add(new Connection('?',0.0,0.75));
	c.add(new Connection('?',1.0,0.5));
	return c;
    }
    
    void evaluate(Connections con) {
	if (in1 == '?' && in2 == '?') {
	    out = '?';
	} else {
	    if (in1 == '1' && in2 == '1') {
		out = '0';
	    } else {
		out = '1';
	    }
	}
	in1 = ((Connection) con.get(0)).val;
	in2 = ((Connection) con.get(1)).val;
	((Connection) con.get(2)).val = out;
	((Connection) con.get(0)).val = '?';
	((Connection) con.get(1)).val = '?';
    }

     LComponent copy() {
	 return new Nand();
     }

}


class Nor extends LComponent  implements Serializable {
    
    char in1, in2, out;

    public Nor() {
	in1 = '?';
	in2 = '?';
        out = '?';
    }

    ImageIcon getImage() {
	Image image;
	Graphics g;
	image = new BufferedImage(40,40,BufferedImage.TYPE_INT_RGB);
	g = image.getGraphics();
	g.setColor(Color.white);
	g.fillRect(0,0,40,40);
	g.setColor(Color.black);

	g.drawArc(31,18,4,4,0,360);

	g.drawLine(0,10,8,10);
	g.drawLine(0,30,8,30);

	g.drawLine(5,5,16,5);
	g.drawLine(5,35,16,35);

	g.drawArc(0,5,10,30,270,180);

	g.drawLine(35,20,40,20);

	g.drawArc(3,5,27,30,270,180);

	return (new ImageIcon(image));
    }
    
    String name() {
	return "NOR";
    }

    Connections getConnections() {
	Connections c = new Connections();

	c.add(new Connection('?',0.0, 0.25));
	c.add(new Connection('?',0.0,0.75));
	c.add(new Connection('?',1.0,0.5));
	return c;
    }
    
    void evaluate(Connections con) {
	if (in1 == '?' && in2 == '?') {
	    out = '?';
	} else {
	    if (in1 == '1' || in2 == '1') {
		out = '0';
	    } else {
		out = '1';
	    }
	}
	in1 = ((Connection) con.get(0)).val;
	in2 = ((Connection) con.get(1)).val;
	((Connection) con.get(2)).val = out;
	((Connection) con.get(0)).val = '?';
	((Connection) con.get(1)).val = '?';
    }

     LComponent copy() {
	 return new Nor();
     }

}



class Or extends LComponent  implements Serializable {
    
    char in1, in2, out;

    public Or() {
	in1 = '?';
	in2 = '?';
        out = '?';
    }

    ImageIcon getImage() {
	Image image;
	Graphics g;
	image = new BufferedImage(40,40,BufferedImage.TYPE_INT_RGB);
	g = image.getGraphics();
	g.setColor(Color.white);
	g.fillRect(0,0,40,40);
	g.setColor(Color.black);
	g.drawLine(0,10,8,10);
	g.drawLine(0,30,8,30);

	g.drawLine(5,5,20,5);
	g.drawLine(5,35,20,35);

	g.drawArc(0,5,10,30,270,180);

	g.drawLine(35,20,40,20);

	g.drawArc(7,5,27,30,270,180);
	return (new ImageIcon(image));
    }
    
    String name() {
	return "OR";
    }

    Connections getConnections() {
	Connections c = new Connections();

	c.add(new Connection('?',0.0, 0.25));
	c.add(new Connection('?',0.0,0.75));
	c.add(new Connection('?',1.0,0.5));
	return c;
    }
    
    void evaluate(Connections con) {
	if (in1 == '?' && in2 == '?') {
	    out = '?';
	} else {
	    if (in1 == '1' || in2 == '1') {
		out = '1';
	    } else {
		out = '0';
	    }
	}
	in1 = ((Connection) con.get(0)).val;
	in2 = ((Connection) con.get(1)).val;
	((Connection) con.get(2)).val = out;
	((Connection) con.get(0)).val = '?';
	((Connection) con.get(1)).val = '?';
    }

     LComponent copy() {
	 return new Or();
     }

}



class Xor extends LComponent  implements Serializable {
    
    char in1, in2, out;

    public Xor() {
	in1 = '?';
	in2 = '?';
        out = '?';
    }

    ImageIcon getImage() {
	Image image;
	Graphics g;
	image = new BufferedImage(40,40,BufferedImage.TYPE_INT_RGB);
	g = image.getGraphics();
	g.setColor(Color.white);
	g.fillRect(0,0,40,40);
	g.setColor(Color.black);
	g.drawLine(0,10,5,10);
	g.drawLine(0,30,5,30);

	g.drawLine(5,5,20,5);
	g.drawLine(5,35,20,35);

	g.drawArc(0,5,10,30,270,180);
	g.drawArc(-4,5,10,30,270,180);

	g.drawLine(35,20,40,20);

	g.drawArc(7,5,27,30,270,180);
	return (new ImageIcon(image));
    }
    
    String name() {
	return "XOR";
    }

    Connections getConnections() {
	Connections c = new Connections();

	c.add(new Connection('?',0.0, 0.25));
	c.add(new Connection('?',0.0,0.75));
	c.add(new Connection('?',1.0,0.5));
	return c;
    }
    
    void evaluate(Connections con) {
	if (in1 == '?' && in2 == '?') {
	    out = '?';
	} else {
	    if ((in1 == '1' && in2 == '0') || (in1 == '0' && in2 == '1') ) {
		out = '1';
	    } else {
		out = '0';
	    }
	}
	in1 = ((Connection) con.get(0)).val;
	in2 = ((Connection) con.get(1)).val;
	((Connection) con.get(2)).val = out;
	((Connection) con.get(0)).val = '?';
	((Connection) con.get(1)).val = '?';
    }

     LComponent copy() {
	 return new Xor();
     }

}



class And extends LComponent  implements Serializable {
    
    char in1, in2, out;

    public And() {
	in1 = '?';
	in2 = '?';
        out = '?';
    }

    ImageIcon getImage() {
	Image image;
	Graphics g;
	image = new BufferedImage(40,40,BufferedImage.TYPE_INT_RGB);
	g = image.getGraphics();
	g.setColor(Color.white);
	g.fillRect(0,0,40,40);
	g.setColor(Color.black);
	g.drawLine(0,10,5,10);
	g.drawLine(0,30,5,30);

	g.drawLine(5,5,20,5);
	g.drawLine(5,35,20,35);
	g.drawLine(5,5,5,35);

	g.drawLine(30,20,40,20);

	g.drawArc(7,5,23,30,270,180);
	return (new ImageIcon(image));
    }
    
    String name() {
	return "AND";
    }

    Connections getConnections() {
	Connections c = new Connections();

	c.add(new Connection('?',0.0, 0.25));
	c.add(new Connection('?',0.0,0.75));
	c.add(new Connection('?',1.0,0.5));
	return c;
    }
    
    void evaluate(Connections con) {
	if (in1 == '?' && in2 == '?') {
	    out = '?';
	} else {
	    if (in1 == '1' && in2 == '1') {
		out = '1';
	    } else {
		out = '0';
	    }
	}
	in1 = ((Connection) con.get(0)).val;
	in2 = ((Connection) con.get(1)).val;
	((Connection) con.get(2)).val = out;
	((Connection) con.get(0)).val = '?';
	((Connection) con.get(1)).val = '?';
    }

     LComponent copy() {
	 return new And();
     }

}



class Not extends LComponent  implements Serializable {
    
    char in1, out;

    public Not() {
	in1 = '?';
        out = '?';
    }

    ImageIcon getImage() {
	Image image;
	Graphics g;
	image = new BufferedImage(30,30,BufferedImage.TYPE_INT_RGB);
	g = image.getGraphics();
	g.setColor(Color.white);
	g.fillRect(0,0,30,30);
	g.setColor(Color.black);
	g.drawLine(0,15,5,15);


	g.drawLine(5,5,5,25);
	g.drawLine(5,25,22,15);
	g.drawLine(5,5,22,15);

	g.drawLine(25,15,30,15);

	g.drawArc(22,13,4,4,0,360);
	return (new ImageIcon(image));
    }
    
    String name() {
	return "NOT";
    }

    Connections getConnections() {
	Connections c = new Connections();

	c.add(new Connection('?',0.0, 0.5));
	c.add(new Connection('?',1.0,0.5));
	return c;
    }
    
    void evaluate(Connections con) {
	if (in1 == '?') {
	    out = '?';
	} else {
	    if (in1 == '1') {
		out = '0';
	    } else {
		out = '1';
	    }
	}
	in1 = ((Connection) con.get(0)).val;
	((Connection) con.get(1)).val = out;
	((Connection) con.get(0)).val = '?';
    }

     LComponent copy() {
	 return new Not();
     }

}



class Output extends LComponent  implements Serializable {
    
    char val;

    public Output() {
	val = '?';
    }

    ImageIcon getImage() {
	BufferedImage bi;
	Graphics g;
	bi = new BufferedImage(40,40,BufferedImage.TYPE_INT_RGB);
	
	g = (bi).getGraphics();
	g.setColor(Color.white);
	g.fillRect(0,0,40,40);
	g.setColor(Color.black);
	g.drawRect(10,7,25,25);
	g.drawLine(0,20,10,20);
	g.setColor(Color.blue);
	g.drawString("" + val,20,25);
	return new ImageIcon(bi);
    }
    
    String name() {
	return "Display";
    }

    Connections getConnections() {
	Connections c = new Connections();
	c.add(new Connection('?',0.0,0.5));
	return c;
    }
    
    void evaluate(Connections con) {
	val = ((Connection) con.get(0)).val;
	((Connection) con.get(0)).val = '?';
    }

    LComponent copy() {
	Output i;
	i = new Output();
	return i;
     }
}


class Input extends LComponent  implements Serializable {

    boolean val;
    
    public Input() {
    }

    ImageIcon getImage() {
	BufferedImage bi;
	Graphics g;
	bi = new BufferedImage(40,40,BufferedImage.TYPE_INT_RGB);
	
	g = (bi).getGraphics();
	g.setColor(Color.white);
	g.fillRect(0,0,40,40);
	g.setColor(Color.black);
	g.drawString("1",2,12);
	g.drawString("0",2,35);

	g.drawLine(30,20,40,20);
	if (val) {
	    g.drawLine(30,20,13,10);
	} else {
	    g.drawLine(30,20,13,30);
	}
	return new ImageIcon(bi);

    }
    
    String name() {
	return "Switch";
    }

    Connections getConnections() {
	Connections c = new Connections();
	c.add(new Connection('?',1.0,0.5));
	return c;
    }
    
    void evaluate(Connections con) {
	((Connection) con.get(0)).val = (val ? '1' : '0'); 
    }

    LComponent copy() {
	Input i;
	i = new Input();
	i.val = val;
	return i;
     }
}


class Port extends LComponent  implements Serializable {
    
    Connections connections;

    public Port() {
	connections =  new Connections();
	connections.add(new Connection('?',0.5, 0.5));
    }

    ImageIcon getImage() {
	BufferedImage bi;
	Graphics g;
	bi = new BufferedImage(6,6,BufferedImage.TYPE_INT_RGB);
	
	g = (bi).getGraphics();
	g.setColor(Color.white);
	g.fillRect(0,0,6,6);
	g.setColor(Color.orange);
	g.drawRect(0,0,5,5);
	return new ImageIcon(bi);
    }
    
    String name() {
	return "Port";
    }

    Connections getConnections() {
	return connections;
    }
    
    void evaluate(Connections con) {
	((Connection) con.get(0)).val = '?';
    }

    LComponent copy() {
	return new Port();
    }
}


class Wire extends LComponent  implements Serializable {
    
    public Wire() {
    }

    ImageIcon getImage() {
	return (new ImageIcon("Wire.jpg"));
    }
    
    String name() {
	return "WIRE";
    }

    Connections getConnections() {
	return null;
    }
    
    void evaluate(Connections con) {
    }

 LComponent copy() {
     return new Wire();
 }
}



class MyCellRenderer extends JLabel implements ListCellRenderer {
     final static ImageIcon longIcon = new ImageIcon("long.gif");
     final static ImageIcon shortIcon = new ImageIcon("short.gif");

     // This is the only method defined by ListCellRenderer.
     // We just reconfigure the JLabel each time we're called.

     public java.awt.Component getListCellRendererComponent(
       JList list,
       Object value,            // value to display
       int index,               // cell index
       boolean isSelected,      // is the cell selected
       boolean cellHasFocus)    // the list and the cell have the focus
     {
         String s = ((LComponent) value).name();
         setText(s);
         setIcon(((LComponent) value).getImage());
	 if (isSelected || cellHasFocus) {
             setBackground(Color.red);
	     setForeground(Color.red);
	 } else {
	     setBackground(list.getBackground());
	     setForeground(list.getForeground());
	 }
	 //setEnabled(list.isEnabled());
	 //setFont(list.getFont());
         return this;
     }
 }


class JWork extends JComponent  implements MouseListener, MouseMotionListener{

    Composite current;
    
    Dimension dim;
    Logic logic;
    int xpress, ypress;
    int xcurr, ycurr;
    LPart cmove;
    boolean pressbox;

    public JWork(Logic l){
	super();
	logic = l;
	setPreferredSize(new Dimension(600,600));
	dim = getSize(null);
	current = new Composite();
	xpress = -1;
        ypress = -1;
	xcurr = -1;
	ycurr = -1;
	pressbox = false;
	cmove = null;
	this.addMouseListener(this);
	this.addMouseMotionListener(this);
    }


    public void clear(Composite c) {
	current = c;
	xpress = -1;
        ypress = -1;
	xcurr = -1;
	ycurr = -1;
	pressbox = false;
	paint(getGraphics());
    }

    static public boolean onboarder(int x, int y, Dimension d) {
	int w,h;
	int b = 10;
	
	w = d.width;
	h = d.height;

	return (0 <= x && x <= b) ||
	    (w-b <= x && x <= w) ||
	    (0 <= y && y <= b) ||
	    (h-b <= y && y <= h);
    }

    public void mouseClicked(MouseEvent e) {
	LComponent lcomp;
	LPart p;
	LPart c;
	int x;
	Port port;
	
	getSize(dim);

	c = getConpon(e.getX(),e.getY());
	if (c != null) {
	    if (c.comp instanceof Input) {
		((Input) c.comp).val = ! ((Input) c.comp).val;
	    }
	} else {
	    lcomp = (LComponent) logic.compJList.getSelectedValue();
	    if (lcomp instanceof Port && onboarder(e.getX(), e.getY(), dim)) {
		//System.out.println("onboarder");
		port = (Port) lcomp.copy();
		p = new LPart(port,(double) e.getX()/ (double) dim.width
			      ,(double) e.getY()/ (double) dim.height);
		current.parts.add(p);
		current.connections.add((Connection)port.connections.get(0));
		logic.drawbox.drawBoarderPort(p.xpos,p.ypos);
	    } else {
		//System.out.println("Mouse Clicked" + lcomp.name());
		p = new LPart(lcomp.copy(),(double) e.getX()/ (double) dim.width
			      ,(double) e.getY()/ (double) dim.height);
		current.parts.add(p);
	    }
	}
	paint(getGraphics());
    }
    
    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseMoved(MouseEvent e) {
    }

    public void mouseDragged(MouseEvent e) {
	//System.out.println("Mouse Dragged");
	Graphics g = getGraphics();
	g.setXORMode(Color.yellow);
        if (pressbox) {
	    if (xcurr != -1 && ycurr != -1) {
		g.drawLine(xpress,ypress,xcurr,ycurr);
	    } 
	    xcurr = e.getX();
	    ycurr = e.getY();
	    g.drawLine(xpress,ypress,xcurr,ycurr);
	}
	g.setPaintMode();
    }
    
    public void mousePressed(MouseEvent e) {
	//System.out.println("Mouse Pressed");
	xpress = e.getX();
        ypress = e.getY();
	if (getCon(xpress,ypress) == null) {
	    pressbox = false;
	    cmove = getConpon(e.getX(),e.getY());
	} else {
	    pressbox = true;
	}
    }

    public void mouseReleased(MouseEvent e) {
	int xrelease, yrelease;
	LWire lw;
	Graphics g = getGraphics();
	if (pressbox && xcurr != -1 && ycurr != -1) {
	    g.setXORMode(Color.yellow);
	    g.drawLine(xpress,ypress,xcurr,ycurr);
	    g.setPaintMode();
	}
	xrelease = e.getX();
        yrelease = e.getY();
	if (pressbox) {
	    lw = getWire(xpress,ypress, xrelease, yrelease);
	    if (lw != null) {
	          current.wires.add(lw);
	    }
	    paint(getGraphics());
	} else if (cmove != null && !((xpress == xrelease)&&(ypress == yrelease))) {
	    getSize(dim);
	    if (xrelease >= 0 && xrelease < dim.width &&
		yrelease >= 0 && yrelease < dim.height) {
		cmove.xpos = (double) xrelease/(double) dim.width; 
		cmove.ypos = (double) yrelease/(double) dim.height;

	    } else {
		current.delete(cmove);
		//System.out.println("Delete it.");
	    }
	    paint(getGraphics());
	    cmove = null;
	}	
        xpress = -1;
        ypress = -1;
	xcurr = -1;
	ycurr = -1;
    }
     
    public LWire getWire(int xp, int yp, int xr, int yr) {
	LPC lpc1, lpc2;


	lpc1 = getCon(xp,yp);
        lpc2 = getCon(xr,yr);

	if (lpc1 != null && lpc2 != null) {
	    return new LWire(lpc1,lpc2);
	}
	return null;
    }

    public LPC getCon(int xp, int yp) {
	Enumeration e, e2, e3;
	LPart lp;
	Image i;
	int x,y, k;
	Connection c;

	getSize(dim);
	e = current.parts.elements();
	while (e.hasMoreElements()) {
	    lp = (LPart) e.nextElement();
	    i = (lp.comp.getImage()).getImage();
	    if (lp.ports != null) {
		k =0;
		e2 = lp.ports.elements();
		while (e2.hasMoreElements()) {
		    c = (Connection) e2.nextElement();
		    x = (int) Math.round(lp.xpos * dim.width - i.getWidth(null)/2 + i.getWidth(null)*c.xpos-5);
		    y = (int) Math.round(lp.ypos * dim.height- i.getHeight(null)/2 + i.getHeight(null)*c.ypos-5);
		    if (x <= xp && xp <= x+10 && y <= yp && yp <= y+10) {
			return new LPC(lp,k);
		    }
		    k++;	
		}
	    }    
	}
	return null;
    }

    public LPart getConpon(int xp, int yp) {
	Enumeration e, e2;
	LPart lp;
	Image i;
	int x,y, xw,yw,k;
	Connection c;

	getSize(dim);
	e = current.parts.elements();
	while (e.hasMoreElements()) {
	    lp = (LPart) e.nextElement();
	    i = (lp.comp.getImage()).getImage();

	    x = (int) Math.round(lp.xpos * dim.width - i.getWidth(null)/2 );
	    y = (int) Math.round(lp.ypos * dim.height- i.getHeight(null)/2 );
	    xw =(int) Math.round(i.getWidth(null)); 
	    yw = (int) Math.round(i.getHeight(null)); 
	    if (x <= xp && xp <= x+xw && y <= yp && yp <= y+yw) {
		return lp;
	    }
	}
	return null;
    }


    public Point getPoint(LPart lp, int con) {
	Image i;
	Connection c;
	getSize(dim);	
	
	i = (lp.comp.getImage()).getImage();
	c = (Connection) lp.ports.get(con); 
	return new Point((int) Math.round(lp.xpos * dim.width - i.getWidth(null)/2 + i.getWidth(null)*c.xpos),
			 (int) Math.round(lp.ypos * dim.height- i.getHeight(null)/2 + i.getHeight(null)*c.ypos));
    }

    public void paint(Graphics g) {
	Enumeration e, e2, e3;
	LPart lp;
	boolean r;
	Image i;
	Connection c;
	LWire lw;
	Point p1, p2;
	
	if (g != null) {
	g.setColor(Color.white);
	getSize(dim);
	g.fillRect(0,0,dim.width,dim.height);
	if (current != null){
	    g.setColor(Color.red);
	    g.drawRect(5,5,dim.width-10,dim.height-10);
	    e = current.parts.elements();
	    while (e.hasMoreElements()) {
		lp = (LPart) e.nextElement();
		i = (lp.comp.getImage()).getImage();
		
		r = g.drawImage(i,
				(int) Math.round(lp.xpos * dim.width - i.getWidth(null)/2),
				(int) Math.round(lp.ypos * dim.height- i.getHeight(null)/2),
				null);
		if (lp.ports != null) {
		    e2 = lp.ports.elements();
		    while (e2.hasMoreElements()) {
			c = (Connection) e2.nextElement();
			r = g.drawImage((Logic.port.getImage()).getImage(),
					(int) Math.round(lp.xpos * dim.width - i.getWidth(null)/2 + i.getWidth(null)*c.xpos-3),
					(int) Math.round(lp.ypos * dim.height- i.getHeight(null)/2 + i.getHeight(null)*c.ypos-3),
					null);
		    }
		}



	    }
	    g.setColor(Color.blue);
	    e = current.wires.elements();
	    while (e.hasMoreElements()) {
		lw = (LWire) e.nextElement();
		if (lw != null) {
		    p1 = getPoint(lw.p1,lw.con1);
		    p2 = getPoint(lw.p2,lw.con2);
		    g.drawLine(p1.x,p1.y,p2.x,p2.y);
		}
	
	    }
	}
	}
    }


    public void paintChangable(Graphics g) {
	Enumeration e, e2, e3;
	LPart lp;
	boolean r;
	Image i;
	Connection c;
	LWire lw;
	Point p1, p2;
	
	if (g != null) {
	getSize(dim);
	if (current != null){
	    e = current.parts.elements();
	    while (e.hasMoreElements()) {
		lp = (LPart) e.nextElement();
		if (lp.comp instanceof Output) {
		i = (lp.comp.getImage()).getImage();
		
		r = g.drawImage(i,
			    (int) Math.round(lp.xpos * dim.width - i.getWidth(null)/2),
			    (int) Math.round(lp.ypos * dim.height- i.getHeight(null)/2),
			    null);
		if (lp.ports != null) {
		    e2 = lp.ports.elements();
		    while (e2.hasMoreElements()) {
			c = (Connection) e2.nextElement();
			r = g.drawImage((Logic.port.getImage()).getImage(),
					(int) Math.round(lp.xpos * dim.width - i.getWidth(null)/2 + i.getWidth(null)*c.xpos-3),
					(int) Math.round(lp.ypos * dim.height- i.getHeight(null)/2 + i.getHeight(null)*c.ypos-3),
					null);
		    }
		}
		}



	    }

	}
	}
    }    

    public void evaluate() {
	//System.out.println("Evaluating work.");
	current.evaluate(null);
	paintChangable(getGraphics());
    }


}

class DrawBox extends JComponent  implements MouseListener, MouseMotionListener, KeyListener, FocusListener {
    Dimension dim;
    Point last = null;
    String state;
    int xoff = 0;
    int yoff = 0;
    Image image;
    Graphics graphics;
    
    public DrawBox() {
	super();
	dim = new Dimension(60,60);
	image = new BufferedImage(60,60,BufferedImage.TYPE_INT_RGB);
	graphics = image.getGraphics();
	clear();
	setPreferredSize(dim);
	setMinimumSize(dim);
	setMaximumSize(dim);
	setState("lines");
	addKeyListener(this);
	addMouseMotionListener(this);
	addMouseListener(this);	
	addFocusListener(this);
	//	setFocusable(true);
    }

    public void setState(String s) {
	state = s;
    }

    public Image getImage() {
	Image bi = new BufferedImage(60,60,BufferedImage.TYPE_INT_RGB);
	Graphics g = bi.getGraphics();
	g.drawImage(image,0,0,null);
	return bi;
    }

    public void focusGained(FocusEvent e) {
	//	System.out.println("Focus Gained");
    }

    public void focusLost(FocusEvent e) {
	//	System.out.println("Focus Lost");
    }

    public void keyPressed(KeyEvent e) {
	//	System.out.println("key Pressed : " + e.getKeyChar());
    }

    public void keyReleased(KeyEvent e) {
	//	System.out.println("key released");
    }


    public void drawBoarderPort(double x, double y) {
	Graphics g = graphics;
	g.setColor(Color.orange);
	g.drawRect((int) Math.round(x*dim.width)-2, (int) Math.round(y*dim.height)-2, 4, 4);
	paint(getGraphics());
	
    }

    public void keyTyped(KeyEvent e) {
	int kc;
	//System.out.println("key Typed : " + (int)e.getKeyChar() + " : "+ e.getKeyCode());
	if (state.equals("text") && last != null) {
	    Graphics g = graphics;
	    kc = (int)e.getKeyChar();
	    if (kc == 8) {
		xoff = xoff - 7;
		g.setColor(Color.white);
		g.fillRect(last.x + xoff,last.y + yoff - 12,7,12);
	    } else if (kc == 10) {
		xoff = 0;
		yoff = yoff + 12;
	    } else {
		g.setColor(Color.black);
		g.drawString("" + e.getKeyChar(),last.x + xoff,last.y + yoff);
		xoff = xoff + 7;
		
	    }
	}
	paint(getGraphics());
    }
        
    


    public void mouseClicked(MouseEvent e) {
    }
    public void mousePressed(MouseEvent e) {
	if (state.equals("lines") || state.equals("box") || state.equals("circle") ||
            state.equals("text")) {
		last = new Point(e.getX(),e.getY());
		xoff = 0;
		yoff = 0;
		//System.out.println("setting last : " + last);
	}
	paint(getGraphics());
    }
    public void mouseReleased(MouseEvent e) {
	Graphics g = graphics;
	int x, y, r;

	x = e.getX();
	y = e.getY();
	if (state.equals("scrible")) {
	    last = null;
	} else if (state.equals("lines") && last != null) {
	    //System.out.println("Draw line");
	    g.setColor(Color.black);
	    g.drawLine(last.x, last.y, x, y);
	    last = null;
	} else if (state.equals("box") && last != null) {
	    g.setColor(Color.black);
	    g.drawRect(Math.min(last.x,x), Math.min(last.y,y), Math.abs(e.getX() - last.x),Math.abs(e.getY()-last.y));
	    last = null;	    
	    
	} else if (state.equals("circle") && last != null) {
	    g.setColor(Color.black);
	    r = (int) Math.round(Math.sqrt(Math.pow((last.x-x),2) + Math.pow((last.y-y),2)));
	    g.drawOval(last.x-r, last.y-r, 2*r,2*r);
	    last = null;	    
	    
	} 
	paint(getGraphics());
    }

    public void mouseDragged(MouseEvent e) {
	Graphics g = graphics;
	if (state.equals("scrible")) {
	    //System.out.println("setting last.");
	    if (last != null) {
		g.setColor(Color.black);
		g.drawLine(last.x, last.y, e.getX(),e.getY());
		last.x = e.getX();
		last.y = e.getY();
	    } else {
		last = new Point(e.getX(),e.getY());
	    }
	} else if (state.equals("delete")) {
	    g.setColor(Color.white);
	    g.fillRect(e.getX()-3,e.getY()-3,6,6);
	    last = null;
	}
	paint(getGraphics());
	//System.out.println("mouseDragged : " + e.getX() + " " + e.getY() + " " + last);
    }

    public void mouseMoved(MouseEvent e) {
	//	last = null;
	
    }
    public void mouseEntered(MouseEvent e) {
	requestFocus();
    }
    public void mouseExited(MouseEvent e) {
    }

    public void clear() {
	Graphics g = graphics;

	g.setColor(Color.white);
	g.fillRect(0,0,dim.width,dim.height);
	g.setColor(Color.red);
	g.drawRect(2,2,dim.width-4,dim.height-4);
    }

    public void paint(Graphics g) {
	g.drawImage(image,0,0,null);
	//	g.setColor(Color.white);
	//	g.fillRect(0,0,dim.width,dim.height);
    }

}




class Logic extends JFrame implements  ActionListener {

    
    private JMenuBar theMenuBar;
    private JMenu fileMenu;
    //    private JMenu editMenu;
    private JMenu helpMenu;
    private JMenuItem newMenuItem;
    private JMenuItem saveMenuItem;
    private JMenuItem loadMenuItem;
    private JMenuItem quitMenuItem;
    private JMenuItem infoMenuItem;
    private JButton simulateJButton;
    private JPanel mainJPanel;
    private JPanel workJPanel;
    private JPanel buttonJPanel;
    private JPanel sideJPanel;
    private JPanel sideButtonJPanel;
    private JButton clearJButton;
    private JButton newJButton;
    private JButton editJButton;
    private JComboBox selectJComboBox;
    private JScrollPane compJScrollPane;
    DrawBox drawbox;
    JList compJList;
    Vector compVec;
    JWork work;

    JTextArea infoText;

    TimerTask simtime;
    Timer simtimer;

    JFileChooser jfcs;

    static LComponent nand = new Nand();
    static LComponent and = new And();
    static LComponent or = new Or();
    static LComponent nor = new Nor();
    static LComponent xor = new Xor();
    static LComponent notgate = new Not();
    static LComponent output = new Output();
    static LComponent input = new Input();
    static LComponent port = new Port();
    static LComponent wire = new Wire();
    static final String infoString = 
	"      Logic Simulator \n" +
	"     -----------------\n" +
	" \n" +
	" Eric McCreath FEIT@ANU 2003\n" +
	" \n" +
	"   This program is designed facilitate the design and \n" +
	"simulation of simple logic components. The program provides\n" +
	"the basic gates, along with a toggle switch which can be used \n" +
	"for input, and a display to permit users to see the value of a  \n" +
	"wire. \n" +
	" \n" +
	" Adding Gates : To add a gate simply select the gate you\n" +
	" wish to add from the list on the right. Then click on the\n" +
	" work area in the location you wish to add the gate.\n" +
	" \n" +
	" Connecting Gates : To connect the gates with wires simply\n" +
	" drag the mouse between the ports you wish to connect.\n" +
	" \n" +
	" Moving Gates : To move a gate just drag it to the desired\n" +
	" location.\n" +
	" \n" +
	" Deleting Gates : To delete a gate simply drag it off the \n" +
	" work area.  Note that, in the current implementation it is not\n" +
	" possible to delete individual wires.\n" +
	" \n" +
	" Changing the value of a switch : Click on the switch to toggle\n" +
	" its value.\n" +
	" \n" +
	" Saving Components : Select \"file->save\" menu item to save the \n" +
	" current working area.\n" +
	" \n" +
	" Loading Components : Select \"file->load\" menu item to load a\n" +
	" working area from a file.  Note this will overwrite the current\n" +
	" working area.\n" +
	" \n";

    public Logic() {
	super("Logic");
	theMenuBar = new JMenuBar();
        fileMenu = new JMenu("File");
        //editMenu = new JMenu("Edit");
        helpMenu = new JMenu("Help");

	infoText = new JTextArea();
	infoText.setText(infoString);
	infoText.setEditable(false);
	
	simtime = new TimerTask() {
		public void run() {
		    if (work != null) {
		    work.evaluate();
		    }
		}
	    };

	simtimer = new Timer();
	
	//	drawbox = new DrawBox();

	//	newJButton = new JButton("new");
	//	newJButton.addActionListener(this);
	//editJButton = new JButton("edit");
	//editJButton.addActionListener(this);

        newMenuItem = new JMenuItem("New");
        newMenuItem.addActionListener(this);
        saveMenuItem = new JMenuItem("Save");
        saveMenuItem.addActionListener(this);
        loadMenuItem = new JMenuItem("Load");
        loadMenuItem.addActionListener(this);


        quitMenuItem = new JMenuItem("Exit");
        quitMenuItem.addActionListener(this);
        infoMenuItem = new JMenuItem("Info");
        infoMenuItem.addActionListener(this);

	compVec = new Vector();
	compVec.add(port);
	compVec.add(and);
	compVec.add(nand);
	compVec.add(or);
	compVec.add(nor);
	compVec.add(xor);
	compVec.add(notgate);
	compVec.add(input);
	compVec.add(output);

	compJList = new JList(compVec);
	compJList.setCellRenderer(new MyCellRenderer());

	compJScrollPane = new JScrollPane(compJList);

        fileMenu.add(newMenuItem);
        fileMenu.add(loadMenuItem);
        fileMenu.add(saveMenuItem);
        fileMenu.add(quitMenuItem);
        helpMenu.add(infoMenuItem);
        theMenuBar.add(fileMenu);
        //theMenuBar.add(editMenu);
        theMenuBar.add(helpMenu);
	this.setJMenuBar(theMenuBar);
	String list[] =  {"lines","scrible","box","circle","text","delete"};
	simulateJButton = new JButton("Simulate");

	selectJComboBox = new JComboBox(list);
	selectJComboBox.setMaximumSize(new Dimension(125,40));
	clearJButton = new JButton("Clear");
	sideButtonJPanel = new JPanel();
	clearJButton.addActionListener(this);
	sideButtonJPanel.setLayout(new BoxLayout(sideButtonJPanel,BoxLayout.X_AXIS));

	sideButtonJPanel.add(clearJButton);
	sideButtonJPanel.add(selectJComboBox);
	selectJComboBox.addActionListener(this);

	work = new JWork(this);

	/* buttonJPanel = new JPanel();
        buttonJPanel.setLayout(new BoxLayout(buttonJPanel,BoxLayout.X_AXIS));
        buttonJPanel.add(simulateJButton);
	simulateJButton.addActionListener(this); */

	sideJPanel = new JPanel();
        sideJPanel.setLayout(new BoxLayout(sideJPanel,BoxLayout.Y_AXIS));
	/*sideJPanel.add(newJButton);
	sideJPanel.add(editJButton);
	sideJPanel.add(drawbox);
	sideJPanel.add(sideButtonJPanel);*/
	sideJPanel.add(compJScrollPane);

	workJPanel = new JPanel();
        workJPanel.setLayout(new BoxLayout(workJPanel,BoxLayout.X_AXIS));
        workJPanel.add(work);
	workJPanel.add(sideJPanel);

	mainJPanel = new JPanel();
        mainJPanel.setLayout(new BoxLayout(mainJPanel,BoxLayout.Y_AXIS));
        //mainJPanel.add(buttonJPanel);
        mainJPanel.add(workJPanel);
	
	jfcs = new JFileChooser(".");
	jfcs.setFileFilter(new CircuitFileFilter());
	
       

        (this.getContentPane()).add(mainJPanel);
	simtimer.schedule(simtime,0,100);
    }

    void save(File f) {
	try {
	    FileOutputStream file = new FileOutputStream(f);
	    ObjectOutputStream out = new ObjectOutputStream(file);
	    out.writeObject(work.current);
	    out.flush();
	    file.close();
	} catch (Exception e) {
	    System.out.println("problem saving : " + e);
	}
    }

    Composite load(File f) {
	Composite res;
        try {
            FileInputStream file = new FileInputStream(f);
            ObjectInputStream in = new ObjectInputStream(file);
            res = (Composite) in.readObject();
            file.close();
        } catch (Exception e) {
            System.out.println("problem loading : " + e);
            return new Composite();
        }
        return res;
    }


    public void actionPerformed(ActionEvent e) {
	String sel;
	int res;
	String n;
	if ( e.getSource() == quitMenuItem) {
	    System.out.println("Bye");
	    System.exit(0);
	} else if ( e.getSource() == newMenuItem) {
	    work.clear(new Composite());
	} else if ( e.getSource() == saveMenuItem) {
	    res = jfcs.showSaveDialog(this);
	    if (res == JFileChooser.APPROVE_OPTION) {
		//System.out.println("You chose : " + jfcs.getSelectedFile().getName());
		save(jfcs.getSelectedFile());
	    }

	} else if ( e.getSource() == loadMenuItem) {

	    res = jfcs.showOpenDialog(this);
	    if (res == JFileChooser.APPROVE_OPTION) {
		//System.out.println("You chose : " + jfcs.getSelectedFile().getName());
		
		work.clear(load(jfcs.getSelectedFile()));
		
	    }


	} else if ( e.getSource() == infoMenuItem) {
  
	    JOptionPane.showMessageDialog(this, infoText,
					   "information", 
					   JOptionPane.INFORMATION_MESSAGE);


	}

	/*else if (e.getSource() == simulateJButton) {
	    work.evaluate();
	} else if (e.getSource() == clearJButton) {
	    drawbox.clear();
	} else if (e.getSource() == selectJComboBox) {
	    sel = (String) selectJComboBox.getSelectedItem();
	    drawbox.setState(sel);
	} else if (e.getSource() == newJButton) {
	    System.out.println("new");

	    Composite comp = new Composite();
	    compVec.add(comp);
	    compJList.setListData(compVec);
	    comp.image = new ImageIcon(drawbox.getImage());
	    drawbox.clear();
	    work.clear(comp);

	} else if (e.getSource() == editJButton) {
	    System.out.println("edit");
	    }*/
    }
    public static void main(String[] args) {
	
        try {
            Logic f = new Logic();
            f.pack();
            f.setVisible(true);
        }
        catch (Throwable e) {
            e.printStackTrace();
        }
    }

}

