import java.applet.Applet;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import java.lang.*;
import java.text.*;
import java.net.*;

public class Cube45Applet extends Applet implements Runnable, MouseListener, MouseMotionListener
{

	// 定数
	final static int NONE = -1;
	final static int SIZE_CUBE = 32;

	// モード
	final static int MODE_TITLE		= 0;
	final static int MODE_GAMEON	= 1;
	final static int MODE_CLEAR		= 2;
	private int mode = MODE_TITLE;

	// ゲームモード
	final static int GAMEMODE_FLOWER	= 0;
	final static int GAMEMODE_CROSS 	= 1;
	final static int GAMEMODE_FIVE		= 2;
	private int gamemode = GAMEMODE_FLOWER;

	// 位置
	private int mx	= 0;					// 座標
	private int my	= 0;
	private int lockonmx = NONE;
	private int lockonmy = NONE;
	private int[][] basepoint = {{SIZE_CUBE * 3,0}, {0, SIZE_CUBE*3}, {SIZE_CUBE*3,SIZE_CUBE*3}, 
								{SIZE_CUBE*6,SIZE_CUBE*3}, {SIZE_CUBE*3,SIZE_CUBE*6}};
	private CubeObject[][] cubetbl = new CubeObject[5][9];

	// マウス
	private boolean mouseonflg = false;
	private boolean mouseenterflg = true;
			
	// 得点
	private int[]		hightime = new int[3];
	private long		starttime = NONE;
	private int			nowtime  = NONE;

	// 画面描写用
	Dimension dm	= null;
	Image offs		= null;
	Graphics grf	= null;

	// スレッド
	private Thread kicker = null;
	private boolean loop = false;
	final static int threadspeed = 100;

	public void init() {
		
		// 変数の初期化
		for(int i=0;i<hightime.length;i++)
			hightime[i] = 9999;

		// 背景色設定、サイズ設定
		setBackground(Color.black);
		setSize(SIZE_CUBE*9, SIZE_CUBE*9);

		// マウスイベント追加
		addMouseListener(this);
		addMouseMotionListener(this);
		requestFocus();

		// タイトルモード設定
		mode = MODE_TITLE;

		// スレッドスタート
		start();
				
	}

	public void destroy(){
		// スレッドエンド
		stop();
	}

	////////////////////////////////////////////////////////////////
	// 画面描画
	////////////////////////////////////////////////////////////////
	public void paint(Graphics g) {
		update(g);
	}

	public void update(Graphics g) {
		
		// オフスクリーン作成
		if(grf==null){
			dm = getSize(); // 表示領域の取得
			offs = createImage( dm.width, dm.height);
			grf  = offs.getGraphics();
		}

		// 画面を塗りつぶす
		grf.setColor(Color.black);
		grf.fillRect(0,0,dm.width,dm.height);
		
		if(mode==MODE_TITLE){
			int x = NONE;
			int y = NONE;
			grf.setColor(Color.red);
			grf.fillRect(SIZE_CUBE*3+1,             1,SIZE_CUBE*3-2,SIZE_CUBE*3-2);
			grf.fillRect(            1, SIZE_CUBE*3+1,SIZE_CUBE*3-2,SIZE_CUBE*3-2);
			grf.fillRect(SIZE_CUBE*3+1, SIZE_CUBE*3+1,SIZE_CUBE*3-2,SIZE_CUBE*3-2);
			grf.fillRect(SIZE_CUBE*6+1, SIZE_CUBE*3+1,SIZE_CUBE*3-2,SIZE_CUBE*3-2);
			grf.fillRect(SIZE_CUBE*3+1, SIZE_CUBE*6+1,SIZE_CUBE*3-2,SIZE_CUBE*3-2);

			grf.setColor(Color.white);
			grf.setFont(new Font("Monospaced", Font.BOLD, 16));
			grf.drawString("CUBE45", 96+24, 48);
			grf.drawString("FLOWER",  0+24, 96 + 48);
			grf.drawString(String.valueOf(hightime[GAMEMODE_FLOWER]), 0+24, 96 + 72);
			grf.drawString("CROSS",  96+24, 96 + 48);
			grf.drawString(String.valueOf(hightime[GAMEMODE_CROSS]), 96+24, 96 + 72);
			grf.drawString("FIVE",  192+24, 96 + 48);
			grf.drawString(String.valueOf(hightime[GAMEMODE_FIVE]), 192+24, 96 + 72);

			// カーソルの表示
			if((mx>=0)&&(mx<9)&&(my>=0)&&(my<9)){
				grf.setColor(Color.white);
				x = mx / 3;
				y = my / 3;
				grf.drawRect(x*3*SIZE_CUBE,y*3*SIZE_CUBE,SIZE_CUBE*3,SIZE_CUBE*3);
			}

		}
		else {
			// CUBEの描写
			for(int i=0;i<basepoint.length;i++){
				int cnt = 0;
				for(int wky=0;wky<3;wky++){
					for( int wkx=0;wkx<3;wkx++){
						grf.setColor(cubetbl[i][cnt++].getColor());
						grf.fillRect(basepoint[i][0]+wkx*SIZE_CUBE+1,basepoint[i][1]+wky*SIZE_CUBE*+1,
									SIZE_CUBE-2,SIZE_CUBE-2);
					}
				}
			}
		}
		if(mode==MODE_CLEAR){
			grf.setColor(Color.white);
			grf.drawString("CLEAR!!", 24, 48);
			if(hightime[gamemode]>=nowtime)
				grf.drawString("New Record", 3, 48+24);
			
		}

		if((mode==MODE_GAMEON)||(mode==MODE_CLEAR)){
			grf.setColor(Color.white);
			grf.drawString(String.valueOf(nowtime), 192+32, 48);			
		}

		if(mode==MODE_GAMEON){
			// カーソルの表示
			if((mx>=0)&&(mx<9)&&(my>=0)&&(my<9)){
				if(mouseonflg)
					grf.setColor(Color.yellow);
				else
					grf.setColor(Color.white);
				grf.drawRect(mx*SIZE_CUBE,my*SIZE_CUBE,SIZE_CUBE,SIZE_CUBE);
			}
		}
		

		// オフスクリーンのイメージを一挙に実際の表示領域に描く
		g.drawImage(offs, 0, 0, this);
	}


