/*
	File:		MBCLanguageModel.mm
	Contains:	Build and interpret speech recognition language model
	Version:	1.0
	Copyright:	© 2003 by Apple Computer, Inc., all rights reserved.

	File Ownership:

		DRI:				Matthias Neeracher    x43683

	Writers:

		(MN)	Matthias Neeracher

	Change History (most recent first):

		$Log: MBCLanguageModel.mm,v $
		Revision 1.2  2003/09/06 04:15:03  neerache
		Fixed wrong length for 'queen'
		
		Revision 1.1  2003/07/14 23:22:50  neerache
		Move to much smarter speech recognition model
		
*/

#import "MBCLanguageModel.h"
#import "MBCEngineCommands.h"

static const char sTakeback[] = "take back move";
static const char sUndo[]     = "undo";

static const char * sPieceName[] = {
	"", "king", "queen", "bishop", "knight", "rook", "pawn"
};

@implementation MBCLanguageModel

- (void) addTo:(SRLanguageObject)base languageObject:(SRLanguageObject)add
{
	SRAddLanguageObject(base, add);
	SRReleaseObject(add);
}

- (id) initWithRecognitionSystem:(SRRecognitionSystem)system
{
	int debug 	= getenv("MBC_DEBUG") ? atoi(getenv("MBC_DEBUG")) : 0;
	fDumpModels	= debug & 8;

	fSystem	= system;

	SRNewLanguageModel(fSystem, &fToModel, "to", 2);
	SRAddText(fToModel, "to", 2, 0);
	SRAddText(fToModel, "takes", 5, 0);

	SRNewPath(fSystem, &fPromotionModel);
	SRAddText(fPromotionModel, "promoting to", 12, 0);

	SRLanguageModel promoPieces;
	SRNewLanguageModel(fSystem, &promoPieces, "promotion", 9);
	SRAddText(promoPieces, "queen", 5, 	MBCEncodeMove("a1a1q", 0));
	SRAddText(promoPieces, "bishop", 6, MBCEncodeMove("a1a1b", 0)); 
	SRAddText(promoPieces, "knight", 6, MBCEncodeMove("a1a1n", 0)); 
	SRAddText(promoPieces, "rook", 4, 	MBCEncodeMove("a1a1r", 0)); 
	SRAddText(promoPieces, "king", 4, 	MBCEncodeMove("a1a1k", 0)); // Suicide
	[self addTo:fPromotionModel languageObject:promoPieces];

	Boolean opt = true;
	SRSetProperty(fPromotionModel, kSROptional, &opt, 1);

	return self;
}

- (SRLanguageObject) movesFrom:(MBCSquare)from to:(MBCBoardMask)mask 
						  pawn:(BOOL)pawn
{
	char 	move[5];
	move[0]		= Col(from);
	move[1]		= '0'+Row(from);
	move[2]		= 0;
	move[4]		= 0;

	SRPath	model;
	SRNewPath(fSystem, &model);
	SRAddText(model, move, 2, 0);
	SRAddLanguageObject(model, fToModel);

	SRLanguageModel	destinations;
	NSString *		pieceDest = [NSString stringWithFormat:@"%s to", move];
	SRNewLanguageModel(fSystem, &destinations, 
					   [pieceDest cString], [pieceDest length]);
	if (fDumpModels) {
		const char * seenDest = "";
		fprintf(stderr, "<%c%dmoves> =", Col(from), Row(from));
		for (MBCSquare to = Square("a1"); to<=Square("h8"); ++to)
			if (mask & (1llu << to)) {
				fprintf(stderr, " %s%c%d%s", seenDest, Col(to), Row(to),
						(pawn && (Row(to)==1 || Row(to)==8)) 
						? " <promotion>" : "");
				seenDest = "| ";
			}
		fprintf(stderr, " ;\n");
	}
	for (MBCSquare to = Square("a1"); to<=Square("h8"); ++to)
		if (mask & (1llu << to)) {
			move[2]	= Col(to);
			move[3]	= '0'+Row(to);
			if (pawn && (Row(to)==1 || Row(to)==8)) {
				SRPath	path;
				SRNewPath(fSystem, &path);
				SRAddText(path, move+2, 2, MBCEncodeMove(move, 0));
				SRAddLanguageObject(path, fPromotionModel);
				[self addTo:destinations languageObject:path];
			} else {
				SRAddText(destinations, move+2, 2, MBCEncodeMove(move, 0));
			}
		}
	[self addTo:model languageObject:destinations];

	return model;
}

