import java.applet.Applet;    
import java.awt.event.*;      
import java.applet.AudioClip; 
import java.awt.*;            
import java.util.*;

/**
 * アプレットを実行するクラス。
*/
public class MineSweeper3 extends Applet implements Runnable,MouseListener,KeyListener,ActionListener,ItemListener{
	/** x座標格納用の変数 */
	private int px;                      
	/** y座標格納用の変数 */
	private int py;						 
	/** チェックモードを変更したかの判定変数 */
	private boolean henkan;              
	/** チェックモード時にマスをクリックしたかの判定変数 */
	private boolean henkan2 = false;	 
	/** オープンモードを変更したかの判定変数 */
	private boolean henkan3;			 
	/** オープンモード時にマスをクリックしたかの判定変数 */
	private boolean henkan4 = false;	 
	/** キーボードに押された値 */
	private int presskey;				 
	/** ゲームの判定を行うクラスの変数 */
	private Judge judge;				 
	/** ゲームの盤面を表すクラスの変数 */
	private Banmen banmen;				 
	/** プレイヤークラスの変数 */
	private Player player;				 
	/** 盤面のマスのクラスの配列 */
	private Masu[][] masu;				 
	/** アプレット上の時計を動かすためのクラス */
	private TimeCheck timec;			 
	/** アプレット画面の盤面と画面上下端の間の余白 */
	private final int tw = 40;			 
	/** アプレット画面の盤面と画面左右端の間の余白 */
	private final int yw = 65;			 
	/** １行１列にあるマスの数 */
	private int Count = 15;		
	/** １マスの横幅 */
	private int masuWidth = 16;			 
	/** １マスの縦幅 */
	private int masuHeight = 16;		 
	/** マスとマスの間の余白 */
	private int margin = 2;				 
	/** 幅と高さの大きさをカプセル化するクラスの変数。 */
	Dimension d;                 		 
	/** オフスクリーン用のイメージ変数 */
	Image offscreen;             	     
	/** オフスクリーン用のGraphics変数 */
	Graphics grf;                		 
	/** 各マスに表示する画像を格納するイメージ配列 */
	Image img[];						 
	/** アプレットに表示する数字画像を格納するイメージ配列 */
	Image simg[];						 
	/** アプレットに表示する時計の画像を格納するイメージ変数 */
	Image timer;						 
	/** アプレットに表示する爆弾の画像を格納するイメージ変数 */
	Image bomb;							 
	/** 複数のメディア オブジェクトのステータスをトラッキングするユーティリティ クラスの変数 */ 
	MediaTracker mt;					 
	/** 爆弾に当たった時に鳴らすサウンド変数 */ 
	AudioClip bom;
	/** マスをオープンしたときに鳴らすサウンド変数 */
	AudioClip open;
	/** マスにフラグを立てたときに鳴らすサウンド変数 */
	AudioClip check;
	/** アプレット画面に設定する背景色 */
	Color bgcolor;						 
	/** タイムをカウントするために使用するスレッド変数 */
	Thread thread2 = null;				 
	/** メディア（画像・音楽）をMediaTrackerにより表示するために使用するスレッド変数 */
	Thread thread = null;
	/** 画面下端にレベル情報を表示させるためのpanel変数	*/			 
	Panel panel;
	/** チェックボックスをひとまとめにするためのチェックボックスグループ変数 */
	CheckboxGroup group1;
	/** レベルを選択するために使用するチェックボックス変数 */
	Checkbox cb1,cb2,cb3,cb4,cb5;
	/** 最初の１回だけパネルを設置させるようにするための変数 */
	boolean first = true;
	/** レベルを選択するまでゲームを実行させないようにするための変数 */
	boolean move;
	/** STARTボタンを設定するためのボタン変数 */
	Button button;
	/** "レベル" ラベルを表示するためのラベル変数 */
	Label label;
	/** "マス数" ラベルを表示するためのラベル変数 */
	Label label2;
	/** マス数を選択するために使用するリストボックス変数 */
	Choice choice;
	/** アプレット下端のパネル上で変化するレベルの値を格納する変数 */
	int lv = 2;
	/** レベルを格納するための変数(ゲームレベル＝lvの値 - 1） */
	private int level;
	/**
 	 * アプレット実行の準備をする。
	*/
	public void init(){
		if(first){
			panelControl();
			first = false;
		}
		d = getSize();														
		offscreen = createImage(d.width,d.height);							
		grf = offscreen.getGraphics();										
		player = new Player("Yamada",level);									
		banmen = new Banmen(Count,this);											
		masu = new Masu[Count][Count];										
		timec = new TimeCheck(this,banmen);									
		for(int k = 0;k < 2;k++){
			for(int i = 0;i < Count;i++){									
				for(int j = 0;j < Count;j++){
					if(k == 0){
						masu[i][j] = new Masu(player);							
					}else{
						banmen.bombCnt(masu,i,j);								
					}
				}
			}
		}
		judge = new Judge(banmen);											
		mt = new MediaTracker(this);										
		bgcolor = Color.orange;												
		img = new Image[13];												
		for(int k = 0;k < 13;k++){
			if(k < 10){
				img[k] = getImage(getCodeBase(),"masu_0" + k + ".gif");			
			}else{
				img[k] = getImage(getCodeBase(),"masu_" + k + ".gif");			
			}
			mt.addImage(img[k],0);												
			
		}
		simg = new Image[10];												
		for(int k = 0;k < 10;k++){
			simg[k] = getImage(getCodeBase(),"segu0" + k + ".gif");				
			mt.addImage(simg[k],0);												
		}
		timer = getImage(getCodeBase(),"watch.gif");							
		mt.addImage(timer,0);													
		bomb = getImage(getCodeBase(),"bomb.gif");								
		mt.addImage(bomb,0);													
		bom = getAudioClip(getDocumentBase(),"bomb.wav");
		open = getAudioClip(getDocumentBase(),"open.wav");
		check = getAudioClip(getDocumentBase(),"check.wav");
		addKeyListener(this);													
		requestFocus();															
		addMouseListener(this);													
		
	}
	/**
 	 * アプレット画面の描画をupdate(Graphics g)に任せる。
 	 * 
 	 * @param g 描画するグラフィックエリア
	*/
	public void paint(Graphics g){
		update(g);
	}
	/**
 	 * アプレット画面の描画をするメソッド。
 	 * 
 	 * @param g 描画するグラフィックエリア
	*/
	public void update(Graphics g){
		if(!mt.checkID(0)){
			g.clearRect(0,0,d.width,d.height);
			g.setColor(Color.black);
			g.drawString("Please wait....",d.width / 4,d.height / 2);
			return;
		}
		if(henkan){
			if(banmen.getCheckmode()){
				banmen.setCheckmode(false);
			}else{
				banmen.setCheckmode(true);
				banmen.setOpenmode(false);
			}
			henkan = false;
		}
		if(henkan2){
			banmen.EnterBomb(masu,py,px);
			henkan2 = false;
		}
		if(henkan3){
			if(banmen.getOpenmode()){
				banmen.setOpenmode(false);
			}else{
				banmen.setOpenmode(true);
				banmen.setCheckmode(false);
			}
			henkan3 = false;
		}
		if(henkan4){
			banmen.openMethod(masu,py,px);
			henkan4 = false;
			judge.EndFlg(masu,Count);
			repaint();
		}
		grf.setColor(bgcolor);
		grf.fillRect(0,0,d.width,d.height);
		grf.setColor(Color.black);
		grf.setFont(new Font("TimesRoman",Font.PLAIN,18));
		grf.drawString("マインスィーパ",130,25);
		grf.drawImage(timer,25,5,this);
		int time = timec.getTime();
		for(int i = 0;i < 3;i++){
			int j = time % 10;
			grf.drawImage(simg[j],80 - 15 * i,5,this);
			time = time / 10;
		}
		grf.drawImage(bomb,296,5,this);
		int bc = banmen.AllBombCnt(masu,Count);
		for(int i = 0;i < 3;i++){
			int j = bc % 10;
			grf.drawImage(simg[j],351 - 15 * i,5,this);
			bc = bc / 10;
		}
		grf.setFont(new Font("TimesRoman",Font.PLAIN,14));
		if(banmen.getCheckmode()){
			grf.drawString("チェックモード",300,340);
		}else{
			if(banmen.getOpenmode()){
				grf.drawString("オープンモード",300,340);
			}else{
				grf.drawString("通常モード",300,340);
			}
		}
		for(int i = 0;i < Count;i++){
			int ii = (i + (15 - Count) / 2) * (masuHeight + margin);
			for(int j = 0;j < Count;j++){
				int jj = (j + (15 - Count) / 2) * (masuWidth + margin);
				if(masu[i][j].getOpenCheck()){
					if(masu[i][j].getBombCheck()){
						grf.drawImage(img[10],jj + yw,ii + tw,this);
					}else{
						grf.drawImage(img[masu[i][j].getAroundBombCount()],jj + yw,ii + tw,this);
					}
				}else{
					if(masu[i][j].getCheckBombCheck()){
						if(!banmen.getGameOver()){
							grf.drawImage(img[11],jj + yw,ii + tw,this);
						}else{
							grf.drawImage(img[12],jj + yw,ii + tw,this);
						}
					}else{
						grf.drawImage(img[9],jj + yw,ii + tw,this);
					}
				}
			}
		}
		grf.setFont(new Font("TimesRoman",Font.PLAIN,18));
		if(banmen.getGameClear()){
			grf.setColor(Color.red);
			grf.drawString("GAME CLEAR",d.width / 5 * 2,d.height / 2);
		}else{
			if(banmen.getGameOver()){
				grf.setColor(Color.red);
				grf.drawString("GAME OVER",d.width / 5 * 2,d.height / 2);
			}
		}
		g.drawImage(offscreen,0,0,this);
	}
	/**
 	 * マウスボタンを押した時の動作。
 	 * 
 	 * @param e マウスイベントのインスタンス
	*/
	public void mousePressed(MouseEvent e){
		if(move){
			int ix = e.getX();
			int iy = e.getY();
			if(!banmen.getGameOver() && !banmen.getGameClear()){
				int x = (ix - yw) / (masuWidth + margin) - ((15 - Count) / 2);
				int y = (iy - tw) / (masuHeight + margin) - ((15 - Count) / 2);
				if(x >= 0 && x < Count && y >= 0 && y < Count){
					if(!banmen.getCheckmode()){
						if(!banmen.getOpenmode()){
							banmen.Click_SyoriMethod(masu,y,x);
							judge.EndFlg(masu,Count);
						}else{
							if(masu[y][x].getOpenCheck()){
								if(banmen.aroundCheckcount(masu,y,x) == masu[y][x].getAroundBombCount()){
									henkan4 = true;
									py = y;
									px = x;
								}
							}
						}
					}else{
						if(banmen.AllBombCnt(masu,Count) > 0){
							henkan2 = banmen.EnterBombCheck(masu,y,x);
							py = y;
							px = x;
						}
					}
					repaint();
				}
			}
		}
	}
	/**
 	 * マウスボタンを離した時の動作。
 	 * 
 	 * @param e マウスイベントのインスタンス
	*/
	public void mouseReleased(MouseEvent e){                                                                                                              
	}
	/**
 	 * マウスカーソルがアプレット画面の外に出た時の動作。
 	 * 
 	 * @param e マウスイベントのインスタンス
	*/
	public void mouseExited(MouseEvent e){
		timec.setZobun(0);                                                                                                                  //    //
	}
	/**
 	 * マウスカーソルがアプレット画面の中に入ってきたの動作。
 	 * 
 	 * @param e マウスイベントのインスタンス
	*/
	public void mouseEntered(MouseEvent e){
		timec.setZobun(1);                                                                                                                 //    //
	}
	/**
 	 * マウスボタンをクリックした時の動作。
 	 * 
 	 * @param e マウスイベントのインスタンス
	*/
	public void mouseClicked(MouseEvent e){                                                                                                                 //    //
	}
	/**
 	 * キーボードのキーがタイプされた時の動作。
 	 * 
 	 * @param e キーイベントのインスタンス
	*/
	public void keyTyped(KeyEvent e){
	}
	/**
 	 * キーボードのキーを離した時の動作。
 	 * 
 	 * @param e キーイベントのインスタンス
	*/
	public void keyReleased(KeyEvent e){
	}
	/**
 	 * キーボードのキーを押した時の動作。
 	 * 
 	 * @param e キーイベントのインスタンス
	*/
	public void keyPressed(KeyEvent e){
		if(move){
			presskey = e.getKeyCode();
			if(presskey == 83){
				henkan = true;
			}
			if(presskey == 87){
				henkan3 = true;
			}
			repaint();
		}
	}
	/**
 	 * 画像の読み込みを行うスレッドを実行させるための処理。
	*/
	public void start(){
		if(thread == null){
			thread = new Thread(this);
			thread.start();
		}
	}
	/**
 	 * 実行中のスレッドを停止させるための処理。
	*/
	public void stop(){
		if(thread != null){
			thread.stop();
			thread = null;
		}
		if(bom != null){
			bom.stop();
		}
		if(open != null){
			open.stop();
		}
		if(check != null){
			check.stop();
		}
	}
	/**
 	 * 画像の読み込みを行うための処理。
	*/
	public void run(){
		try{
			mt.waitForID(0);
		}catch(InterruptedException e){
			return;
		}
		move = false;
		repaint();
	}
	/**
 	 * 時計を動かすスレッドを実行させるための処理。
	*/
	public void timeThreadstart(){
		thread2 = new Thread(timec);
		thread2.start();
	}
	/**
 	 * STARTボタンをクリックを押した時の動作(ゲームを開始する。）。
 	 * 
 	 * @param e アクションイベントのインスタンス
	*/
	public void actionPerformed(ActionEvent e){
		level = lv;
		Count = Integer.parseInt(choice.getSelectedItem());
		move = true;
		repaint();
		init();
		timeThreadstart();
	}
	/**
 	 * ラジオボタンのチェックを変えた時の動作（レベルの変更を行う）。
 	 * 
 	 * @param e アイテムイベントのインスタンス
	*/
	public void itemStateChanged(ItemEvent e){
		if(cb1.getState()){
			lv = 2;
		}else{
			if(cb2.getState()){
				lv = 3;
			}else{
				if(cb3.getState()){
					lv = 4;
				}else{
					if(cb4.getState()){
						lv = 5;
					}else{
						lv = 6;
					}
				}
			}
		}
		repaint();
	}
	/**
 	 * 音を鳴らすための処理。
 	 * 
 	 * @param number ならす音楽の番号
	*/
	public void musicPlay(int number){
		if(number == 0){
			open.play();
		}else{
			if(number == 1){
				bom.play();
			}else{
				check.play();
			}
		}
	}
	/**
 	 * 下のほうのパネル（ラジオボタン・スタートボタン）の設定処理。
 	*/
	public void panelControl(){
		setLayout(new BorderLayout());
		panel = new Panel();
		panel.setBackground(Color.white);
		label = new Label("レベル");
		label.setFont(new Font("SanSerif",Font.PLAIN,12));
		panel.add(label);
		group1 = new CheckboxGroup();
		cb1 = new Checkbox("1",true,group1);
		cb2 = new Checkbox("2",false,group1);
		cb3 = new Checkbox("3",false,group1);
		cb4 = new Checkbox("4",false,group1);
		cb5 = new Checkbox("5",false,group1);
		cb1.setFont(new Font("SanSerif",Font.PLAIN,10));
		cb2.setFont(new Font("SanSerif",Font.PLAIN,10));
		cb3.setFont(new Font("SanSerif",Font.PLAIN,10));
		cb4.setFont(new Font("SanSerif",Font.PLAIN,10));
		cb5.setFont(new Font("SanSerif",Font.PLAIN,10));
		cb1.addItemListener(this);
		cb2.addItemListener(this);
		cb3.addItemListener(this);
		cb4.addItemListener(this);
		cb5.addItemListener(this);		
		panel.add(cb1);
		panel.add(cb2);
		panel.add(cb3);
		panel.add(cb4);
		panel.add(cb5);
		label2 = new Label("マス数");
		label2.setFont(new Font("SanSerif",Font.PLAIN,12));
		panel.add(label2);
		choice = new Choice();
		choice.add("7");
		choice.add("9");
		choice.add("11");
		choice.add("13");
		choice.add("15");
		panel.add(choice);
		button = new Button("START");
		button.setFont(new Font("SanSerif",Font.PLAIN,12));
		button.addActionListener(this);
		panel.add(button);
		this.add("South",panel);
	}
}
		