/*
 * Maze
 * Copyright (C) 2000  Paul Davis, pdavis@lpccomp.bc.ca
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

import java.util.*;
import java.awt.*;

/**
 * Draw a 2D maze in an AWT Component.
 * There is no user input.
 */
public class Maze2D extends Component implements MazeListener {

	/**
	 * The maze model.
	 * @see Maze
	 */
	private MazeModel model;

	/**
	 * The width of each displayed cell of the maze.
	 */
	private int cellWidth;

	/**
	 * The size of the maze displayed.
	 */
	private Coordinate3D siz;

	/**
	 * The current Z co-ordinate being displayed.
	 */
	private int z;

	/**
	 * The number of cells to display in the X and Y dimensions.
	 * They will be no larger than the dimensions of the maze.
	 */
	private int dispWidth,dispHeight;

	/**
	 * Create a 2D maze display with a cell size of 20 pixels.
	 * @param model The MazeModel used as the source of the maze.
	 */
	public Maze2D(MazeModel model) {
		this(model,20);
	}

	/**
	 * Create a 2D maze display.
	 * @param model The MazeModel used as the source of the maze.
	 * @param cellSize The cell size in pixels.
	 */
	public Maze2D(MazeModel model, int cellSize) {
		this(model, cellSize, model.getSize().x, model.getSize().y);
	}

	/**
	 * Create a 2D maze display.
	 * @param model The MazeModel used as the source of the maze.
	 * @param cellSize The cell size in pixels.
	 * @param dispWidth The number of horizontal cells to display at a time.
	 * @param dispHeight The number of vertical cells to display at a time.
	 */
	public Maze2D(MazeModel model, int cellSize, int dispWidth, int dispHeight) {
		this.model = model;

		if ( dispWidth >= model.getSize().x )
			dispWidth = model.getSize().x;
		if ( dispHeight >= model.getSize().y )
			dispHeight = model.getSize().y;

		this.dispWidth = dispWidth;
		this.dispHeight = dispHeight;
		siz = model.getSize();
		//System.out.println("model size = " + String.valueOf(siz.x) +
		//	"," + String.valueOf(siz.y));
		cellWidth = cellSize;
		z = 0;
		model.addMazeListener(this);
	}

	/**
	 * From Component.getPreferredSize.
	 */
	public Dimension getPreferredSize() {
		//Dimension d = new Dimension(siz.x * cellWidth, siz.y * cellWidth);
		//System.out.println("getPreferredSize: " + d.toString());
		return new Dimension(dispWidth * cellWidth+1, dispHeight * cellWidth+1);
	}

	/**
	 * From interface MazeListener.
	 * The maze has changed, just repaint.
	 */
	public void mazeChanged(EventObject e) {
		repaint();
	}

	/**
	 * Set the Z co-ordinate of the maze being displayed.
	 * Repaints the display.
	 */
	public void setZ(int z) {
		this.z = z;
		repaint();
	}

	/**
	 * Get the Z co-ordinate of the maze being displayed.
	 */
	public int getZ() {
		return z;
	}

	/**
	 * Paint the maze.  From Component.
	 */
	public void paint(Graphics g) {
		Dimension s = getSize();
		//System.out.println("Maze2D: " + s.toString());
		int x,y;
		int b = cellWidth/3;
		g.setColor(Color.white);
		g.fillRect(0,0,s.width,s.height);

		Coordinate3D current = model.getCurrent();
		if ( current != null )
			z = current.z;

		// Figure out the X and Y offsets for when the displayed
		// size is less than the maze size.
		int xoff = 0;
		if ( current != null ) {
			xoff = current.x - dispWidth/2;
			if ( xoff < 0 )
				xoff = 0;
			if ( xoff + dispWidth > siz.x )
				xoff = siz.x - dispWidth;
		}
		int yoff = 0;
		if ( current != null ) {
			yoff = current.y - dispHeight/2;
			if ( yoff < 0 )
				yoff = 0;
			if ( yoff + dispHeight > siz.y )
				yoff = siz.y - dispHeight;
		}

		// Draw the outside edge.
		g.setColor(Color.black);
		for ( y=0 ; y<=dispHeight ; y+=dispHeight )
			g.drawLine(0,y*cellWidth,s.width,y*cellWidth);
		for ( x=0 ; x<=dispWidth ; x+=dispWidth )
			g.drawLine(x*cellWidth,0,x*cellWidth,s.height);

		byte value;
		for ( y=0 ; y<dispHeight ; y++ ) {
			for ( x=0 ; x<dispWidth ; x++ ) {
				value = model.grid(x+xoff,y+yoff,z);
				// Wall to the left
				if ( (value & MazeModel.XMI) == 0 )
					g.drawLine(x*cellWidth,y*cellWidth,
						x*cellWidth,(y+1)*cellWidth);
				// Wall above
				if ( (value & MazeModel.YMI) == 0 )
					g.drawLine(x*cellWidth,y*cellWidth,
						(x+1)*cellWidth,y*cellWidth);
				// Pathway up - draw an up arrow.
				if ( (value & MazeModel.ZPL) != 0 ) {
					int topx = x*cellWidth+cellWidth/2;
					int topy = y*cellWidth+3;
					g.drawLine(topx,topy,
						topx,topy+cellWidth-6);
					g.drawLine(topx-2,topy+2,topx,topy);
					g.drawLine(topx+2,topy+2,topx,topy);
				}
				// Pathway Down - draw an down arrow.
				if ( (value & MazeModel.ZMI) != 0 ) {
					int botx = x*cellWidth+cellWidth/2;
					int boty = (y+1)*cellWidth-3;
					g.drawLine(botx,boty,
						botx,boty-cellWidth+6);
					g.drawLine(botx-2,boty-2,botx,boty);
					g.drawLine(botx+2,boty-2,botx,boty);
				}
				/*
				if ( (value & MazeModel.ZPL) != 0 )
					System.out.print('u');
				else if ( (value & MazeModel.MARK) != 0 )
					System.out.print('.');
				else
					System.out.print(' ');
				if ( (value & MazeModel.ZMI) != 0 )
					System.out.print('d');
				else if ( (value & MazeModel.MARK) != 0 )
					System.out.print('.');
				else
					System.out.print(' ');
				*/
				if ( model.isMarked(new Coordinate3D(x+xoff,y+yoff,z)) ) {
					g.fillRect(x*cellWidth+b,y*cellWidth+b,b,b);
				}
			}
		}

		// Draw a red block in the finish cell.
		Coordinate3D finish = model.getFinish();
		if ( finish != null && finish.z == z ) {
			g.setColor(Color.red);
			g.fillRect((finish.x-xoff)*cellWidth+b,(finish.y-yoff)*cellWidth+b,
				b,b);
		}

		// Draw a green block in the current play position cell.
		if ( current != null && current.z == z ) {
			g.setColor(Color.green);
			g.fillRect((current.x-xoff)*cellWidth+b,(current.y-yoff)*cellWidth+b,
				b,b);
		}
	}
}