- (SRLanguageObject) movesForPiece:(MBCPiece)piece
{
	SRPath			model;
	SRNewPath(fSystem, &model);
	SRAddText(model, sPieceName[piece], strlen(sPieceName[piece]), 0);

	SRLanguageModel	origins;
	NSString *		pieceOrigins = [NSString stringWithFormat:@"%s from",
											 sPieceName[piece]];
	SRNewLanguageModel(fSystem, &origins, 
					   [pieceOrigins cString], [pieceOrigins length]);

	MBCPieceMoves *	moves	= fMoves->fMoves+piece;
	if (fDumpModels) {
		fprintf(stderr, "<%smoves> = \n", sPieceName[piece]);
		for (int i = 0; i<moves->fNumInstances; ++i)
			fprintf(stderr, " %c %c%d <to> <%c%dmoves>\n", 
					i ? '|' : ' ', Col(moves->fFrom[i]), Row(moves->fFrom[i]),
					Col(moves->fFrom[i]), Row(moves->fFrom[i]));
		fprintf(stderr, " ;\n");
	}
	for (int i = 0; i<moves->fNumInstances; ++i)
		[self addTo:origins
			  languageObject:[self movesFrom:moves->fFrom[i] to:moves->fTo[i] 
								   pawn:piece==PAWN]];
	[self addTo:model languageObject:origins];
	
	return model;
}

- (SRLanguageObject) castles
{
	char move[5];
	MBCSquare king = fMoves->fMoves[KING].fFrom[0];
	move[0] = Col(king);
	move[1] = '0'+Row(king);
	move[3] = '0'+Row(king);
	move[4] = 0;
	
	if (fDumpModels) {
		fprintf(stderr, "<castles> = castle <castlesides> side ;\n");
		fprintf(stderr, "<castlesides> =%s%s ;\n",
				fMoves->fCastleKingside ? " king" : "",
				fMoves->fCastleQueenside ? " queen" : "");
	}

	SRPath			model;
	SRNewPath(fSystem, &model);
	SRAddText(model, "castle", 6, 0);	

	SRLanguageModel	sides;
	SRNewLanguageModel(fSystem, &sides, "castle", 6); 

	if (fMoves->fCastleKingside) {
		move[2]	= 'g';
		SRAddText(sides, "king", 4, MBCEncodeMove(move, 0));
	}
	if (fMoves->fCastleQueenside) {
		move[2]	= 'c';
		SRAddText(sides, "queen", 5, MBCEncodeMove(move, 0));
	}
   
	[self addTo:model languageObject:sides];
	SRAddText(model, "side", 4, 0);

	return model;
}

- (SRLanguageObject) pawnDrops
{
	char drop[5];
	drop[0] = fMoves->fWhiteMoves ? 'P' : 'p';
	drop[1] = '@';
	drop[4] = 0;
	
	if (fDumpModels) {	
		const char *	seenDest = "";
		fprintf(stderr, "<pawndrops> = drop pawn at <pawnat> ;\n");
		fprintf(stderr, "<pawnat> =");
		for (MBCSquare to = Square("a1"); to<=Square("h8"); ++to)
			if (fMoves->fPawnDrops & (1llu << to)) {
				fprintf(stderr, " %s%c%d", seenDest, Col(to), Row(to));
				seenDest = "| ";
			}
		fprintf(stderr, " ;\n");
	}

	SRPath			model;
	SRNewPath(fSystem, &model);
	SRAddText(model, "drop pawn at", 12, 0);	

	SRLanguageModel	destinations;
	SRNewLanguageModel(fSystem, &destinations, "pawnat", 6); 
   
	for (MBCSquare to = Square("a1"); to<=Square("h8"); ++to)
		if (fMoves->fPawnDrops & (1llu << to)) {
			drop[2]	= Col(to);
			drop[3]	= '0'+Row(to);
			SRAddText(destinations, drop+2, 2, MBCEncodeDrop(drop, 0));
		}
	[self addTo:model languageObject:destinations];

	return model;
}

