/*<Header>==============================================================
*
*	WIZME / "WIZ_IR.C"	WIZリンクソフト / ワイヤレスコマンダー操作ブロック
*
*		[ EIN(tm) project : サンプルプログラム ]
*
*	COPYRIGHT  Nam  1995, All rights reserved.
*
*	配付・組込・改変・商利用すべて自由。ただし無保証っす
*
*-----------------------------------------------------------------------
*	V1.0L01α	95.08.04/Nam	プロトタイプ
*								(サンプルのZR.C,Z.Cを参考にさせて頂きました)
*	V1.0L02α	95.08.08/Nam	右クリック中断
*</Header>==============================================================*/
#include	<stdio.h>
#include	<ctype.h>
#include	<mos.h>			// このサンプルでのみ必要


#define	LEFT		0
#define	RIGHT		1

#define	ERROR	(-1)
#ifndef NOERR
	#define	NOERR	(0)
#endif

typedef	unsigned char	BYTE;
typedef	unsigned short	WORD;

#define	ZRCVMAX		(512+17+1)
#define	BLOCKMAX	(512)

#define ENQ		(0x05)
#define	SYN		(0x16)
#define	ACK		(0x06)
#define	NACK	(0x15)
#define	CAN		(0x18)

#define	RETRY	(4)

extern int	termSize;			// 元データバッファ内データサイズ(bytes)

int		irPort;					// IR送受信用ポート番号(0/1)

BYTE	irBuf[ZRCVMAX];			// IR送受信用バッファ
BYTE	repBuf[ZRCVMAX];		// IR送受信リトライ時再送信用バッファ

BYTE	enqDat[ 8 ] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x82, ENQ  },
	synDat[ 8 ] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x82, SYN  },
	ackDat[ 8 ] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x82, ACK  },
	nacDat[ 8 ] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x82, NACK },
	canDat[ 8 ] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x82, CAN  };


extern int		rmcCommand    ( int, BYTE );
extern int		rmcSendStop   ( int );
extern int		rmcRecieveByte( int, BYTE * );
extern int		rmcRecieveStop( int );
extern int		rmcSendByte   ( int, BYTE );
extern int		rmcPutZaurus  ( int, BYTE *, int, BYTE *, int, int * );
extern int		rmcGetZaurus  ( int, BYTE *, int, int * );
extern void		wait200mic    ( void );		// 200マイクロ秒待ち

extern int		DM_showMsg();
extern int		DM_dispCount();

extern int		WL_recBuf();
extern int		WL_recRBuf();
extern int		WL_mkSendBuf();

int		IR_checkBreak()
{
	int		btn, bx, by;
	// 現在のマウスカーソルの位置を読む
	MOS_rdpos( &btn, &bx, &by );
	// 右クリックか?
	if ( (btn & 0x02) != 0 ){
		// 終了処理
		rmcSendStop( irPort );
		DM_showMsg( "※中断したっス", NULL);
		return 1;
	}
	return 0;
}

int	IR_waitBreak( int sec )
{
	int		btn, bx, by, i;

	for( i = 0; i < (sec * 1000 * 5); i++ ){	/* タイムアウト迄    */
										/*(=(200μsec * 5 * 1000)*sec秒) */
		// 右クリック中断
		// 現在のマウスカーソルの位置を読む
		MOS_rdpos( &btn, &bx, &by );
		// 右クリックか?
		if ( (btn & 0x02) != 0 ){
			return ERROR;
		}
		wait200mic();					/* (200マイクロ秒待ち) */
	}
	return NOERR;
}

void	IR_dispErrWC( int err )
{
	switch( err )
	{
		case 1:
			DM_showMsg( "※ワイヤレスコマンダがはずれたっス", NULL);
			break;
		case 2:
			DM_showMsg( "※中断したっス", NULL);
			break;
		default:
			DM_showMsg( "※ワイヤレスコマンダが見あたらないっス","(もし繋がってるなら電池を交換してね)" );
			break;
	}
}