	////////////////////////////////////////////////////////////////
	// マウス管理
	////////////////////////////////////////////////////////////////
	public void mouseClicked(MouseEvent e){}
	public void mouseEntered(MouseEvent e){
		mouseenterflg = true;
	}
	public void mouseExited(MouseEvent e){
		mouseenterflg = false;
	}
	public void mouseDragged(MouseEvent e){
		if(!mouseenterflg) return;
		mx = e.getX() / SIZE_CUBE;
		my = e.getY() / SIZE_CUBE;
		if(mode==MODE_GAMEON)
			moveCube(mx, my, lockonmx, lockonmy);
		lockonmx = mx;
		lockonmy = my;
		repaint();
	}
	public void mousePressed(MouseEvent e){
		if(!mouseenterflg) return;
		lockonmx = e.getX() / SIZE_CUBE;
		lockonmy = e.getY() / SIZE_CUBE;
		mouseonflg = true;
		repaint();
	}
	public void mouseReleased(MouseEvent e){
		mouseonflg = false;
		if(mode==MODE_TITLE){
			int x = mx / 3;
			int y = my / 3;
			if((x==0)&&(y==1))
				initGame(GAMEMODE_FLOWER);
			if((x==1)&&(y==1))
				initGame(GAMEMODE_CROSS);
			if((x==2)&&(y==1))
				initGame(GAMEMODE_FIVE);
			else
				return;
		}
		else if(mode==MODE_GAMEON){
			if(isCheckClear()){
				mode=MODE_CLEAR;
				// ハイスコアの処理
				if(hightime[gamemode]>nowtime)
					hightime[gamemode]=nowtime;
			}
		}
		else if(mode==MODE_CLEAR){
			mode = MODE_TITLE;
		}
		repaint();
	}
	
	public void mouseMoved(MouseEvent e){
		if(!mouseenterflg) return;
		mx = e.getX() / SIZE_CUBE;
		my = e.getY() / SIZE_CUBE;
		repaint();
	}

		
	////////////////////////////////////////////////////////////////
	// スレッド管理
	////////////////////////////////////////////////////////////////
	public void start() {
		if(kicker == null) {
			// スレッドを実行させる
			loop = true;
			kicker = new Thread(this);
			kicker.start();
		}
	}

	public void stop() {
		// スレッドを止める
		if ( kicker != null ) {
			//kicker.stop();
			loop = false;
			try {
				kicker.join();
			} catch(Exception e){};
			kicker = null;
		}
	}

