/*
	Speed for WonderSwan
		H.Iwasaki
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/bios.h>
#include <sys/process.h>
#include "hima.h"

#define DMOVE		15
#define	DEALWAIT	30
#define CTDWAIT		40
#define CTDOWN		3

#define rot33(n)	((n) % 3 * 3 + 2 - (n) / 3)

static void init_game_screen( void );
static void deal( void );
static void countDown( void );
static void setMvSp( int pNo, char fld[], int csr, int *rest, char card, int idx );
static void autoput( char fld[], int idx );
static int game_clear( void );
static void play_game( void );
static void keyChk( int pad );
static void dispScreen( void );
static void init_game( void );
static void shuffl( void );
static int putChk( void );
static int putOk( char c1, char c2 );

/* 移動させるカードを管理する変数 */
static struct {
	int		p;
	double		dx, dy;
	int 		x, y, cnt, *restP;
	char		*from, *to, card;
} mvSp[8];

/* カードを並べるときの座標 */
static int	posX[5] = { 5, 5, 9, 5, 1 };
static int	posY[5] = { 7, 3, 7, 11, 7 };

/* COMのレベル毎のWAIT */
static int	eWait[3]    = { 75, 50, 30 };
static int	eWaitRnd[3] = { 90, 80, 20 };

/* ゲーム用変数 */
static char	myCard[26], eCard[26];
static char	myFld[5], eFld[5], field[2];
static int 	myCardP, eCardP, mode, cursor;
static int 	myRest, eRest, gCnt, ctd, eWaitTotal;

void speed_main()
{
	static char	s[3];
	int 		i;

	srand( sys_get_tick_count() );
	/* card set */
	for( i = 0; i < 13; i++ ) {
		myCard[i]    = i * 4;
		myCard[i+13] = i * 4 + 2;
		eCard[i]     = i * 4 + 1;
		eCard[i+13]  = i * 4 + 3;
	}
	mode = 1;

	init_game_screen();

	init_game();

	dispWaku(12,2,3,14,1);
	msg(13,3,"PUSH ANY KEY",0,1,CHAR_STR);
	play_disp_msg();
	key_wait();
	screen_fill_char(SCREEN1, 12, 2, 3, 14, CHAR_SPACE);

	while( mode != 0 ) {
		keyChk( key_press_check() );
		switch( mode ) {
			case 1:		/* 自分のカードを並べる */
				dispScreen();
				deal();
				break;
			case 2:		/* カウントダウン */
				dispScreen();
				sprintf(s,"%d",ctd);
				msg(14,8,s,0,1,507);
				countDown();
				break;
			case 3:		/* プレイ中 */
				play_game();
				dispScreen();
				if( myRest == 0 || eRest == 0 ) {
					dispScreen();
					sys_wait(75 * 2);
					mode = 4;
				}
				else if( putChk() ) {
					for( i = 0; i < 75; i++ ) {
						sys_wait(1);
						dispScreen();
					}
					mode = 2;
					ctd = CTDOWN;
					gCnt = 0;
					play_key_push();
				}
				break;
			case 4:		/* 結果表示 & REPLAY? */
				mode = game_clear();
				init_game();
				screen_fill_char(SCREEN1, 0, 0, 28, 18, CHAR_SPACE);
		}
	}
}

static void init_game_screen( void )
{
	int 	i, j, k;

	lcd_off();	/* ディスプレイを消す */

	/* FONT LOAD */
	for( i = 0; i < cards_width * cards_height; i++ )
		font_set_colordata(CHAR_CARDS + i, 1, rotFont(&bmp_cards[i * 8]));
	for( i = 0; i < 9; i++ )
		font_set_colordata(CHAR_WAKU + i, 1, rotFont(&waku[i * 8]));

	/* card character set */
	/* screen_set_char(SCREEN1, x, y, 3, 3, card_char[0-3][0-12]); */
	for ( i = 0; i < 4; i++ )
		for( j = 0; j < 13; j++ )
			for( k = 0; k < 9; k++ )
				card_char[i][j][rot33(k)] 
					= (0 << CFSFT_PALETTE) | (i * 13 * 3 * 3 + j * 3 + k + k / 3 * 36);

	/* clear screen */
	screen_fill_char(SCREEN1, 0, 0, 28, 30, CHAR_SPACE);

	for( i = 0; i < 9; i++ ) {
		sprite_set_char(rot33(i), ((12 - 8) << CFSFT_PALETTE) | (CHAR_WAKU + i));
		cell_char[rot33(i)] = (2 << CFSFT_PALETTE) | (i + i / 3 * 36);
	}

	sprite_set_range(0, 9 * 9);			/* スプライト表示ON */
	for( i = 0; i < 9; i++ )
		putSprite33(i * 9, 224, 0);

	display_control(DCM_SCR1 | DCM_SPR);	/* ディスプレイ設定 */

	lcd_on();	/* ディスプレイを点ける */
}