int		IR_checkWC()
{
    int		ret;
    BYTE	stt;

	irPort = RIGHT;
	ret = rmcCommand ( irPort, 0x33 );	/* バッテリチェック	*/
	if( ret == 1 ){						/* 右 非接続		*/
		irPort = LEFT;
		ret = rmcCommand ( irPort, 0x33 );
	}
	if( ret != 0 ){
		if( rmcSendStop( irPort ) ){
//			IR_dispErrWC( 0 );	// 『ワイヤレスコマンダが見当たりません』表示
		}
		IR_dispErrWC( 0 );	// 『ワイヤレスコマンダが見当たりません』表示
		return ERROR;
	}
	ret = rmcRecieveByte( irPort, &stt );
	if( ret != 0 ){
		if( ret != 1 && rmcRecieveStop( irPort ) ){
			IR_dispErrWC( 1 );	// 『ワイヤレスコマンダがはずされました』表示
		}
		return ERROR;
	}
//	printf( "電池:%s [ROM-Ver=%02X]", (stt & 0x40)?"OK":"NG", (stt & 0x3f) );
	if( rmcSendStop( irPort ) ){
		IR_dispErrWC( 1 );	// 『ワイヤレスコマンダがはずされました』表示
		return ERROR;
	}
	if ( !(stt & 0x40) ){
		DM_showMsg( "※ワイヤレスコマンダの電池を交換してください", NULL);
		return ERROR;
	}
	return NOERR;
}


int	IR_repChk( BYTE *p, int cnt, BYTE ctrl )
{
	if( cnt == 8 &&
		p[ 0 ] == 0x00 && p[ 1 ] == 0x00 && p[ 2 ] == 0x00 &&
		p[ 3 ] == 0x00 && p[ 4 ] == 0x00 && p[ 5 ] == 0x96 &&
		p[ 6 ] == 0x82 && p[ 7 ] == ctrl ){
		return 0;
	}
	return 1;
}

int	IR_rlastBlock( int blockNo, BYTE *p, int cnt )
{
    BYTE	headr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x81 };
    int		i;
    WORD	sum;

	if( cnt < 15 ){
		// ヘッダ長不足
		return 2;
	}
	for( i = 0; i < sizeof( headr ); i++ ){
		if( p[ i ] != headr[ i ] ){
			// ヘッダ部異常
			return 3;
		}
	}
	if( cnt != 15 + *(WORD *)( p + 13 ) + 2 ){
		// データ長異常
		return 4;
	}
	for( i = sum = 0; i < *(WORD *)( p + 13 ); i++ ){
		sum += *( p + 15 + i );
	}
	if( sum != *(WORD *)( p + 15 + *(WORD *)( p + 13 ) ) ){
		// チェックサム エラー
		return 5;
	}
	if( *(WORD *)( p + 8 ) == 0xffff ){
		// 最終ブロックである
		return 1;
	}
	if( *(WORD *)( p + 8 ) != blockNo ){
		// ブロックNo.エラー
		return 6;
	}
	return 0;
}

int	IR_waitENQ( int irPort )
{
    int		i, rcnt, ret;

	for( i = 0; i < 3000; i++ ){
		// 右クリック中断
		if ( IR_checkBreak() != NOERR ){
			return 2;
		}
		ret = rmcPutZaurus( irPort, NULL, 0, repBuf, ZRCVMAX, &rcnt );
		if( ret!=0 && ret < 10 ){
//			printf( "\nENQ待ち時" );
			WL_recRBuf( rcnt );
			DM_dispCount( 0 );
			return 1;
		}
		if( !rcnt )
			continue;
		if( !IR_repChk( repBuf, rcnt, ENQ ) )
			return( 0 );
	}
//	printf( "\nENQ待ちﾀｲﾑｱｳﾄ" );
	return 1;
}


