//******************************************************************************
// Maze.java:	Applet
//
//******************************************************************************
import java.applet.*;
import java.awt.*;
import MazeFrame;

//==============================================================================
// Main Class for applet Maze
//
//==============================================================================
public class Maze extends Applet
{
	// STANDALONE APPLICATION SUPPORT:
	//		m_fStandAlone will be set to true if applet is run standalone
	//--------------------------------------------------------------------------
	boolean m_fStandAlone = false;

	// STANDALONE APPLICATION SUPPORT
	// 	The main() method acts as the applet's entry point when it is run
	// as a standalone application. It is ignored if the applet is run from
	// within an HTML page.
	//--------------------------------------------------------------------------
	public static void main(String args[])
	{
		// Create Toplevel Window to contain applet Maze
		//----------------------------------------------------------------------
		MazeFrame frame = new MazeFrame("Maze");

		// Must show Frame before we size it so insets() will return valid values
		//----------------------------------------------------------------------
		frame.show();
		frame.hide();
		frame.resize(frame.insets().left + frame.insets().right + 320,
		frame.insets().top  + frame.insets().bottom + 320);

		// The following code starts the applet running within the frame window.
		// It also calls GetParameters() to retrieve parameter values from the
		// command line, and sets m_fStandAlone to true to prevent init() from
		// trying to get them from the HTML page.
		//----------------------------------------------------------------------
		Maze applet_Maze = new Maze();

		frame.add("Center", applet_Maze);
		applet_Maze.m_fStandAlone = true;
		applet_Maze.init();
		applet_Maze.start();
		frame.show();
	}

	// Maze Class Constructor
	//--------------------------------------------------------------------------
	public Maze()
	{
		// TODO: Add constructor code here
	}

	// APPLET INFO SUPPORT:
	//		The getAppletInfo() method returns a string describing the applet's
	// author, copyright date, or miscellaneous information.
    //--------------------------------------------------------------------------
	public String getAppletInfo()
	{
		return "Name: MazeMaker 1.0\r\n" +
		       "Author: Walter D. Pullen (Astara@msn.com)\r\n" +
		       "Created with Microsoft Visual J++ Version 1.0";
	}


	// The init() method is called by the AWT when an applet is first loaded or
	// reloaded.  Override this method to perform whatever initialization your
	// applet needs, such as initializing data structures, loading images or
	// fonts, creating frame windows, setting the layout manager, or adding UI
	// components.
    //--------------------------------------------------------------------------
	public void init()
	{
        // If you use a ResourceWizard-generated "control creator" class to
        // arrange controls in your applet, you may want to call its
        // CreateControls() method from within this method. Remove the following
        // call to resize() before adding the call to CreateControls();
        // CreateControls() does its own resizing.
        //----------------------------------------------------------------------
		resize(xmax, ymax);

		// TODO: Place additional initialization code here
	}

	// Place additional applet clean up code here.  destroy() is called when
	// when you applet is terminating and being unloaded.
	//-------------------------------------------------------------------------
	public void destroy()
	{
		// TODO: Place applet cleanup code here
	}

	// Maze Paint Handler
	//--------------------------------------------------------------------------
	public void paint(Graphics g)
	{
		//g.drawString("Created with Microsoft Visual J++ Version 1.0", 10, 20);
		mazedraw(g);
	}

	//		The start() method is called when the page containing the applet
	// first appears on the screen. The AppletWizard's initial implementation
	// of this method starts execution of the applet's thread.
	//--------------------------------------------------------------------------
	public void start()
	{
		// TODO: Place additional applet start code here
		mazestart();
	}
	
	//		The stop() method is called when the page containing the applet is
	// no longer on the screen. The AppletWizard's initial implementation of
	// this method stops execution of the applet's thread.
	//--------------------------------------------------------------------------
	public void stop()
	{
	}


	// TODO: Place additional applet code here

	public boolean mouseDown(Event evt, int x, int y)
	{
		if (fDot) {
			if (x > y) {
				if (xmax-1-x > y)
					movedot(0, -1);
				else
					movedot(1, 0);
			} else {
				if (x > ymax-1-y)
					movedot(0, 1);
				else
					movedot(-1, 0);
			}
		} else {
			mazestart();
			repaint();
		}
		return true;
	}