static void play_game( void )
{
	int 	i, key;

	key = key_press_check();
	keyChk(key);

	/* プレイヤー */
	if( (key & KEY_X1) && cursor ) {		/* 左へカードを出す */
		if( !(myFld[cursor] & 0x80) )
			setMvSp( 0, myFld, cursor, &myRest, myFld[cursor], 0 );
	}
	else if( (key & KEY_X3) && cursor ) {	/* 右へカードを出す */
		if( !(myFld[cursor] & 0x80) )
			setMvSp( 0, myFld, cursor, &myRest, myFld[cursor], 1 );
	}
	else if( (key & KEY_X4) && cursor && myFld[cursor] == -1 && myCardP > -1 ) {
		myFld[cursor] = myCard[myCardP--];
		play_card_put();
	}

	/* コンピュータ */
	/* eWaitTotalフレーム毎にカードを場に出す。
	 * ただし，並べる必要がある場合は，eWait[level]フレームで
	 * カードを置き，フレームカウンタをリセットする。
	 */
	if( gCnt > eWait[level] ) {
		for( i = 1; i < 5; i++ )
			if( eFld[i] == -1 && eCardP > -1 ) {
				eFld[i] = eCard[eCardP--];
				gCnt = 0;
				break;
			}
	}
	if( gCnt > eWaitTotal ) {
		for( i = 1; i < 5; i++ )
			if( !(eFld[i] & 0x80) && putOk(eFld[i], field[0]) ) {
				setMvSp( 1, eFld, i, &eRest, eFld[i], 0 );
				break;
			}
			else if( !(eFld[i] & 0x80) && putOk(eFld[i], field[1]) ) {
				setMvSp( 1, eFld, i, &eRest, eFld[i], 1 );
				break;
			}
		gCnt = 0;
		eWaitTotal = eWait[level] + rand() % eWaitRnd[level];
	}
}

/**********************************************
 * 開始前にカードを並べる
 *
 **********************************************/
static void deal( void )
{
	int 	i;

	if( gCnt > DEALWAIT ) {
		for( i = 1; i < 5; i++ )
			if( myFld[i] == -1 ) {
				myFld[i] = myCard[myCardP--];
				eFld[i] = eCard[eCardP--];
				break;
			}
		play_card_put();
		gCnt = 0;
	}
	if( myFld[4] != -1 ) {
		dispScreen();
		mode = 2;
		ctd = CTDOWN;
		gCnt = 0;

		sys_wait(75);
		play_key_push();
	}
}

/**********************************************
 * スタートまでのカウントダウン
 *　& 持ち札から場にカードを出す
 **********************************************/
static void countDown( void )
{
	if( gCnt > CTDWAIT ) {
		play_key_push();
		ctd--;
		gCnt = 0;
	}
	if( ctd < 1 ) {
		if( myCardP > -1 )
			field[1] = myCard[myCardP--];
		else
			if( cursor && !(myFld[cursor] & 0x80) ) {
				field[1] = myFld[cursor];
				myFld[cursor] = -1;
			}
			else
				autoput(myFld,1);
		if( eCardP > -1 )
			field[0] = eCard[eCardP--];
		else
			autoput(eFld,0);
		myRest--;
		eRest--;
		screen_fill_char(SCREEN1, 14, 8, 1, 1, CHAR_SPACE);
		eWaitTotal = eWait[level] + rand() % eWaitRnd[level];
		mode = 3;
		gCnt = 0;
	}
}

