import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.awt.event.*;
import java.applet.*;

public class MineSweeper extends Applet implements MouseListener {
	protected int [][] mines;
	protected int [][] field;
	protected int numCells;
	protected int numMines;
	protected int height;
	protected int width;
	protected int marked;
	protected int opened;
	protected Color bgColor;
	protected Color borderColor;
	protected Color textColor;
	protected Color openColor;
	protected Color hiddenColor;
	protected Image bi;
	protected boolean lost;
	protected boolean won;
	
	public void init() {
		bgColor=getColor("#eaeabb");
		borderColor=getColor("#000000");
		openColor=getColor("#efefcb");
		hiddenColor=getColor("#dadaab");
		textColor=getColor("#000000");
     	height = Integer.parseInt(getParameter("hgt"));
     	width = Integer.parseInt(getParameter("wid"));
     	numMines = Integer.parseInt(getParameter("mines"));
		numCells = height * width;
		mines = new int [height][width];
		field = new int [height][width];
		resize(100+20*width,100+20*height);
		bi=createImage(20*width, 20*(height+1));
		paintBoard(bi.getGraphics());
		setVisible(true);
		addMouseListener(this);
		restart();
	}
	
	protected boolean isMined(int x, int y) {
		if (x<0 || y<0 || x>=width || y>=height)
			return false;
		return (mines[y][x]==10);
	}

	protected boolean isMarked(int x, int y) {
		if (x<0 || y<0 || x>=width || y>=height)
			return false;
		return (field[y][x]==2);
	}

	protected void mark(int x, int y) {
		if (marked==numMines) return;
		if (field[y][x]==0) {
			field[y][x]=2;
			marked++;
			drawCell(bi.getGraphics(),x,y);
		}
	}
	
	protected void unMark(int x, int y) {
		if (field[y][x]==2) {
			field[y][x]=0;
			marked--;
			drawCell(bi.getGraphics(),x,y);
		}		
	}	

	protected void open(int x,int y) {
		if (x<0 || y<0 || x>=width || y>=height || field[y][x]!=0)
			return;		
		field[y][x]=1;
		opened++;
		drawCell(bi.getGraphics(),x,y);
		if (mines[y][x]==0) {
			open(x-1,y-1);
			open(x,y-1);
			open(x+1,y-1);
			open(x+1,y);
			open(x+1,y+1);
			open(x,y+1);
			open(x-1,y+1);
			open(x-1,y);
		}
	}
	
	protected void restart() {
		int i; int j;
		lost=false;
		won=false;
		opened=0;
		for (i=0; i<height; i++) 
			for (j=0; j<width; j++) {
				field[i][j]=0;
				mines[i][j]=0;
			}
		for (i=0; i<numMines; i++) {
			int x; int y;
			do {
				x=(int)(Math.random()*width);
				y=(int)(Math.random()*height);
			} while (isMined(x,y));
			mines[y][x]=10;
		}
		for (i=0; i<height; i++) 
			for (j=0; j<width; j++) 
				if (! isMined(j,i)) {
					int total=0;
					if (isMined(j-1,i-1)) total++;
					if (isMined(j,i-1)) total++;
					if (isMined(j+1,i-1)) total++;
					if (isMined(j+1,i)) total++;
					if (isMined(j+1,i+1)) total++;
					if (isMined(j,i+1)) total++;
					if (isMined(j-1,i+1)) total++;
					if (isMined(j-1,i)) total++;
					mines[i][j]=total;
				}
		marked=0;
	}

	public void paint(Graphics g) {
		g.drawImage(bi,0,0,null);
		g.drawString(""+(numMines-marked),40,15+20*height);
		if (won) {
			g.setColor(openColor);
			g.fillRect(20,20,20*(width-2)-2,38);
			g.setColor(borderColor);
			g.drawRect(20,20,20*(width-2)-2,38);
			g.setColor(textColor);
			g.drawString("You won! Click to play again.",22,30+15);
		}
		if (lost) {
			g.setColor(openColor);
			g.fillRect(20,20,20*(width-2)-2,38);
			g.setColor(borderColor);
			g.drawRect(20,20,20*(width-2)-2,38);
			g.setColor(textColor);
			g.drawString("You lost! Click to play again.",22,30+15);
		}

		super.paint(g);
	}
	
	public void paintBoard(Graphics g) {
		int i; int j;
		g.setColor(bgColor);
		g.fillRect(0,0,width*20,(height+1)*20);
		for (i=0; i<height; i++) 
			for (j=0; j<width; j++) 
				drawCell(g,j,i);		
		g.setColor(borderColor);
		g.drawRect(0,height*20,20*width-2,18);
		g.drawLine(20*(width-4),height*20,20*(width-4),(height+1)*20-2);
		g.setColor(textColor);
		g.drawString("Mines:",2,15+20*height);
		g.drawString("Restart",20*(width-3),15+20*height);
	}
	
	protected void drawCell(Graphics g, int x, int y) {
		int rx=20*x;
		int ry=20*y;
		if (field[y][x]!=1) {
			g.setColor(hiddenColor);
			g.fillRect(rx,ry,18,18);
			g.setColor(textColor);
			if (field[y][x]==2) {
				g.drawString(" P",rx+1,ry+15);
			}
		} else {
			g.setColor(openColor);
			g.fillRect(rx,ry,18,18);
			g.setColor(textColor);
			if (isMined(x,y)) {
				g.drawString(" o",rx+1,ry+15);
			} else if (mines[y][x]>0) {
				g.drawString(" "+mines[y][x],rx+1,ry+15);
			}	
		}
		g.setColor(borderColor);
		g.drawRect(x*20,y*20,18,18);
	}

	public static Color getColor(Object v) {
        if (v instanceof Color) return (Color)v;
        if (!(v instanceof String)) return Color.black;
        String value=(String)v;
        Color c=null;
        if (value==null || value.length()<1) return null;
        try {
            if (value.charAt(0) == '#')
                c = new Color(Integer.decode("0x"+value.substring(1)).intValue());
            if (c == null) c = Color.white;
            return c;
        }
        catch (Exception e){ return null; }
    }
    
    public void mouseClicked(MouseEvent e) {
		if (won || lost) {
			restart();
			paintBoard(bi.getGraphics());
			repaint();
			return;
		}
 		int x = e.getX()/20;
 		int y = e.getY()/20;	 			
		if ((e.getModifiers() & InputEvent.BUTTON1_MASK) !=0) {			
			if (y==height && x>width-4 && x<width) {
				restart();
				paintBoard(bi.getGraphics());
				repaint();
				return;				
			}
	 		open(x,y);
	 		if (isMined(x,y)) {
	 			lost=true;
	 		} else if (opened==numCells-numMines) {
	 			won=true;
	 		}
	 	}
	 	else if ((e.getModifiers() & InputEvent.BUTTON3_MASK) !=0) {
	 		if (isMarked(x,y)) 
	 			unMark(x,y);
	 		else
	 			mark(x,y);	 		
	 	}
		repaint();
    }
    
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void mousePressed(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    
}