	public boolean keyDown(Event evt, int key)
	{
		char ch = (char)key;

		switch (key) {
		case '\n':
		case '\r':
			mazestart();
			repaint();
			break;
		case ',':
			mazeresize(xsiz - 1, ysiz - 1);
			break;
		case '.':
			mazeresize(xsiz + 1, ysiz + 1);
			break;
		case '<':
			mazeresize(xsiz - 5, ysiz - 5);
			break;
		case '>':
			mazeresize(xsiz + 5, ysiz + 5);
			break;
		case ' ':
			f3D = !f3D;
			repaint();
			break;
		case '8':
			if (fDot)
				movedot(0, -1);
			break;
		case '4':
			if (fDot)
				movedot(-1, 0);
			break;
		case '5':
		case '2':
			if (fDot)
				movedot(0, 1);
			break;
		case '6':
			if (fDot)
				movedot(1, 0);
			break;
		case '\b':
			if (fDot) {
				xloc = xlocorg; yloc = 1; wdir = 2;
				repaint();
			}	
			break;
		}
		return true;
	}

	// Constants

	static final int xmax = 320;
	static final int ymax = 320;

	static final int xwid = 2;
	static final int ywid = 2;

	static final int xsizmax = 79;
	static final int ysizmax = 79;

	static final int xmazmax = xsizmax*2 + 2;
	static final int ymazmax = ysizmax*2 + 2;

	static final int xinc[] = {0, -1, 0, 1};
	static final int yinc[] = {-1, 0, 1, 0};

	static final String szDir[] =
		{"Facing North", "Facing West", "Facing South", "Facing East"};

	// Variables

	int xsiz = 16;
	int ysiz = 16;

	int xmaz = xsiz*2 + 2;
	int ymaz = ysiz*2 + 2;

	int xoff, yoff, xcel, ycel;

	boolean fDot = false, f3D = false;

	int xlocorg, xloc, yloc, wdir;

	boolean[] rgfCell = new boolean[xmazmax * ymazmax];

	// Functions

	private int rnd(int n)
	{
		return (int)(Math.random() * (double)n);
	}

	private void mazeresize(int x, int y)
	{
		if (x < 1 || y < 1 || x > xsizmax || y > ysizmax)
			return;
		xsiz = x; ysiz = y;
		xmaz = xsiz*2 + 2; ymaz = ysiz*2 + 2;
		mazestart();
		repaint();
	}

	private boolean fmap(int x, int y)
	{
		return rgfCell[y*xmaz + x];
	}

	private boolean fget(int x, int y)
	{
		if (x < 0 || y < 0 || x >= xmaz || y >= ymaz)
			return false;
		return rgfCell[y*xmaz + x];
	}

	private void set(int x, int y)
	{
		rgfCell[y*xmaz + x] = false;
	}

	private void movedot(int x, int y)
	{
		int w;

		if (!f3D)
			wdir = x != 0 ? x + 2 : y + 1;
		else {
			if (x != 0)	{
				wdir -= x; wdir &= 3;
				x = 0;
			} else {
				w = y;
				x = -w*xinc[wdir]; y = -w*yinc[wdir];
			}
		}
		if (xloc + x < 1 || xloc + x > xmaz-3 || yloc + y < 1 ||
			yloc + y > ymaz-3 || fmap(xloc + x, yloc + y))
			return;
		xloc += (x*2); yloc += (y*2);
		repaint();
	}

	private void drawinside(Graphics g, int x1, int y1, int x2, int y2, int i)
	{
		int x, y;
		boolean f;

		f = i > 0;
		x = xmax-1; y = ymax-1;

		g.drawLine(f ? x1 : x-x1, y1, f ? x2 : x-x2, y2);
		g.drawLine(f ? x1 : x-x1, y-y1, f ? x2 : x-x2, y-y2);
	}

	// MazeMaker 1.0 - By Walter D. Pullen - Dec 6, 1996

	private void mazestart()
	{
		int x, y, i;
		int xnew = 0, ynew = 0, dirx = 2, diry = 2, count = xsiz*ysiz - 1, d;
		boolean hunt = false;

		xcel = (xmax-xwid) / xsiz;
		ycel = (ymax-xwid) / ysiz;
		xoff = (xmax - xsiz*xcel - xwid) / 2;
		yoff = (ymax - ysiz*ycel - ywid) / 2;

		for (i = 0; i < xmaz*ymaz; i++)
			rgfCell[i] = true;
		for (y = 0; y < ymaz; y++)
			set(xmaz - 1, y);
		for (x = 0; x < xmaz; x++)
			set(x, ymaz - 1);

		xlocorg = rnd(xsiz)*2 + 1; xloc = xlocorg;
		yloc = 1; wdir = 2; fDot = false;

		x = xloc; y = yloc;
		set(x, 0); set(x, y);
		do {
			d = rnd(4);
			if (!fmap(x, y)) {
				for (i = 0; i < 4; i++) {
					xnew = x + xinc[d]*2;
					ynew = y + yinc[d]*2;
					if (xnew > 0 && ynew > 0 && xnew < xmaz-1 && ynew < ymaz-1 &&
						fmap(xnew, ynew))
						break;
					d++; d &= 3;
				}
				hunt = (i >= 4);
			}
			if (!hunt) {
				set(x+xnew >> 1, y+ynew >> 1);
				set(xnew, ynew);
				x = xnew; y = ynew;
				count--;
			} else {
				if (x + dirx >= 0 && x + dirx < xmaz-1)
					x += dirx;
				else {
					dirx = -dirx;
					if (y + diry >= 0 && y + diry < ymaz-1)
						y += diry;
					else
						diry = -diry;
				}
			}
		} while (count > 0);
		set(rnd(xsiz)*2 + 1, ymaz-2);
	}