/**********************************************
 * 場に出すカードを動かすための準備
 *
 **********************************************/
static void setMvSp( int pNo, char fld[], int csr, int *rest, char card, int idx )
{
	int 	i, j;

	for( i = 0; i < 8; i++ )
		if( mvSp[i].p == 0 ) {
			for( j = 0; j < 9; j++ )
				sprite_set_char((i + 1) * 9 + j, ((12 - 8) << CFSFT_PALETTE)
					| card_char[card & 3][card / 4][j]);
			mvSp[i].p = (i + 1) * 9;
			mvSp[i].x = posX[csr] * 8 + pNo * 15 * 8;
			mvSp[i].y = posY[csr] * 8;
			mvSp[i].dx = (double)(13 * 8 - mvSp[i].x) / DMOVE;
			mvSp[i].dy = (double)((idx * 12 + 1) * 8 - mvSp[i].y) / DMOVE;
			mvSp[i].cnt = 0;
			mvSp[i].restP = rest;
			mvSp[i].from = &fld[csr];
			mvSp[i].to = &field[idx];
			mvSp[i].card = fld[csr];
			fld[csr] = -2;
			putSprite33( mvSp[i].p, mvSp[i].x, mvSp[i].y );
			break;
		}
	play_card_put();
}

/**********************************************
 * 並べられたカードを場に出す
 *　　(カウントダウン後持ち札が無いときの処理)
 **********************************************/
static void autoput( char fld[], int idx )
{
	int 	i;

	for( i = 1; i < 5; i++ )
		if( !(fld[i] & 0x80) ) {
			field[idx] = fld[i];
			fld[i] = -1;
			break;
		}
	play_card_put();
}

/**********************************************
 * 勝敗の表示&replay選択
 *　戻り値:1ならreplay,0なら終了
 **********************************************/
static int game_clear( void )
{
	int 	n;

	for( n = 0; n < 8; n++ )
		if( mvSp[n].p != 0 )
			putSprite33( mvSp[n].p, 224, 0);
	dispWaku(12,2,6,15,1);
	if( eRest ) {
		n = msg(16,5,"YOU WIN",0,1,CHAR_STR);
		win[level]++;
	}
	else if( myRest ) {
		n = msg(16,5,"YOU LOSE",0,1,CHAR_STR);
		lose[level]++;
	}
	else {
		n = msg(16,7,"DRAW",0,1,CHAR_STR);
		draw[level]++;
	}
	n = msg(14,3,"REPLAY ?",0,1,n);
	n = msg(13,3,"(X3:yes/else)",0,1,n);
	play_disp_msg();
	if( key_wait() & KEY_X3 )
		return 1;
	else
		return 0;
}

/**********************************************
 * キー入力に従ってカーソルを移動
 *　& 中断メニューの表示
 **********************************************/
static void keyChk( int key )
{
	if( key & KEY_Y1 )
		cursor = 1;
	else if( key & KEY_Y2 )
		cursor = 2;
	else if( key & KEY_Y3 )
		cursor = 3;
	else if( key & KEY_Y4 )
		cursor = 4;
	else
		cursor = 0;
	if( key & KEY_START ) {
		static char *list[] = { " EXIT ", "YIELD " };
		switch( exitMenu(10,5,4,8,list,2,1) ) {
			case 0:	lose[level]++;
				mode = 0;
				break;
			case 1:	procIL->_yield();
			case 2:	;
		}
	}
}

/**********************************************
 * 画面表示
 *
 **********************************************/