// 同期受信
int		IR_syncRead( void )
{
    int		ret, ret2, cnt, rcnt, i, j;

	DM_showMsg( "◆ オプション受信モード (右クリックで中断)◆", "(Wizから送信して下さい)" );
	DM_dispCount( -1 );	// カウンタクリア

	/*	コマンド送信		*/
	ret = rmcCommand ( irPort, 0x74 );	/* 最初のコマンド	*/
	if( ret == 1 ){
		/* 非接続	*/
		irPort = ( irPort == RIGHT )? LEFT : RIGHT;
		ret = rmcCommand ( irPort, 0x74 );
	}
	if( ret != 0 ){
		goto loop1;
	}

	ret = rmcSendByte( irPort, 0x01 );	/* 二番目のコマンド	 */
	if( ret != 0 ){
		goto loop1;
	}

	/*	データ受信	*/
	for( i = 0; i < BLOCKMAX; i++ ){
		for( j = 0; j < RETRY; j++ ){
loop3:
			if( (ret=IR_waitENQ( irPort ))!=0 ){
				/* wait ENQ code */
//				printf( "\nENQ受信時" );
				if ( ret==2 ){
					// 右クリック中断
					return ERROR;
				}
				goto loop1;
			}
			/* put SYN & data get*/
			do ret = rmcPutZaurus( irPort, synDat, 8,
									irBuf, ZRCVMAX, &cnt );
			while( ret == -1 );

			if( ret!=0 && ret < 10 ){
				/* 13,14はリトライ可能 */
//				printf( "\nSYN送信時" );
//				zdumpData( irBuf, cnt );
				WL_recBuf( cnt );
				DM_dispCount( 0 );
				goto loop1;
			}

			if( !IR_repChk( irBuf, cnt, ENQ ) ){
				goto loop3;
			}
			/* data check */
			if( !ret ){
				ret2 = IR_rlastBlock( i+1, irBuf, cnt );
				/*	ブロックNo.エラー	*/
				if( ret2 == 6  && *(WORD *)( irBuf + 8 ) < i + 1 ){
					/*	リトライ時に前ブロック再受信	*/
					/* 途中 ack */		
					do  ret = rmcPutZaurus( irPort, ackDat, 8, repBuf,
											ZRCVMAX, &rcnt );
						while( ret == -1 );

					if( ret!=0 && ret < 10 ){
//						printf( "\nACK送信時");
						goto loop1;
					}
					continue;
				}
			}
//※			if( ret || ret2 > 1 )
			if( (ret || ret2 > 1) && ret2!=3 ){
//				printf("retry: %d %d\n", ret, ret2 );
				if( j == RETRY - 1 ){
					/* リトライオーバーCAN */
					do  ret = rmcPutZaurus( irPort, canDat, 8, repBuf,
											ZRCVMAX, &rcnt );
						while( ret == -1 );
//					printf( "\n受信リトライエラー" );
//					zdumpData( irBuf, cnt );
					WL_recBuf( cnt );
					DM_dispCount( 0 );
					goto loop1;
				}
				/* データエラー NACK */
				do  ret = rmcPutZaurus( irPort, nacDat,
										8, irBuf, ZRCVMAX, &cnt );
					while( ret == -1 );
				if( ret!=0 && ret < 10 ){
//					printf( "\nNACK送信時" );
					DM_dispCount( 0 );
					goto loop1;
				}
				continue;
			}

//			printf( "\nブロック[%d] 受信 (Len=%d)", i + 1, cnt );
//			printf( "[%d] ", i + 1);
			WL_recBuf( cnt );
			DM_dispCount( 0 );

			if( ret2 == 0 ){
				/* 非最終ブロック OK    */
				/* 途中 ack */
				do  ret = rmcPutZaurus( irPort, ackDat,
										8, repBuf, ZRCVMAX, &rcnt );
					while( ret == -1 );

				if( ret!=0 && ret < 10 ){
//					printf( "\nACK送信時" );
					DM_dispCount( 0 );
					goto loop1;
				}
				break;
			} else {
				/* 最終ブロック OK	     */
				/* 最後 ack */
				do  ret = rmcPutZaurus( irPort, ackDat,
										8, repBuf, ZRCVMAX, &rcnt );
					while( ret == -1 );

				if( ret!=0 ){
//					printf( "\nACK送信時" );
					DM_dispCount( 0 );
					goto loop1;
				}
				goto loop7;
			}
		}
	}
loop7:
	/*	受信処理終了	    */
	if( rmcRecieveStop( irPort ) ){
		IR_dispErrWC( 1 );	// 『ワイヤレスコマンダがはずされました』表示
		return ERROR;
	}

loop1:
	if( rmcSendStop( irPort ) ){
		IR_dispErrWC( 1 );	// 『ワイヤレスコマンダがはずされました』表示
		return ERROR;
	}
	return NOERR;
}

// 同報受信