	private void mazedraw(Graphics g)
	{
		int x, y;
		int xc, yc, xm, ym = 0, xn, yn, xd, yd, xe, ye, xl, yl = 0, xf, yf = 0,
			xx = 0, yy = 0, i, j, k;
		FontMetrics fm;
		String sz;

		g.setColor(Color.white);
		g.fillRect(0, 0, xmax, ymax);
		if (!f3D) {

			// Draw maze overview

			if (fDot) {
				g.setColor(Color.red);
				g.fillOval(xoff + (xloc/2)*xcel + xwid,
					yoff + (yloc/2)*ycel + ywid, xcel - xwid, ycel - ywid);
			}
			g.setColor(Color.black);
			for (y = 0; y <= ysiz; y++) {
				for (x = 0; x <= xsiz; x++) {
					if (fmap(x*2 + 1, y*2))
						g.fillRect(xoff + x*xcel, yoff + y*ycel, xcel + xwid, ywid);
				}
				for (x = 0; x <= xsiz; x++) {
					if (fmap(x*2, y*2 + 1))
						g.fillRect(xoff + x*xcel, yoff + y*ycel, xwid, ycel + ywid);
				}
			}
		} else {

			// Draw maze inside view

			fDot = true;
			g.setColor(Color.black);
			fm = g.getFontMetrics();
			xc = xmax >> 1; yc = ymax >> 1;
			for (i = -1; i <= 1; i += 2) {
				xm = xloc; ym = yloc;
				j = (wdir + i) & 3;
				xd = xm + xinc[j]; yd = ym + yinc[j];
				xl = (xc * 3 / 4) * 5 / 3; yl = (yc * 3 / 4) * 5 / 3;
				xx = yy = 0;
				for (j = 0; j < 20; j++) {
					xn = xm + xinc[wdir]; yn = ym + yinc[wdir];
					xe = xd + xinc[wdir]; ye = yd + yinc[wdir];
					if ((j & 1) == 0) {
						xf = xl * 3 / 5; yf = yl * 3 / 5;
					} else {
						xf = xl * 7 / 8; yf = yl * 7 / 8;
					}
					if (fget(xm, ym) || xl == xf || yl == yf)
						break;
					if (fget(xd, yd)) {
						drawinside(g, xc-xl, yc-yl, xc-xf, yc-yf, i);
						xx = xc-xf;	yy = yc-yf;
					} else if (fget(xe, ye)) {
						drawinside(g, xx, yc-yf, xc-xf, yc-yf, i);
						xx = xc-xf;	yy = yc-yf;
					}
					if (fget(xn, yn)) {
						drawinside(g, xc-xf, yc-yf, xc, yc-yf, i);
						if (fget(xd, yd) || !fget(xe, ye))
							drawinside(g, xc-xf, yc-yf, xc-xf, yc, i);
						if (!fget(xe, ye))
							drawinside(g, xx, yc, xc-xf, yc, i);
					} else if (fget(xd, yd) != fget(xe, ye)) {
						drawinside(g, xc-xf, yc-yf, xc-xf, yc, i);
						xx = xc-xf;	yy = yc-yf;
					}
					xm = xn; ym = yn;
					xn += xinc[wdir]; yn += yinc[wdir];
					xd = xe; yd = ye;
					xe += xinc[wdir]; ye += yinc[wdir];
					xl = xf; yl = yf;
				}
			}
			if (yl == yf && (ym < 0 || ym >= ymaz)) {  
				g.fillRect(xx, yy, xmax-1-xx*2, ymax-1-yy*2);
				sz = ym < 0 ?
					"Looking at Entrance." : "You are at the Exit!";
				i = fm.stringWidth(sz);
				g.drawString(sz, (xmax - i)/2, fm.getHeight() +
					fm.getAscent() - fm.getDescent() - fm.getLeading());
			}
			i = fm.stringWidth(szDir[wdir]);
			g.drawString(szDir[wdir], (xmax - i)/2, ymax - fm.getHeight());
		}
	}
}