static void dispScreen( void )
{
	static char	s[3];
	int 		i;

	sys_wait(1);
	/* カーソルの表示 */
	putSprite33( 0, posX[cursor] * 8, posY[cursor] * 8 );

	/* フィールドのカードを表示 */
	for( i = 1; i < 5; i++ ) {
		if( !(myFld[i] & 0x80) )
			screen_set_char(SCREEN1, posX[i], posY[i], 3, 3,
				card_char[ myFld[i] & 3 ][ myFld[i] / 4 ]);
		else
			screen_set_char(SCREEN1, posX[i], posY[i], 3, 3, cell_char);

		if( !(eFld[i] & 0x80) )
			screen_set_char(SCREEN1, posX[i] + 15, posY[i], 3, 3,
				card_char[ eFld[i] & 3 ][ eFld[i] / 4 ]);
		else
			screen_set_char(SCREEN1, posX[i] + 15, posY[i], 3, 3, cell_char);
	}
	for( i = 0; i < 2; i++ )
		if( !(field[i] & 0x80) )
			screen_set_char(SCREEN1, 13, i * 12 + 1, 3, 3,
				card_char[ field[i] & 3 ][ field[i] / 4 ]);
		else
			screen_set_char(SCREEN1, 13, i * 12 + 1, 3, 3, cell_char);

	/* 移動中のカードを表示 */
	for( i = 0; i < 8; i++ )
		if( mvSp[i].p != 0 ) {
			if( mvSp[i].to != 0 )
				mvSp[i].cnt++;
			else
				mvSp[i].cnt--;
			if( mvSp[i].cnt == DMOVE ) {
				if( putOk(mvSp[i].card, *mvSp[i].to) ) {
					*mvSp[i].to = mvSp[i].card;
					*mvSp[i].from = -1;
					(*mvSp[i].restP)--;
					putSprite33( mvSp[i].p, 224, 0);
					mvSp[i].p = 0;
				}
				else
					mvSp[i].to = 0;
			}
			else if( mvSp[i].cnt == 0 ) {
				*mvSp[i].from = mvSp[i].card;
				putSprite33( mvSp[i].p, 224, 0);
				mvSp[i].p = 0;
			}
			else
				putSprite33( mvSp[i].p,
						mvSp[i].x + mvSp[i].dx * mvSp[i].cnt,
						mvSp[i].y + mvSp[i].dy * mvSp[i].cnt );
		}
	gCnt++;

	/* 残り枚数を表示 */
	sprintf(s,"%2d",eRest);
	msg(21,15,s,0,1,508);
	sprintf(s,"%2d",myRest);
	msg(6,15,s,0,1,510);
}

/**********************************************
 *　ゲームの初期化
 *
 **********************************************/
static void init_game( void )
{
	int 	i;

	gCnt = 0;
	cursor = 0;
	myRest = eRest = 26;
	field[0] = field[1] = -1;
	for( i = 0; i < 5; i++ ) {
		myFld[i] = -1;
		eFld[i] = -1;
	}
	for( i = 0; i < 8; i++ )
		mvSp[i].p = 0;
	shuffl();
}

/**********************************************
 *　カードをシャッフル
 *
 **********************************************/
static void shuffl( void )
{
	int 	i, j;
	char	wk;

	for( i = 0; i < 200; i++ ) {
		j = rand() % 26;
		wk = myCard[0];
		myCard[0] = myCard[j];
		myCard[j] = wk;
		j = rand() % 26;
		wk = eCard[0];
		eCard[0] = eCard[j];
		eCard[j] = wk;
	}
	myCardP = eCardP = 25;
}

/**********************************************
 * 場に出せるカードがあるかどうかのチェック
 *　戻り値:一枚も出せなくなったら真
 **********************************************/
static int putChk( void )
{
	int	i, flg = 1;

	for( i = 1; i < 5; i++ ) {
		if( putOk(myFld[i], field[0]) || putOk(myFld[i], field[1]) )
			flg = 0;
		if( putOk(eFld[i], field[0]) || putOk(eFld[i], field[1]) )
			flg = 0;
		if( myFld[i] == -1 && myCardP > -1 )
			flg = 0;
		if( eFld[i] == -1 && eCardP > -1 )
			flg = 0;
		if( myFld[i] == -2 || eFld[i] == -2 )
			flg = 0;
	}
	return flg;
}

/**********************************************
 * c1とc2の数字が連続しているかどうかチェック
 *　戻り値:連続してれば真
 **********************************************/
static int putOk( char c1, char c2 )
{
	if( (c1 || c2) & 0x80 )
		return 0;
	c1 >>= 2;
	c2 >>= 2;
	c1 -= c2;
	return( c1 == 1 || c1 == 0xff || c1 == 12 || c1 == 0xf4 );
}