- (SRLanguageObject) pieceDrops
{
	const char * 	pieceSym	= " KQBNRP  kqbnrp ";
	int				color 		= fMoves->fWhiteMoves ? 0 : 8;
	char drop[5];
	
	if (fDumpModels) {	
		const char *	seenPiece= "";
		const char *	seenDest = "";
		fprintf(stderr, "<piecedrops> = drop <droppiece> at <pieceat> ;\n");
		fprintf(stderr, "<droppiece> = ");
		for (MBCPiece piece = QUEEN; piece < PAWN; ++piece)
			if (fMoves->fDroppablePieces & (1 << piece)) {
				fprintf(stderr, " %s%s", seenPiece, sPieceName[piece]);
				seenPiece = "| ";
			}
		fprintf(stderr, "<pieceat> =");
		for (MBCSquare to = Square("a1"); to<=Square("h8"); ++to)
			if (fMoves->fPieceDrops & (1llu << to)) {
				fprintf(stderr, " %s%c%d", seenDest, Col(to), Row(to));
				seenDest = "| ";
			}
		fprintf(stderr, " ;\n");
	}

	SRPath			model;
	SRNewPath(fSystem, &model);
	SRAddText(model, "drop", 4, 0);	

	SRLanguageModel	pieces;
	SRNewLanguageModel(fSystem, &pieces, "droppiece", 9); 

	strcpy(drop, " @a1");
	for (MBCPiece piece = QUEEN; piece < PAWN; ++piece)
		if (fMoves->fDroppablePieces & (1 << piece)) {
			drop[0] = pieceSym[piece | color];
			SRAddText(pieces, sPieceName[piece], strlen(sPieceName[piece]),
					  MBCEncodeDrop(drop, 0));
		}

	SRLanguageModel	destinations;
	SRNewLanguageModel(fSystem, &destinations, "pieceat", 7);    

	strcpy(drop, " @a1");
	for (MBCSquare to = Square("a1"); to<=Square("h8"); ++to)
		if (fMoves->fPawnDrops & (1llu << to)) {
			drop[2]	= Col(to);
			drop[3]	= '0'+Row(to);
			SRAddText(destinations, drop+2, 2, MBCEncodeDrop(drop, 0));
		}

	[self addTo:model languageObject:pieces];
	SRAddText(model, "at", 2, 0);		
	[self addTo:model languageObject:destinations];

	return model;
}

- (void) buildLanguageModel:(SRLanguageModel)model 
				  fromMoves:(MBCMoveCollection *)moves
				   takeback:(BOOL)takeback
{
	if (fDumpModels) {
		fprintf(stderr, "<to> = to | takes;\n");
		fprintf(stderr, "<promotion> = promoting to <promoPiece>;\n");
		fprintf(stderr, "<promoPiece> = queen | bishop | knight | rook | king;\n"); 
	}

	fMoves		= moves;
	SREmptyLanguageObject(model);
	
	if (fDumpModels) {
		bool	seenPiece = false;

		fprintf(stderr, "\n<moves> = \n");
		for (MBCPiece piece = KING; piece <= PAWN; ++piece)
			if (moves->fMoves[piece].fNumInstances) {
				fprintf(stderr, " %c %s <%smoves>\n", seenPiece ? '|' : ' ',
						sPieceName[piece], sPieceName[piece]);
				seenPiece = true;
			}
		fprintf(stderr, " ;\n");
	}
	for (MBCPiece piece = KING; piece <= PAWN; ++piece)
		if (moves->fMoves[piece].fNumInstances)
			[self addTo:model languageObject:[self movesForPiece:piece]];
	if (moves->fCastleKingside || moves->fCastleQueenside)
		[self addTo:model languageObject:[self castles]];

	if (moves->fPawnDrops)
		[self addTo:model languageObject:[self pawnDrops]];
	if (moves->fPieceDrops)
		[self addTo:model languageObject:[self pieceDrops]];		

	if (takeback) {
		MBCCompactMove takeback = MBCEncodeTakeback();

		SRAddText(model, sTakeback, strlen(sTakeback), 	takeback);
		SRAddText(model, sUndo, 	strlen(sUndo), 		takeback);
	}
}

- (MBCMove *) recognizedMove:(SRRecognitionResult)result
{
	SRPath	resultPhrase;
	Size 	len = sizeof(resultPhrase);

	len = sizeof(resultPhrase);
	if (!SRGetProperty (result, kSRPathFormat, &resultPhrase, &len)) {
		MBCCompactMove	move	= 0;
		long			numWords;
		SRCountItems(resultPhrase, &numWords);

		//
		// We build up our move by ORing the individual refCons, which neatly
		// combines the semantic content of the contributing elements
		//
		while (numWords--) {
			SRLanguageObject 	resultWord;
			MBCCompactMove		wordMove;

			SRGetIndexedItem(resultPhrase, &resultWord, numWords);
			len = 4;
			if (!SRGetProperty(resultWord, kSRRefCon, &wordMove, &len))
				move |= wordMove;
			
			SRReleaseObject(resultWord);
		}
		SRReleaseObject (resultPhrase);

		return [MBCMove moveFromCompactMove:move];
	}
	return nil;
}

@end

// Local Variables:
// mode:ObjC
// End:
