/*
 * Audin Malmin
 * 5-4-98
 * Java/OS week 5 or 6 (I've lost count).
 *
 * This week has seen less progress.  (I spent my main programming time camping...  :)
 * I've added preliminary Cathedral code.  Basically it will load and display 2 cathedral
 * files.  They are displayed at fixed positions in the "background"...
 *
 * Current layout (has-a relationships):
 *
 * ColonyApplet --+
 *                |
 *              Frame --+
 *                      |
 *                  DrawPanel --+--------+---------+---------------+---------+---------+
 *                              |        |         |               |         |         |
 *                           LocalDL RemoteDL   LocalCL        %RemoteCL PopupMenu PopupMenu
 *                            |   |    |   |     |  |            |    |
 *                           Dude |   Dude |     | CathUpdate*%  |  CathUpdate*%
 *                                |        |     |               |
 *                            DudeUpdate*  | Cathedral       Cathedral%
 *                                         |
 *                                    DudeUpdate*
 *
 * Key:		*: thread		%: Not yet implimented
 *
 * (is-a relationships):
 *
 * Panel -> DrawPanel
 *
 * Applet -> ColonyApplet
 *
 * Menu -> PopupMenu
 *
 * LocalDL -> RemoteDL
 *
 * LocalCL -> RemoteCL	(At least in the future)
 *
 * So basically, DrawPanel is the main program loop.  It sets everything up and
 * handles events.  DrawPanel contains two popup menus and two DudeList objects.
 *
 * LocalDL is kind of a mutant linked list container.  It does contain a linked list
 * of Dude objects.  In addition it contains a DudeUpdate object.
 *
 * RemoteDL is derived from LocalDL.  It is identical except for the fact that, in the future,
 * it RemoteDL will get it's movement, creation, and destruction information from the remote
 * machine.
 *
 * 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.
 *
 * LocalCL is another bastard linked list container.  It contains a list of Cathedral object.
 * RemoteCL will be derived from it; again adding a network interfact.
 *
 * Cathedral currently just contains an image.  In the future I plan to have it animate a
 * small string of images...  Cathedrals should be created based on the number of followers
 * each player has.  Their design will shift (toward more complicated, larger structures) as
 * the population grows in size and knowledge.
 *
 * So what does it all do???????????????
 *
 * * It compiles and runs.
 *
 * * Two cathedrals are created and displayed.
 *
 * * 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.
 *
 * * Right-clicking on a cathedral will try to identify the cathedral.  In the future it will
 *   bring up a menu with cathedral options.
 *
 * 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[]) {
		System.out.println("Main\n");
		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 {
	LocalDL local;
	RemoteDL remote;

	LocalCL	localc;
//	RemoteCL remotec;

	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);

		localc = new LocalCL(this);
		localc.add(100, 100);
		localc.add(300, 300);
	}

	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(this, 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(local == null)
			local = new LocalDL(Color.red, this);

		if(remote == null)
			remote = new RemoteDL(Color.blue, this);
		
		if(e.isPopupTrigger()) {
			Dude tempdude;
			Cathedral tempcath;
			System.out.println("Popup menu");

			if((tempdude = local.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 {
				if((tempdude = remote.find(e.getX(), e.getY())) != null) {
					System.out.println("Found him, but he's a heretic!");
					deaddude = tempdude;
					dudepopup.remove(0);
					dudepopup.insert("Remote Dude: " + deaddude.num(), 0);
					dudepopup.show(e.getComponent(), e.getX(), e.getY());
				} else {					
					if((tempcath = localc.find(e.getX(), e.getY())) != null) {
						System.out.println("It's a cathedral!");
					} else {
					System.out.println("No dudes around");
					bkpopup.show(e.getComponent(), e.getX(), e.getY());
					}
				}
			} // End Popup Menu.
		} else if (e.getClickCount() > 1) {
			System.out.println("Multiclick");
		} else if (e.getClickCount() == 1) {
			local.add(e.getX(), e.getY());
			System.out.println("new local");

			remote.add((int)(Math.random() * 640), (int)(Math.random() * 480));
			System.out.println("new remote");
		}
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mouseClicked(MouseEvent e) {
	}

    public void paint(Graphics g) {
		if(localc != null)
			localc.paint(g);
		if(local != null)
			local.paint(g);
		if(remote != null)
			remote.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 LocalDL {
	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 LocalDL(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 RemoteDL extends LocalDL {

	public RemoteDL(Color dudecolor, DrawPanel drawp) {
		super(dudecolor, drawp);
	}

}

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);
   }
}

class LocalCL {
	Cathedral first;
	Cathedral last;
	Cathedral previous;

	Cathedral curr;

//	DudeUpdate du;

	int total;

	Graphics gc;

	DrawPanel dp;

	Color col;

	public LocalCL(DrawPanel drawp) {
		dp = drawp;
		gc = dp.getGraphics();
		first = curr = last = null;
		total = 0;
	}

	public synchronized boolean add(int x_pos, int y_pos) {
		if(first == null) {
			total = 0;
			previous = null;
			first = curr = last = new Cathedral("3dk022.gif", x_pos, y_pos, total, dp);
			if(first != null) {
				curr.draw(gc);
//				du = new DudeUpdate(first, dp);
//				du.start();
			}
		} else {
			previous = curr;
			curr = new Cathedral("gas.gif", x_pos, y_pos, total, dp);
			last.next = curr;
			last = curr;
			if(first != null) {
				curr.draw(gc);
			}
		}
		total++;
		return (curr != null);
	}

	public synchronized Cathedral find(int x, int y) {
		Cathedral current;

		for(current = first; current != null; current = current.next)
			if(current.test(x, y))
				return current;

		return null;
	}

	public synchronized Cathedral current() {
		return curr;
	}

	public synchronized Cathedral 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 total;
	}

	public void paint(Graphics g) {
		Cathedral current;

		for(current = first; current != null; current = current.next)
			current.draw(g);
	}
}

class Cathedral {
	String name;
	Image img;
	DrawPanel dp;
	int x, y, num;
	int height, width;

	Cathedral next;

	public Cathedral(String n, int x1, int y1, int nu, DrawPanel d) {
		name = n;
		x = x1;
		y = y1;
		num = nu;
		dp = d;
		img = Toolkit.getDefaultToolkit().getImage(name);
		if(img != null) {
			height = img.getHeight(dp);
			width  = img.getWidth(dp);
		}

		System.out.println("Width: " + width + "Height: " + height);
	}

	boolean test(int x1, int y1) {
//		System.out.println("Width: " + img.getWidth(dp) + "Height: " + img.getHeight(dp));

		if((x1 > x && x1 < x + img.getWidth(dp)) && (y1 > y && y1 < y + img.getHeight(dp)))
			return true;
		else
			return false;
		
	}

	public void draw(Graphics g) {
		if(g != null && img != null && dp != null)
			g.drawImage(img, x, y, dp);
	}
}