int	IR_onsideRead( void )
{
    int		ret, cnt, i;

	DM_showMsg( "◆ 光受信モード ◆", "(Wizの『光送信』をクリックして下さい)" );
	DM_dispCount( -1 );	// カウンタクリア

	/*	コマンド送信		*/
	ret = rmcCommand ( irPort, 0x74 );	/* 最初のコマンド	*/
	if( ret == 1 ){
		/* 非接続	*/
		irPort = ( irPort == RIGHT )? LEFT : RIGHT;
		ret = rmcCommand ( irPort, 0x74 );
	}
	if( ret != 0 ){
		goto loop1;
	}

	ret = rmcSendByte( irPort, 0x01 );	/* 二番目のコマンド	 */
	if( ret != 0 ){
		goto loop1;
	}

	/*	データ受信	*/
	for( i = 0; i < BLOCKMAX; i++ ){
		// 右クリック中断
		if ( IR_checkBreak() != NOERR ){
			return ERROR;
		}
		ret = rmcGetZaurus( irPort, irBuf, ZRCVMAX, &cnt);
		if ( ret != 0 || !cnt ){
//			printf(" ret=%d, cnt=%d\n", ret, cnt);
			if( cnt ){
				WL_recBuf( cnt );
			}
			goto loop1;
		}
		switch( IR_rlastBlock(i+1, irBuf, cnt) ){
			case 0:
//				zdumpData( irBuf, cnt );
//				printf( "\nブロック %d 受信", i + 1 );
				WL_recBuf( cnt );
				DM_dispCount( 0 );
				continue;
			case 1:
//				zdumpData( irBuf, cnt );
//				printf( "\nブロック %d 受信", i + 1 );
				WL_recBuf( cnt );
				DM_dispCount( 0 );
				goto inEnd;
			default:
				goto loop1;
		}
	}
inEnd:
	/*	受信処理終了	    */
	if( rmcRecieveStop( irPort ) ){
		IR_dispErrWC( 1 );	// 『ワイヤレスコマンダがはずされました』表示
		return ERROR;
	}
loop1:
	if( rmcSendStop( irPort ) ){
		IR_dispErrWC( 1 );	// 『ワイヤレスコマンダがはずされました』表示
		return ERROR;
	}
	return NOERR;
}

int	waitSYN( int port )		/* ENQ送信 & SYN受信 */
{
    int		i, j, rcnt, ret;

	for( i = 0; i < (60 * 2 * 3); i++ ){	/* 3分間でタイムアウトに    */
											/*(=(500msec * 2)*60*3秒) */
		// 右クリック中断
		if ( IR_checkBreak() != NOERR ){
			return 2;
		}
		ret = rmcPutZaurus( port, enqDat, 8, repBuf, ZRCVMAX, &rcnt );
		if( ret!=0 && ret < 10 ){	/* 100ミリ秒待ち    */
//			printf( "\nENQ送信時" );
			return 1;
		}
		if( ret == -1 || ret == 5 ){
//			printf( "\nENQ送信時 送信衝突" );
//			zdumpData( repBuf, rcnt );
			return 1;
		}
		if( !ret && rcnt ){
			if( !IR_repChk( repBuf, rcnt, SYN ) ){
				return 0;
			}

			if( !IR_repChk( repBuf, rcnt, ENQ ) ){
//				printf( "\nENQ送信時 ENQ衝突" );
				return 1;
			}
			if( !IR_repChk( repBuf, rcnt, CAN ) ){
//				printf( "\nENQ送信時 CAN受信" );
				return 1;
			}
//			printf( "\nSYN受信時 無効ﾃﾞｰﾀ" );
//			zdumpData( repBuf, rcnt );
		}
		for( j = 0; j < 400 * 5; j++ ){		/* 400ミリ秒待ち    */
			wait200mic();					/* (200マイクロ秒待ち*5*400) */
		}
	}
//	printf( "\nSYN待ちﾀｲﾑｱｳﾄ" );
	return 1;
}

// 同報送信

