// スライドパズル

//-------------------- 定数 --------------------
var IMG_W = 120;	// 画像の幅
var IMG_H = 150;	// 画像の高さ
var CARD_NUM = 150;	// 全カード枚数

var DIV_NUM     = 3;						// １辺の分割数
var CELL_NUM    = DIV_NUM * DIV_NUM;		// 外見上のセルの数
var IMG_NUM_XY  = DIV_NUM + 1;				// 実際に使用している画像の数（１辺）
var IMG_NUM_ALL = IMG_NUM_XY * IMG_NUM_XY;	// 実際に使用している画像の数（全部）

var CELL_W = IMG_W / DIV_NUM;	// １セルの幅
var CELL_H = IMG_H / DIV_NUM;	// １セルの高さ

var MOVE_ANIMATION_INTERVAL = 20;	// セル移動アニメーションの間隔（ミリ秒）
var MOVE_STEP_MAX = 5;				// セル移動アニメーションの最大ステップ数

var TIME_DISP_INTERVAL = 10;		// 時間表示の間隔（ミリ秒）

var CHECK_LOAD_IMAGE_INTERVAL = 1000;	// 画像の事前読み込みチェックの間隔（ミリ秒）
var CHECK_LOAD_IMAGE_MAX_NUM  = 5;		// 画像の事前読み込みチェックの最大回数

//-------------------- グローバル変数 --------------------
var ImgIdx = new Array();	// ImgIdx[i] == N のとき、iの位置にN番の画像がある
var GameStatus;				// ゲームの状態(0:開始前 1:操作待ち 2:セル移動中 3:完成)
var CellX;					// 動かすセルのX座標
var CellY;					// 動かすセルのY座標
var MoveInterval;			// セル移動アニメーションのタイマーID
var MoveStep;				// セル移動アニメーションのステップ数
var MoveSrcX;				// 移動元のセルのX座標
var MoveSrcY;				// 移動元のセルのY座標
var MoveDstX;				// 移動先のセルのX座標
var MoveDstY;				// 移動先のセルのY座標
var TimeInterval;			// 時間計測のタイマーID
var StartTime;				// ゲーム開始時の Date オブジェクト
var CheckLoadInterval;		// 画像の事前読み込みチェックのタイマーID
var CheckLoadNum;			// 画像の事前読み込みチェックの回数

//--------------------------------------------------------------------------------
// 画像基底処理
//--------------------------------------------------------------------------------
// X,Y座標を指定して画像IDを取得
function getImgIdByXY(x, y)
{
	return 'img' + (x + y * IMG_NUM_XY);
}

// X,Y座標を指定して画像の表示状態の設定
function setImgVisibilityByXY(x, y, visibility)
{
	setImgVisibilityById(getImgIdByXY(x, y), visibility)
}

// 画像IDを指定して画像の表示状態の設定
function setImgVisibilityById(img_id, visibility)
{
	$(img_id).style.visibility = visibility;
}

// X,Y座標を指定して画像のクリッピング
function setImgClipByXY(x, y, clip_top, clip_right, clip_bottom, clip_left)
{
	setImgClipById(getImgIdByXY(x, y), clip_top, clip_right, clip_bottom, clip_left)
}

// 画像IDを指定して画像のクリッピング
function setImgClipById(img_id, clip_top, clip_right, clip_bottom, clip_left)
{
	$(img_id).style.clip = 'rect(' + clip_top + ' ' + clip_right + ' ' + clip_bottom + ' ' + clip_left + ')';
}

// X,Y座標を指定して画像の表示位置の設定
function setImgPositionByXY(x, y, left, top)
{
	setImgPositionById(getImgIdByXY(x, y), left, top)
}

// 画像IDを指定して画像の表示位置の設定
function setImgPositionById(img_id, left, top)
{
	$(img_id).style.left = left;
	$(img_id).style.top  = top;
}

//--------------------------------------------------------------------------------
// 画像ゲーム用基本処理
//--------------------------------------------------------------------------------
// 座標AX,AYの位置に、画像のN番の部分を表示
function setImgPos(ax, ay, n)
{
	setImgPosByXY(ax, ay, n % DIV_NUM, Math.floor(n / DIV_NUM), 0, 0, 0, 0, 0, 0);
}

// 座標AX,AYの位置に、移動中の画像のBX,BYの部分を表示
function setMovingImgPos(ax, ay, n, clip_t, clip_r, clip_b, clip_l, pos_l, pos_t)
{
	setImgPosByXY(ax, ay, n % DIV_NUM, Math.floor(n / DIV_NUM), clip_t, clip_r, clip_b, clip_l, pos_l, pos_t);
}

// 座標AX,AYの位置に、画像のBX,BYの部分を表示
function setImgPosByXY(ax, ay, bx, by, clip_t, clip_r, clip_b, clip_l, pos_l, pos_t)
{
	var clip_left   = CELL_W *  bx      + clip_l;
	var clip_right  = CELL_W * (bx + 1) - clip_r;
	var clip_top    = CELL_H *  by      + clip_t;
	var clip_bottom = CELL_H * (by + 1) - clip_b;
	setImgClipByXY(ax, ay, clip_top, clip_right, clip_bottom, clip_left);

	var left = CELL_W * (ax - bx) - pos_l;
	var top  = CELL_H * (ay - by) - pos_t;
	setImgPositionByXY(ax, ay, left, top);
}

// 固定された（セル移動中でない場合の）全画像の表示状態を設定
function setFixedImgDisp()
{
	// 各セルの表示設定
	for(var x = 0; x < DIV_NUM; x++){
		for(var y = 0; y < DIV_NUM; y++){
			setImgPos(x, y, ImgIdx[x + y * DIV_NUM]);
		}
	}

	var visibility;
	for(var x = 0; x < IMG_NUM_XY; x++){
		for(var y = 0; y < IMG_NUM_XY; y++){
			if( (x == IMG_NUM_XY - 1) || (y == IMG_NUM_XY - 1) ){
				visibility = 'hidden';
			} else {
				visibility = 'visible';
			}
			setImgVisibilityByXY(x, y, visibility);
		}
	}
}

// 移動中のセルの画像の表示状態を設定
function setMovingImgDisp()
{
	var n_list      = new Array();
	var clip_t_list = new Array();
	var clip_r_list = new Array();
	var clip_b_list = new Array();
	var clip_l_list = new Array();
	var pos_l;
	var pos_t;

	if(MoveSrcX == MoveDstX){
		// 列移動
		for(var i = 0; i < IMG_NUM_XY; i++){
			clip_t_list[i] = 0;
			clip_b_list[i] = 0;
		}
		if(MoveSrcY > MoveDstY){
			// 上に移動
			for(var i = 0; i < IMG_NUM_XY; i++){
				n_list[i] = ImgIdx[(MoveDstX + ((i + DIV_NUM - 1) % DIV_NUM) * DIV_NUM) % CELL_NUM];
			}
			clip_t_list[0]              = CELL_H * (MoveStep / MOVE_STEP_MAX);
			clip_b_list[IMG_NUM_XY - 1] = CELL_H - CELL_H * (MoveStep / MOVE_STEP_MAX);
		} else {
			// 下に移動
			for(var i = 0; i < IMG_NUM_XY; i++){
				n_list[i] = ImgIdx[(MoveDstX + (i % DIV_NUM) * DIV_NUM) % CELL_NUM];
			}
			clip_t_list[0]              = CELL_H - CELL_H * (MoveStep / MOVE_STEP_MAX);
			clip_b_list[IMG_NUM_XY - 1] = CELL_H * (MoveStep / MOVE_STEP_MAX);
		}
		pos_t = clip_t_list[0];
		for(var i = 0; i < IMG_NUM_XY; i++){
			setMovingImgPos(MoveDstX, i, n_list[i], clip_t_list[i], 0, clip_b_list[i], 0, 0, pos_t);
		}
	} else {
		// 行移動
		for(var i = 0; i < IMG_NUM_XY; i++){
			clip_l_list[i] = 0;
			clip_r_list[i] = 0;
		}
		if(MoveSrcX > MoveDstX){
			// 左に移動
			for(var i = 0; i < IMG_NUM_XY; i++){
				n_list[i] = ImgIdx[(((i + DIV_NUM - 1) % DIV_NUM) + MoveDstY * DIV_NUM) % CELL_NUM];
			}
			clip_l_list[0]              = CELL_W * (MoveStep / MOVE_STEP_MAX);
			clip_r_list[IMG_NUM_XY - 1] = CELL_W - CELL_W * (MoveStep / MOVE_STEP_MAX);
		} else {
			// 右に移動
			for(var i = 0; i < IMG_NUM_XY; i++){
				n_list[i] = ImgIdx[((i % DIV_NUM) + MoveDstY * DIV_NUM) % CELL_NUM];
			}
			clip_l_list[0]              = CELL_W - CELL_W * (MoveStep / MOVE_STEP_MAX);
			clip_r_list[IMG_NUM_XY - 1] = CELL_W * (MoveStep / MOVE_STEP_MAX);
		}
		pos_l = clip_l_list[0];
		for(var i = 0; i < IMG_NUM_XY; i++){
			setMovingImgPos(i, MoveDstY, n_list[i], 0, clip_r_list[i], 0, clip_l_list[i], pos_l, 0);
		}
	}
}

//--------------------------------------------------------------------------------
// 画像ゲームフロー用処理
//--------------------------------------------------------------------------------
// リセットボタンを押したときの初期化処理
function initImg()
{
	// 伏せカード画像だけ表示、他は非表示
	var visibility;
	for(var x = 0; x < IMG_NUM_XY; x++){
		for(var y = 0; y < IMG_NUM_XY; y++){
			if( (x == IMG_NUM_XY - 1) && (y == IMG_NUM_XY - 1) ){
				visibility = 'visible';
			} else {
				visibility = 'hidden';
			}
			setImgVisibilityByXY(x, y, visibility);
		}
	}

	// カード画像をランダムに決定する
	var card_id = Math.floor(Math.random() * CARD_NUM) + 1;
	if(card_id < 10){
		card_id = '00' + card_id;
	} else if(card_id < 100){
		card_id = '0' + card_id;
	}
	var old_src = $('img0').src;
	var new_src = old_src.substr(0, old_src.length - 7) + card_id + old_src.substr(old_src.length - 4, 4);
	for(var i = 0; i < IMG_NUM_ALL - 1; i++){
		$('img' + i).src = new_src;
	}

	CheckLoadNum = 0;
	CheckLoadInterval = setInterval('isLoadedImg()', CHECK_LOAD_IMAGE_INTERVAL);
}

// 画像の読み込み待ちインターバル関数
function isLoadedImg()
{
	CheckLoadNum++;
	if(CheckLoadNum > CHECK_LOAD_IMAGE_MAX_NUM){
		endLoadingImg();
		return;
	}

	for(var i = 0; i < IMG_NUM_ALL - 1; i++){
		if( !($('img' + i).complete) ){
			break;
		}
	}
	if(i == IMG_NUM_ALL - 1){
		endLoadingImg();
	}
}

// 画像の読み込み完了
function endLoadingImg()
{
	clearInterval(CheckLoadInterval);
	$('start_button').disabled = false;
}

// スタートボタンを押したときの初期化処理
function startImg()
{
	setFixedImgDisp();
}

// セル移動のアニメーション開始
function beginMoveAnimation(ax, ay, bx, by)
{
	GameStatus = 2;
	MoveStep = 0;
	MoveSrcX = ax;
	MoveSrcY = ay;
	MoveDstX = bx;
	MoveDstY = by;
	MoveInterval = setInterval('doMoveAnimation()', MOVE_ANIMATION_INTERVAL);
}

// セル移動のアニメーション実行
function doMoveAnimation()
{
	if(MoveStep < MOVE_STEP_MAX){
		if(MoveStep == 0){
			// アニメーション用の隠しセルを表示
			if(MoveSrcX == MoveDstX){
				// 列移動
				setImgVisibilityByXY(MoveDstX, IMG_NUM_XY - 1, 'visible');
			} else {
				// 行移動
				setImgVisibilityByXY(IMG_NUM_XY - 1, MoveDstY, 'visible');
			}
		}
		MoveStep++;
		setMovingImgDisp();
	} else {
		clearInterval(MoveInterval);
		endMoveAnimation();
	}
}

// セル移動のアニメーション終了
function endMoveAnimation()
{
	setFixedImgDisp();
	GameStatus = 1;
	if( isGameCleared() ){
		// ゲームクリア
		endGame();
	}
}

//--------------------------------------------------------------------------------
// データ処理
//--------------------------------------------------------------------------------
// リセットボタンを押したときの初期化処理
function initData()
{
	clearInterval(TimeInterval);
	clearInterval(MoveInterval);
	clearInterval(CheckLoadInterval);
	for(var i = 0; i < CELL_NUM; i++){
		ImgIdx[i] = i;
	}
	GameStatus = 0;
	CellX = -1;
	CellY = -1;
	MoveStep = 0;
	MoveSrcX = -1;
	MoveSrcY = -1;
	MoveDstX = -1;
	MoveDstY = -1;
	StartTime = null;
}

// 座標AX,AYのセルを、座標BX,BYに移動
function moveCell(ax, ay, bx, by)
{
	if( ((ax != bx) && (ay != by)) || ((ax == bx) && (ay == by)) ){
		// スライド移動できないセル指定になっている
		return false;
	}

	var cell = new Array();
	if(ax == bx){
		// 列移動
		for(var i = 0; i < DIV_NUM; i++){
			cell[i] = ImgIdx[ax + i * DIV_NUM];
		}
		for(var i = 0; i < DIV_NUM; i++){
			ImgIdx[ax + i * DIV_NUM] = cell[(DIV_NUM + i + ay - by) % DIV_NUM];
		}
	} else {
		// 行移動
		for(var i = 0; i < DIV_NUM; i++){
			cell[i] = ImgIdx[i + ay * DIV_NUM];
		}
		for(var i = 0; i < DIV_NUM; i++){
			ImgIdx[i + ay * DIV_NUM] = cell[(DIV_NUM + i + ax - bx) % DIV_NUM];
		}
	}
	return true;
}

// シャッフル処理
function shuffle()
{
	// シャッフル
	for(var i = 0; i < 20; i++){
		for(var j = 0; j < DIV_NUM; j++){
			moveCell(j, 0, j, Math.floor(Math.random() * DIV_NUM));
		}
		for(var j = 0; j < DIV_NUM; j++){
			moveCell(0, j, Math.floor(Math.random() * DIV_NUM), j);
		}
	}

	// もし完成形になっていればシャッフルやりなおし
	if( isGameCleared() ){
		shuffle();
	}
}

// ゲームクリアの状態かチェック
function isGameCleared()
{
	for(var i = 0; i < CELL_NUM; i++){
		if(ImgIdx[i] != i){
			return false;
		}
	}
	return true;
}

// ゲームクリア時の処理
function endGame()
{
	clearInterval(TimeInterval);
	GameStatus = 3;
	var play_time = doDispTime();
	$('gameclear').style.color = '#cc0000';

	// Ajaxでクリアタイムを送信
	var url = document.URL.split("#")[0];
	new Ajax.Request(url, {
		method: "post",
		parameters: "play_time=" + play_time
	});
}

//--------------------------------------------------------------------------------
// ゲーム処理
//--------------------------------------------------------------------------------
// スタートボタンを押したときのゲーム開始処理
function gameStart()
{
	$('start_button').disabled = true;
	$('reset_button').disabled = false;

	shuffle();
	startImg();
	GameStatus = 1;

	// スタート時間を取得
	StartTime = new Date();
	TimeInterval = setInterval('doDispTime()', TIME_DISP_INTERVAL);
}

// プレイ時間表示のインターバル関数
function doDispTime()
{
	var now_time;
	var play_time;
	var min;
	var sec;
	var ms;

	now_time      = new Date();									// 現在の時間を取得
	play_time     = now_time.getTime() - StartTime.getTime();	// プレイ時間を通算ミリ秒で計算
	min           = Math.floor(play_time / (60*1000));			// '分'取得
	play_time_sub = play_time - (min * 60*1000);
	sec           = Math.floor(play_time_sub / 1000);			// '秒'取得
	ms            = Math.floor((play_time_sub % 1000) / 10);	// 'コンマ○秒'取得
	dispTime(min, sec, ms);

	return play_time;
}

// プレイ時間を表示
function dispTime(min, sec, ms)
{
	if(sec < 10){
		sec = '0' + sec;
	}
	if(ms < 10){
		ms = '0' + ms;
	}
	$('gametime').value = min + ':' + sec + '.' + ms;
}

// 画像の onmousedown 時の処理
function imgMouseDown(x, y)
{
	if(GameStatus != 1){
		return;
	}
	CellX = x;
	CellY = y;
}

// 画像の onmouseover 時の処理
function imgMouseOver(x, y)
{
	if(GameStatus != 1){
		return;
	}
	if(CellX >= 0){
		// 一度に１セル分しか移動させない
		if(x == CellX){
			// 列移動
			if(y > CellY + 1){
				y = CellY + 1;
			} else if(y < CellY - 1){
				y = CellY - 1;
			}
		} else if(y == CellY){
			// 行移動
			if(x > CellX + 1){
				x = CellX + 1;
			} else if(x < CellX - 1){
				x = CellX - 1;
			}
		}
		// セル移動
		if( moveCell(CellX, CellY, x, y) ){
			beginMoveAnimation(CellX, CellY, x, y);
		}
		CellX = -1;
		CellY = -1;
	}
}

//--------------------------------------------------------------------------------
// 初期化
//--------------------------------------------------------------------------------
// onload時の初期化処理
function init()
{
	// 画像にイベントハンドラをセット
	$('img0').onmousedown  = function(){ imgMouseDown(0, 0); };
	$('img0').onmouseover  = function(){ imgMouseOver(0, 0); };
	$('img1').onmousedown  = function(){ imgMouseDown(1, 0); };
	$('img1').onmouseover  = function(){ imgMouseOver(1, 0); };
	$('img2').onmousedown  = function(){ imgMouseDown(2, 0); };
	$('img2').onmouseover  = function(){ imgMouseOver(2, 0); };
	$('img4').onmousedown  = function(){ imgMouseDown(0, 1); };
	$('img4').onmouseover  = function(){ imgMouseOver(0, 1); };
	$('img5').onmousedown  = function(){ imgMouseDown(1, 1); };
	$('img5').onmouseover  = function(){ imgMouseOver(1, 1); };
	$('img6').onmousedown  = function(){ imgMouseDown(2, 1); };
	$('img6').onmouseover  = function(){ imgMouseOver(2, 1); };
	$('img8').onmousedown  = function(){ imgMouseDown(0, 2); };
	$('img8').onmouseover  = function(){ imgMouseOver(0, 2); };
	$('img9').onmousedown  = function(){ imgMouseDown(1, 2); };
	$('img9').onmouseover  = function(){ imgMouseOver(1, 2); };
	$('img10').onmousedown = function(){ imgMouseDown(2, 2); };
	$('img10').onmouseover = function(){ imgMouseOver(2, 2); };

	gameReset();
}

// リセットボタンを押したときの初期化処理
function gameReset()
{
	$('reset_button').disabled = true;
	$('gameclear').style.color = '#999999';

	initData();	// clearInterval をしてから
	initImg();	// setInterval をしているので、順番を変えてはいけない

	dispTime(0, 0, 0);
}

window.onload = init;