	public void run() {

		// 描画オブジェクトの確定

		while(loop){

			// 時間の取得
			if(mode==MODE_GAMEON)
				nowtime = (int)((System.currentTimeMillis() - starttime) / 1000);
			repaint();

			// スレッド待機
			try {
				kicker.sleep(threadspeed);
			} catch(Exception e) {
				System.out.println("--- THREAD ERROR:" + e.toString() + " ---");
			}

		}
	}	

	////////////////////////////////////////////////////////////////
	// 初期化
	////////////////////////////////////////////////////////////////
	public void initGame(int valgamemode){
		gamemode = valgamemode;
		
		ArrayList<CubeObject> vec = new ArrayList<CubeObject>();

		// 配列の構成
		switch(gamemode){
		case GAMEMODE_FLOWER:
			for(int i=0;i<9;i++)
				vec.add(new CubeObject(0));
			for(int i=0;i<(9*4);i++)
				vec.add(new CubeObject(1));
			break;
		case GAMEMODE_CROSS:
			for(int i=0;i<9;i++)
				vec.add(new CubeObject(0));
			for(int i=0;i<(9*2);i++)
				vec.add(new CubeObject(1));
			for(int i=0;i<(9*2);i++)
				vec.add(new CubeObject(2));
			break;
		default:
			for(int i=0;i<9;i++)
				vec.add(new CubeObject(0));
			for(int i=0;i<9;i++)
				vec.add(new CubeObject(1));
			for(int i=0;i<9;i++)
				vec.add(new CubeObject(2));
			for(int i=0;i<9;i++)
				vec.add(new CubeObject(3));
			for(int i=0;i<9;i++)
				vec.add(new CubeObject(4));
			break;
		}

		// 乱数で並べ替えを行い、画面に設定
		int index = NONE;
		for(int i=0;i<5;i++){
			for(int j=0;j<9;j++){
				index = (int)(Math.random() * vec.size());
				cubetbl[i][j]= (CubeObject) vec.get(index);
				vec.remove(index);
			}
		}

		// 開始時間
		starttime = System.currentTimeMillis();
		mode = MODE_GAMEON;
	}

	////////////////////////////////////////////////////////////////
	// Cube移動
	////////////////////////////////////////////////////////////////
	private void moveCube(int valmx, int valmy, int vallockonmx, int vallockonmy){
		// 移動できるか否か
		if(((valmx-vallockonmx)!=1)&&((valmx-vallockonmx)!=-1)&&
			((valmy-vallockonmy)!=1)&&((valmy-vallockonmy)!=-1))
			return;

		// 移動
		int side;
		int pos;
		int horizontal;
		int vertical;
		CubeObject[] wkcubetbl = null;
		CubeObject wkobj = null;

		// 座標をside、posに変換
		if((valmx>=3)&&(valmx<6)&&(valmy>=0)&&(valmy<3)){
			side = 0;
			pos = valmy * 3 + (valmx - 3);
		}
		else if((valmx>=0)&&(valmx<3)&&(valmy>=3)&&(valmy<6)){
			side = 1;
			pos = (valmy-3) * 3 + valmx;
		}
		else if((valmx>=3)&&(valmx<6)&&(valmy>=3)&&(valmy<6)){
			side = 2;
			pos = (valmy-3) * 3 + (valmx-3);
		}
		else if((valmx>=6)&&(valmx<9)&&(valmy>=3)&&(valmy<6)){
			side = 3;
			pos = (valmy-3) * 3 + (valmx-6);
		}
		else if((valmx>=3)&&(valmx<6)&&(valmy>=6)&&(valmy<9)){
			side = 4;
			pos = (valmy-6) * 3 + (valmx-3);
		}
		else 
			return;
		
		// 変数の設定
		horizontal = valmx - vallockonmx;
		vertical = valmy - vallockonmy;
		wkcubetbl = getCubeLine(side,pos,(horizontal!=0));
		wkobj = new CubeObject();
		
		// 配列の並べ替え
		if((horizontal==1)||(vertical==1)){
			// 下にずらす
			wkobj.copy(wkcubetbl[wkcubetbl.length-1]);
			for(int i=wkcubetbl.length-1;i>0;i--){
				wkcubetbl[i].copy(wkcubetbl[i-1]);
			}
			wkcubetbl[0].copy(wkobj);
		}
		else {
			// 上にずらす
			wkobj.copy(wkcubetbl[0]);
			for(int i=0;i<wkcubetbl.length-1;i++){
				wkcubetbl[i].copy(wkcubetbl[i+1]);
			}
			wkcubetbl[wkcubetbl.length-1].copy(wkobj);
		}
	}

	// CUBEの配列を取得
	private CubeObject[] getCubeLine(int valside, int valpos, boolean valhorizontalflg){
		
		CubeObject[] wkcubetbl = null;
		int posy = valpos / 3;
		int posx = valpos - posy * 3;

		// 配列を取得
		if((valside==0)||(valside==4)){
			// 0, 4 のテーブル
			if(valhorizontalflg){
				// 水平
				wkcubetbl = new CubeObject[3];
				for(int i=0;i<3;i++)
					wkcubetbl[i] = cubetbl[valside][posy*3+i];
			}
			else {
				// 垂直
				wkcubetbl = new CubeObject[9];
				for(int i=0;i<3;i++)
					wkcubetbl[i] = cubetbl[0][i*3+posx];
				for(int i=0;i<3;i++)
					wkcubetbl[i+3] = cubetbl[2][i*3+posx];
				for(int i=0;i<3;i++)
					wkcubetbl[i+6] = cubetbl[4][i*3+posx];
			}
		}
		else if((valside==1)||(valside==3)){
			// 1, 3 のテーブル
			if(valhorizontalflg){
				// 水平
				wkcubetbl = new CubeObject[9];
				for(int i=0;i<3;i++)
					wkcubetbl[i] = cubetbl[1][posy*3+i];
				for(int i=0;i<3;i++)
					wkcubetbl[i+3] = cubetbl[2][posy*3+i];
				for(int i=0;i<3;i++)
					wkcubetbl[i+6] = cubetbl[3][posy*3+i];
			}
			else {
				// 垂直
				wkcubetbl = new CubeObject[3];
				for(int i=0;i<3;i++)
					wkcubetbl[i] = cubetbl[valside][i*3+posx];
			}
		}
		else{
			if(valhorizontalflg){
				// 水平
				wkcubetbl = new CubeObject[9];
				for(int i=0;i<3;i++)
					wkcubetbl[i] = cubetbl[1][posy*3+i];
				for(int i=0;i<3;i++)
					wkcubetbl[i+3] = cubetbl[2][posy*3+i];
				for(int i=0;i<3;i++)
					wkcubetbl[i+6] = cubetbl[3][posy*3+i];
			}
			else {
				// 垂直
				wkcubetbl = new CubeObject[9];
				for(int i=0;i<3;i++)
					wkcubetbl[i] = cubetbl[0][i*3+posx];
				for(int i=0;i<3;i++)
					wkcubetbl[i+3] = cubetbl[2][i*3+posx];
				for(int i=0;i<3;i++)
					wkcubetbl[i+6] = cubetbl[4][i*3+posx];
			}
		}

		return wkcubetbl;
	
	}

	////////////////////////////////////////////////////////////////
	// 判定
	////////////////////////////////////////////////////////////////
	private boolean isCheckClear(){
		int wkkind = NONE;
		
		// gamemodeによりクリア条件が変わる
		if(gamemode==GAMEMODE_FLOWER){
			for(int i=0;i<9;i++){
				if(cubetbl[2][i].kind!=0) return false;
			}
			return true;
		}
		else if(gamemode==GAMEMODE_CROSS){
			for(int i=0;i<9;i++)
				if(cubetbl[2][i].kind!=0) return false;
			wkkind = cubetbl[0][0].kind;
			for(int i=0;i<9;i++)
				if(cubetbl[0][i].kind!=wkkind) return false;
			for(int i=0;i<9;i++)
				if(cubetbl[4][i].kind!=wkkind) return false;
			return true;
		}
		else {
			for(int i=0;i<4;i++){
				wkkind = cubetbl[i][0].kind;
				for(int j=0;j<9;j++)
					if(cubetbl[i][j].kind!=wkkind) return false;
			}
			return true;
		}

	}
		
	////////////////////////////////////////////////////////////////
	// 内部クラス
	////////////////////////////////////////////////////////////////
	private class CubeObject extends Object {
		int kind	= NONE;

		public CubeObject(){
			super();
		}

		public CubeObject(int valkind){
			super();
			kind = valkind;
		}
		
		public void copy(CubeObject valobj){
			kind = valobj.kind;
		}

		public Color getColor(){
			switch(kind){
			case 0:
				return Color.red;
			case 1:
				return Color.yellow;
			case 2:
				return Color.blue;
			case 3:
				return Color.white;
			default:
				return Color.gray;
			}
		}
	};
}