int	IR_onsideSend( void )
{
    int		ret, cnt, i, j, size, max;

	DM_showMsg( "◆ 光送信モード (右クリックで中断)◆", "(Wizの『光受信』をクリックして下さい)" );
	DM_dispCount( -1 );	// カウンタクリア
	max = (termSize / 512 )+1;	// 全転送回数

	ret = rmcCommand ( irPort, 0x74 );	/* 最初のコマンド	*/
	if( ret!=0 ){
		goto loop1;
	}
	ret = rmcSendByte( irPort, 0x01 );	/* 次のコマンド	*/
	if( ret!=0 ){
		goto loop1;
	}

	WL_mkSendBuf(-1);	// 転送前処理

	for( i = 0; i < BLOCKMAX; i++ ){
		if ( (size = WL_mkSendBuf(1)) < 1 ){
			break;
		}
		for( j = 0; j < 500 * 5; j++ ){
			// 右クリック中断
			if ( IR_checkBreak() != NOERR ){
				return ERROR;
			}
			wait200mic();		/* 200マイクロ秒待ち    */
		}
//		printf( "\nﾌﾞﾛｯｸ[%d]を送っています｡", i + 1 );
		DM_dispCount( max );

		ret = rmcPutZaurus( irPort, irBuf, size, repBuf, 16, &cnt );
		if ( ret != 0 ){
			if( rmcRecieveStop( irPort ) ){
				IR_dispErrWC( 1 );	// 『ワイヤレスコマンダがはずされました』表示
			}
			break;
		}
	}
loop1:
	if( rmcSendStop( irPort ) ){
		IR_dispErrWC( 1 );	// 『ワイヤレスコマンダがはずされました』表示
		return ERROR;
	}
	return NOERR;
}

// 同期送信
int		IR_syncSend( void )
{
    int		ret, rcnt, i, j, size, max;

	DM_showMsg( "◆ オプション送信モード (右クリックで中断)◆", "(Wizを受信状態にして下さい)" );
	DM_dispCount( -1 );	// カウンタクリア
	max = (termSize / 512 )+1;	// 全転送回数を算出

	ret = rmcCommand ( irPort, 0x74 );	/* 最初のコマンド	*/
	if( ret!=0 ){
		goto loop1;
	}
	ret = rmcSendByte( irPort, 0x01 );	/* 次のコマンド	*/
	if( ret!=0 ){
		goto loop1;
	}

	WL_mkSendBuf(-1);	// 転送前処理

	for( i = 0; i < BLOCKMAX; i++ ){
		if ( (size = WL_mkSendBuf(1)) < 1 ){
			break;
		}
//		printf( "\nﾌﾞﾛｯｸ[%d]を送っています｡", i + 1 );
		DM_dispCount( max );

		for( j = 0; j < RETRY; j++ ){
			// 右クリック中断
			if ( IR_checkBreak() != NOERR ){
				return ERROR;
			}
			if( (ret=waitSYN( irPort ))!=0 ){	/* ENQ送信 & SYN受信 */
				if ( ret==2 ){
					// 右クリック中断
					return ERROR;
				}
				goto loop1;
			}
			do {
				// 右クリック中断
				if ( IR_checkBreak() != NOERR ){
					return ERROR;
				}
				/* ﾃﾞｰﾀ送信 & 応答受信 */
				ret = rmcPutZaurus( irPort, irBuf, size, repBuf,
									ZRCVMAX, &rcnt );
				if( ret == -1 ){
//					printf( "\nﾃﾞｰﾀ送信時､ﾃﾞｰﾀ送出前に受信ﾃﾞｰﾀがあった" );
				}
			} while( ret == -1 );
			if( ret == 13 || ret == 14 ){
				continue;
			}
			if( ret!=0 && ret != 3 && ret != 4 ){
//				printf( "\nﾃﾞｰﾀ送信時" );
				goto loop1;
			}
			if( !rcnt ){
//				printf( "\n送信ﾘﾄﾗｲ" );
				continue;
			}
			if( !IR_repChk( repBuf, rcnt, ACK  ) ){
				break;
			}
			if( !IR_repChk( repBuf, rcnt, CAN  ) ){
				goto loop1;
			}

			if( j < RETRY - 1 ){
//				printf( "\nACK待ち無効ﾃﾞｰﾀ" );
				continue;
			}

			/* ﾘﾄﾗｲｵｰﾊﾞｰ CAN */
			do {
				ret = rmcPutZaurus( irPort, canDat, 8,
									repBuf, ZRCVMAX, &rcnt );
			} while( ret == -1 );
//			printf( "\nACK受信ﾘﾄﾗｲ ｴﾗｰ" );
			goto loop1;
		}
	}
loop1:
	if( rmcSendStop( irPort ) ){
		IR_dispErrWC( 1 );	// 『ワイヤレスコマンダがはずされました』表示
		return ERROR;
	}
	return NOERR;
}
