/*
 * Audin Malmin
 * 4-26-98
 * Java/OS week 4.
 *
 * This week has seen some real progress.  I have (I hope) eradicated the race
 * conditions which were plaguing me last week.
 *
 * Current layout:
 *
 * ColonyApplet --+
 *                |
 *              Frame --+
 *                      |
 *                  DrawPanel --+--------+--------+---------+
 *                              |        |        |         |
 *                          DudeList DudeList PopupMenu PopupMenu
 *                            |   |    |   |
 *                           Dude |   Dude |
 *                                |        |
 *                            DudeUpdate*  |
 *                                         |
 *                                    DudeUpdate*
 *
 * So basically, DrawPanel is the main program loop.  It sets everything up and
 * handles events.  DrawPanel contains two popup menus and two DudeList objects.
 *
 * DudeList is kind of a mutant linked list container.  It does contain a linked list
 * of Dude objects.  In addition it contains a DudeUpdate object.
 *
 * DudeUpdate is a thread.  It's job is to animate each of the Dudes.  Basically it
 * runs through the linked list on it's own (it gets a pointer to the "first" Dude
 * object), calling the Dude.move() function for each one.  The DudeUpdate constructor
 * also gets a pointer to the main DrawPanel object, so that it can get ahold of the
 * Graphics Context and the background color.
 *
 * So what does it all do???????????????
 *
 * * It compiles and runs.
 *
 * * Left-clicking on the screen will create one red Dude directly under the mouse
 *   cursor, and one blue Dude will be created randomly somewhere else.
 *
 * * The Dudes (both red and blue) will move around the window, bouncing off the
 *   edges.
 *
 * * The Dudes will vary slightly in color.  (Ie: all red dudes are red, but some are
 *   brighter and some are darker.)
 *
 * * Right-clicking on an unoccupied section of screen you will get a popup menu
 *   with a list of acts you can perform on the general area.  These acts will have
 *   a circle of influance on which they will be active.  They will also most likely
 *   "taint" the area with an aura of fear.  (ie: the Dudes should try to stay away
 *   from these areas.)
 *
 * * Right-clicking in a small area around a Dude will give you a popup menu with both
 *   the normal options and acts you can inflict on the Dude itself.  The menu also
 *   gives you the Dude's number.  Currently the only working option is "Mutate", which
 *   causes the dude to double in size.
 *
 * So what doesn't it do???
 *
 * * You can't currently kill a Dude.  This isn't hard to add, though.
 *
 * * There's a problem with erasing the first drawing of a Dude...so you get some
 *   fluff left over on the background.
 */
import java.awt.event.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
import java.applet.*;

public class ColonyApplet extends Applet {
	public void init() {
		System.out.println("Init\n");
		setLayout(new BorderLayout());
		DrawPanel dp = new DrawPanel();
		add("Center", dp);
	}

	public static void main(String args[]) {
		Frame f = new Frame("wow");
		ColonyApplet drawtest = new ColonyApplet();
		drawtest.init();
		drawtest.start();

		f.add("Center", drawtest);
		f.setSize(640, 480);
		f.show();
	}
}

class DrawPanel extends Panel implements MouseListener, MouseMotionListener,
      ActionListener {
	DudeList d, c;

	PopupMenu dudepopup, bkpopup;

	Dude deaddude;

	String[] eventlst = {						// List of event strings...
		new String("Info"),						// 0
		new String("Giant"),					// 1
		new String("Zap"),						// 2
		new String("Plague"),					// 3
		new String("etc"),						// 4
		new String("Barney"),					// 5
		new String("The pig is comming..."),	// 6
		new String("Giant Foot")				// 7
	};

	public DrawPanel() {
		setBackground(Color.green.darker().darker());
		addMouseMotionListener(this);
		addMouseListener(this);

		dudepopup = new PopupMenu();
		makeMenu(dudepopup,
				 new Object[] {
			     	"This menu has been poorly used",
			        null,
			        "Info",
			        makeMenu("Mutate",
			        	new Object[] {
							"Giant",
							"Barney",
							"The pig is comming...",
							"Giant Foot"							
						}, this),
			        "Zap",
			        "Plague",
			        "etc" },
			        this);

		bkpopup = new PopupMenu();
		makeMenu(bkpopup,
		         new Object[] {
		         	"Zap",
		         	"Plague",
		         	"etc"
		         }, this);

		add(dudepopup);
		add(bkpopup);
	}

	public void actionPerformed(ActionEvent e) {
		int i = 0;
		
		MenuItem item = (MenuItem)e.getSource();
		String arg = item.getLabel();

		for(; i < eventlst.length; i++)
			if(arg.equalsIgnoreCase(eventlst[i]))
				break;

		System.out.println("ActionPerformed: i = " + i);

		switch(i) {
			case 0:
				System.out.println("Info");
//				AboutDialog ad = new AboutDialog(getParent(),getParnet(), new Label("" + deaddude));
//				ad.show();
				break;
			case 1:
				deaddude.setform(1);
				System.out.println("Mutate dude!");
				break;
			case 2:
				System.out.println("Zap!");
				break;
			case 3:
				System.out.println("Plague");
				break;
			case 4:
				System.out.println("etc...");
				break;
			case 5:
				System.out.println("Barney");
				break;
			case 6:
				System.out.println("Big");
				break;
			case 7:
				System.out.println("Foot");
				break;
			default:
				System.out.println("Wow, something is screwed");
				break;
		}

		deaddude = null;
	}

	public void mouseDragged(MouseEvent e) {
		e.consume();
	}

	public void mouseMoved(MouseEvent e) {
	}

	public void mousePressed(MouseEvent e) {
	    e.consume();
	}

	public void mouseReleased(MouseEvent e) {
		System.out.println("Mouseevent");
	    e.consume();


		if(d == null)
			d = new DudeList(Color.red, this);

		if(c == null)
			c = new DudeList(Color.blue, this);
		

		if(e.isPopupTrigger()) {
			Dude tempdude;
			System.out.println("Popup menu");

			if((tempdude = d.find(e.getX(), e.getY())) != null) {
				System.out.println("Found him!");
				deaddude = tempdude;
				dudepopup.remove(0);
				dudepopup.insert("Dude: " + deaddude.num(), 0);
				dudepopup.show(e.getComponent(), e.getX(), e.getY());
			} else {
				System.out.println("No dudes around");
				bkpopup.show(e.getComponent(), e.getX(), e.getY());
			}

		} else if (e.getClickCount() > 1) {
			System.out.println("Multiclick");
		} else if (e.getClickCount() == 1) {
			d.add(e.getX(), e.getY());
			System.out.println("new d");

			c.add((int)(Math.random() * 640), (int)(Math.random() * 480));
			System.out.println("new c");
		}
//	    repaint();
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mouseClicked(MouseEvent e) {
	}

    public void paint(Graphics g) {
		if(c != null)
			c.paint(g);
		if(d != null)
			d.paint(g);
    }


   private static Menu makeMenu(Object parent, 
      Object[] items, Object target)
   {  Menu m = null;
      if (parent instanceof Menu)
         m = (Menu)parent;
      else if (parent instanceof String)
         m = new Menu((String)parent);
      else
         return null;

      for (int i = 0; i < items.length; i++)
      {  if (items[i] instanceof String)
         {  MenuItem mi = new MenuItem((String)items[i]);
            if (target instanceof ActionListener)
               mi.addActionListener((ActionListener)target);
            m.add(mi);
         }
         else if (items[i] instanceof CheckboxMenuItem
            && target instanceof ItemListener)
         {  CheckboxMenuItem cmi 
               = (CheckboxMenuItem)items[i];
            cmi.addItemListener((ItemListener)target);
            m.add(cmi);
         }
         else if (items[i] instanceof MenuItem)
         {  MenuItem mi = (MenuItem)items[i];
            if (target instanceof ActionListener)
               mi.addActionListener((ActionListener)target);
            m.add(mi);
         }
         else if (items[i] == null) 
            m.addSeparator();
      }

      return m;
   }
}

class DudeList {
	Dude first;
	Dude last;
	Dude previous;

	Dude curr;

	DudeUpdate du;

//	Dude update_curr;
//	Dude disp_curr;

	int totaldudes;

	Graphics gc;

	DrawPanel dp;

	Color col;

	public DudeList(Color dudecolor, DrawPanel drawp) {
		dp = drawp;
		gc = dp.getGraphics();
		col = dudecolor;
		first = curr = last = null;
		totaldudes = 0;
	}

	public synchronized boolean add(int x_pos, int y_pos) {
		if(first == null) {
			totaldudes = 1;
			previous = null;
			first = curr = last = new Dude(x_pos, y_pos, col, totaldudes);
			if(first != null) {
				curr.draw(gc);
				du = new DudeUpdate(first, dp);
				du.start();
			}
		} else {
//			du.stop();
//			try {
//				du.join();
//			} catch (InterruptedException e) {
//			}
			totaldudes++;
			previous = curr;
			curr = new Dude(x_pos, y_pos, col, totaldudes);
			last.next = curr;
			last = curr;
			if(first != null) {
				curr.draw(gc);
//				du = new DudeUpdate(first, dp);
//				du.start();
			}
		}
		totaldudes ++;
		return (curr != null);
	}

	public synchronized Dude find(int x, int y) {
		Dude current;

		for(current = first; current != null; current = current.next)
			if(current.test(x, y))
				return current;

		return null;
	}

	public synchronized Dude current() {
		return curr;
	}

	public synchronized Dude reset() {
		curr = first;
		return curr;
	}

	public synchronized boolean more() {
		if(curr == null)
			return false;
		
		if(curr != null && curr.next != null)
			return true;
		else
			return false;
	}

	public int getTotal() {
		return totaldudes;
	}

	public void paint(Graphics g) {
		Dude current;

		for(current = first; current != null; current = current.next)
			current.draw(g);
	}
}

class Dude {
	short x, y;
	short dx = 2;
	short dy = 2;
	Color col;

	int form = NORMAL;

	int num;

	boolean drawn = false;

	Dude next;

	static final int NORMAL = 0;
	static final int BIG    = 1;
	static final int LITTLE = 2;

	Dude(int x1, int y1, Color color, int n) {
		System.out.println("Dude: constructor.");

		int funny;
		x = (short)x1;
		y = (short)y1;
		col = color;
		num = n;
		next = null;
	
		// This "funny" stuff randomly assigns a shade to the new Dude.
		
		if((funny = (int)((Math.random() - 0.5) * 7)) > 0)
			for(; funny > 0; funny--)
			    col = col.brighter();
		else
			for(; funny < 0; funny++)
				col = col.darker();
	}

	boolean test(int x1, int y1) {
//		System.out.println("Dude:test(" + x1 + ", " + y1 + "), x = " + x + ", y = " + y);
		if((x1 < x + 8 && x - 8 < x1) && (y1 < y + 15 && y1 > y - 15))
			return true;
		else
			return false;
	}

	void setform(int x) {
		form = x;
	}

	void move(Graphics g, Color bg) {
//		if(Math.random() > 0.003)
//			return;

		g.setXORMode(bg);

//		erase(g, bg);
		
//	
//	if(drawn) {
			draw(g);
//			drawn = false;
//		}

		x += dx;
		y += dy;

		if(Math.random() < 0.001)
			dx += (short)(Math.random() - .5);

		if(x < 0) {
			x = 0;
			dx = (short)-dx;
		}

		if(x + 8 >= 640) {
			x = 640 - 8;
			dx = (short)-dx;
		}

		if(y < 0) {
			y = 0;
			dy = (short)-dy;
		}

		if(y + 20 >= 480) {
			y = 480 - 20;
			dy = (short)-dy;
		}

//		int direction = (int)Math.random() * 360;
//		int distance  = (int)Math.random() * 50;
//		x = (int)(Math.random() * 640);
//		y = (int)(Math.random() * 480);

		draw(g);
//		System.out.println("X: " + x + " Y: " + y);
	}

	void draw(Graphics g) {
		if(!drawn)
			drawn = true;
//		else
//			drawn = false;

		switch(form) {
			case NORMAL:
				g.setColor(Color.black);

				g.drawLine(x, y + 4 , x, y + 10);

				g.drawLine(x, y + 10, x - 3, y + 17);
				g.drawLine(x, y + 10, x + 3, y + 17);
				g.drawLine(x, y + 2, x - 4, y + 10);
				g.drawLine(x, y + 2, x + 4, y + 10);

				g.setColor(col);
				g.fillOval(x - 4, y - 5, 8, 8);
			break;
			
			case BIG:
				g.setColor(Color.black);

				g.drawLine(x, y + 4 , x, y + 20);

				g.drawLine(x, y + 20, x - 3, y + 17);
				g.drawLine(x, y + 20, x + 3, y + 17);
				g.drawLine(x, y + 2, x - 4, y + 20);
				g.drawLine(x, y + 2, x + 4, y + 20);

				g.setColor(col);
				g.fillOval(x - 8, y - 10, 16, 16);
			break;
		}
	}

	void erase(Graphics g, Color bg) {
//		if(!drawn)
//			drawn = true;
//		else
//			drawn = false;
			
		g.setColor(bg);

		g.drawLine(x, y + 4 , x, y + 10);

		g.drawLine(x, y + 10, x - 3, y + 17);
		g.drawLine(x, y + 10, x + 3, y + 17);
		g.drawLine(x, y + 2, x - 4, y + 10);
		g.drawLine(x, y + 2, x + 4, y + 10);

		g.fillOval(x - 4, y - 5, 8, 8);
	}	

	public int num() {
		return num;
	}

	public String toString() {
		return ("Dude[col=" + col + ", X=" + x + ", Y=" + y + "]");
	}

}

class DudeUpdate extends Thread {
	Dude fst;
	Dude curr;
	Graphics gc;
	DrawPanel dp;
	Color bg;

	public DudeUpdate(Dude first, DrawPanel drawp) {
		fst = first;
		dp = drawp;
		gc = dp.getGraphics();
		bg = dp.getBackground();
		System.out.println("DudeUpdate created.");
	}

	public void run() {
		System.out.println("DudeUpdate: run.");
		while(true)
			for(curr = fst; curr != null; curr = curr.next) {
				curr.move(gc, bg);
//				curr.draw(gc);
			}
	}
}

class AboutDialog extends Dialog
{  public AboutDialog(Frame parent, Label lbl)
   {  super(parent, "About DialogTest", true);         

      Panel p1 = new Panel();
//      p1.add(new Label("Core Java 1.1: Fundamentals"));
//      p1.add(new Label("By Cay Horstmann and Gary Cornell"));
      p1.add(lbl);
      add(p1, "Center");
                
      Panel p2 = new Panel();
      Button ok = new Button("Ok");
      p2.add(ok);
      add(p2, "South");

      ok.addActionListener(new ActionListener() { public void
         actionPerformed(ActionEvent evt) { setVisible(false); } } );

      addWindowListener(new WindowAdapter() { public void
            windowClosing(WindowEvent e) { setVisible(false); } } );

      setSize(220, 150);
   }
}

