import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class MazeApplet extends Applet implements MouseListener,
    MouseMotionListener, ActionListener
{
    private static final int GEN=0, SOLVE=1;
    private Maze maze;
    private int mwidth=10, mheight=10;
    private MazeThread [] threads = new MazeThread [2];
    private ThreadGroup appTG;
    private Button cmdRedraw;
    private TextField txtWidth, txtHeight;

    public void init ()
    {
	String param;
	appTG = Thread.currentThread ().getThreadGroup ();
	setLayout (null);

	if ((param=getParameter("MAZEWIDTH")) != null)
	    try {
		mwidth = Integer.parseInt (param);
	    } catch (NumberFormatException e) {
	    }
	if ((param=getParameter("MAZEHEIGHT")) != null)
	    try {
		mheight = Integer.parseInt (param);
	    } catch (NumberFormatException e) {
	    }

	maze = new WallMaze ();
	BorderLayout bl = new BorderLayout ();
	setLayout (bl);
	add ("Center", maze);

	Panel controlPanel = new Panel ();
	controlPanel.setLayout (new BorderLayout ());
	add ("South", controlPanel);
	Panel gp = new Panel ();
	gp.setLayout (new GridLayout (2, 2));
	controlPanel.add ("Center", gp);
	cmdRedraw = new Button ("Redraw Maze");
	txtWidth = new TextField ("" + mwidth);
	txtHeight = new TextField ("" + mheight);
	controlPanel.add ("East", cmdRedraw);
	gp.add (new Label ("Width:"));
	gp.add (txtWidth);
	gp.add (new Label ("Height"));
	gp.add (txtHeight);
	maze.addMouseListener (this);
	maze.addMouseMotionListener (this);
	cmdRedraw.addActionListener (this);
    }

    public void start ()
    {
	setMaze ();
    }

    public void setBounds (int x, int y, int w, int h)
    {
	super.setBounds (x, y, w, h);
	setMaze();
    }

    private void setMaze ()
    {
	if (maze != null) {
	    maze.setDimensions (mwidth, mheight);
	    maze.setSize (getSize ());
	    //if (linewidth > 0)
	    //   maze.setLineSizes(linewidth, linewidth);
	    maze.resetMaze ();
	    if (threads [GEN] == null)
	        threads [GEN] = new MazeThread(appTG, maze, false, false);
	}
    }

    boolean isMouseDown = false;
	//////////////////////////////////////////
	//	MouseListener actions
	//////////////////////////////////////////
    int dragging = -1;

    public void mouseEntered (MouseEvent e)
    {
    }
    public void mouseExited (MouseEvent e)
    {
    }
    public void mousePressed (MouseEvent evt)
    {
	boolean solve = !evt.isShiftDown() && !maze.won ();
	boolean show = !evt.isControlDown();
	int index = solve ? SOLVE : GEN;
	if (threads [SOLVE] != null)
	    stopThread(SOLVE);  // always stop SOLVE thread
	if (threads [index] != null)
	    stopThread(index);  // stop thread that will be restarted
	if (solve) {
	    maze.setDest (evt.getX (), evt.getY ());
	    maze.pathToSquare (evt.getX (), evt.getY ());
	} else {
	    //	    threads[index] = new MazeThread(appTG, maze, false, false);
	}
	//maze.flipSquare (evt.getX (), evt.getY ();
    }
    public void mouseReleased (MouseEvent e)
    {
	isMouseDown = false;
    }
    public void mouseClicked (MouseEvent e)
    {
    }

	//////////////////////////////////////////
	//	MouseMotionListener actions
	//////////////////////////////////////////
    public void mouseMoved (MouseEvent evt)
    {
    }
    public void mouseDragged (MouseEvent evt)
    {
	if (!evt.isShiftDown () && !maze.won ())
	    mousePressed (evt);
    }

    void stopThread (int which)
    {
	threads [which].stopThread ();
    }

	//////////////////////////////////////////
	//	ActionListener actions
	//////////////////////////////////////////
    public void actionPerformed (ActionEvent e)
    {
	if (e.getSource () == cmdRedraw) {
	    try {
		mwidth = Integer.valueOf (txtWidth.getText ()).intValue ();
	    } catch (NumberFormatException ex) {
		mwidth = 3;
	    }
	    try {
		mheight = Integer.valueOf (txtHeight.getText ()).intValue ();
	    } catch (NumberFormatException ex) {
		mheight = 3;
	    }
	    if (mwidth < 3)
		mwidth = 3;
	    if (mwidth > 75)
		mwidth = 75;
	    if (mheight < 3)
		mheight = 3;
	    if (mheight > 50)
		mheight = 50;
	    txtWidth.setText("" + mwidth);
	    txtHeight.setText("" + mheight);
	    //redraw the maze
	    maze.setDimensions (mwidth, mheight);
	    if (threads [SOLVE] != null)
		stopThread(SOLVE);  // always stop SOLVE thread
	    if (threads [GEN] != null)
		stopThread(GEN);  // always stop SOLVE thread
	    threads[GEN] = new MazeThread(appTG, maze, false, false);
	    //threads[GEN] = new MazeThread(appTG, maze, true, false);
	} else {
	}
    }
}

class MazeThread extends Thread
{
    private Maze    maze;
    private boolean show, solve;
    
    MazeThread (ThreadGroup tg, Maze maze, boolean show, boolean solve)
    {
	super(tg, solve ? "Solve thread" : "Generate thread");
	this.maze = maze;
	this.show = show;
	this.solve = solve;
	start();
    }

    public void run () {
	if (show)
	    setPriority(Thread.MIN_PRIORITY + 1);

	if (solve)
	    maze.traverse(show);
	else
	    maze.generate(show);
    }

    public void stopThread ()
    {
	if (solve) {
	} else {
	    maze.stopGenerating ();
	}
    }
}

