// Cribbage.m
// Project: Cribbage
// Stephan Wacker
// 93-06-20


#import "deck.h"
#import "cribbage.h"


#import "Cribbage.h"

#import "Deck.h"
#import "ComputerPlayer.h"
#import "HumanPlayer.h"
#import "Preferences.h"
#import "Table.h"
#import "Score.h"
#import "LocalizableStrings.h"

#import <assert.h>


#define SCORE_NONDEALER_HAND	1
#define SCORE_DEALER_HAND	2
#define SCORE_DEALER_CRIB	3
#define SCORE_COMP_CRIB		4


@implementation Cribbage


- appDidInit: sender
{
    id		window = [table window];
    
    games = 0;
    [self new: self];
    [window setBackgroundColor: [Preferences defaultBackgroundColor]];
    [window makeKeyAndOrderFront: self];
    [[messages window] orderFront: self];
    
    return self;
}


- windowWillMiniaturize: sender toMiniwindow: miniwindow 
{
    [sender setMiniwindowIcon: "app"];
    return self;
}


- new: sender
{
    // [table new];
    
    done = NO;
    [deck makedeck];
    [comp startGame];
    [human startGame];
    
    if( games == 0 ) {
	[self prepareForFirstCut];
    } else {
	sprintf( local_buf, "Loser (%s) gets first crib",
	  [loser reflexivePronoun] );
	msg( LOCAL_BUF );
	[self prepareForDealing: loser];
    }
    
    return self;
}


- showInfo: sender
{
    if( !info && [NXApp loadNibSection: "info.nib" owner: self] == nil ) {
	return nil;
    }
    
    [info makeKeyAndOrderFront: nil];
    
    return self;
}


- showScore: sender
{
    if( !score && [NXApp loadNibSection: "score.nib" owner: self] == nil ) {
	return nil;
    } else {
	// Make sure the Done button is connected to the correct target.
	[[doneButton setTarget: score] setAction: @selector(ok:)];
	[human setScoreMgr: score];
    }
    
    [[score window] makeKeyAndOrderFront: nil];
    
    return self;
}


- showPreferences: sender
{
    if(   !preferences
       && [NXApp loadNibSection: "preferences.nib" owner: self] == nil ) {
	return nil;
    }
    
    [preferences run: nil];
    
    // Disable limit button if the game has already started.
    if( button != firstCutButton && button != newButton )
    {
	[preferences freezeLimit];
    }
    
    return self;
}


- preferences
{
    return preferences;
}


- updatePreferences
{
    id		window = [table window];
    
    if( !NXEqualColor( [preferences backgroundColor],
	[window backgroundColor] ) )
    {
	[window disableFlushWindow];
	[window setBackgroundColor: [preferences backgroundColor]];
	[window display];
	[[window reenableFlushWindow] flushWindow];
    }
    
    [comp changePegColor];
    [human changePegColor];
    
    return self;
}

////////////////////////////////////////////////////////////////////////


- prepareForFirstCut
{
    [self installButton: firstCutButton];
    
    msg( STR_WELCOME_TO_CRIBBAGE );
    msg( quiet ? STR_CUT_FOR_CRIB : STR_CUT_FOR_CRIB_VERBOSE );
    
    return self;
}


- firstCut: sender
{
    CARD	pCard, cCard;
    
    [deck shuffle];
    
    pCard = [human firstCut];
    addmsg(quiet ? STR_YOU_CUT : STR_YOU_CUT_VERBOSE);
    msgcard(pCard, FALSE);
    addmsg( CUT_POSTFIX );
    endmsg();

    cCard = [comp firstCut];
    addmsg(quiet ? STR_I_CUT : STR_I_CUT_VERBOSE);
    msgcard(cCard, FALSE);
    addmsg( CUT_POSTFIX );
    endmsg();

    if (pCard.rank == cCard.rank) {
	msg(quiet ? STR_WE_TIED : STR_WE_TIED_VERBOSE);
    }
    else {
	[self prepareForDealing: (pCard.rank > cCard.rank ? comp : human)];
    }

    return self;
}


- prepareForDealing: aPlayer
{
    [self installButton: dealButton];
    [preferences freezeLimit];
    
    [deck setDealer: aPlayer];
    
    strcpy( local_buf, [[deck dealer] pronoun] );
    strcat( local_buf, " deal" );
    if( aPlayer == nil ) {
	if( !quiet ) strcat( local_buf, " next and get the crib" );
    } else {
	if( !quiet ) strcat( local_buf, " first and get the first crib" );
    }
    msg( LOCAL_BUF );
    
    player = [deck nondealer];				// first player
    nextPlayer = [deck dealer];				// second player
    
    return self;
}


- deal: sender;
{
    [deck makedeck];
    [deck shuffle];
    
    [deck deal];

    [self prepareForDiscarding];

    return self;
}


- prepareForDiscarding
{
    [self installButton: discardButton];
    [button setEnabled: NO];

    [comp discard];
    [human prepareForDiscarding];
    
    if( !quiet ) {
	sprintf( local_buf, "It is %s crib",
		 [[deck dealer] possessivePronoun] );
	msg( LOCAL_BUF );
    }
    msg( quiet ? STR_DISCARD : STR_DISCARD_VERBOSE );
    
    return self;
}


- discard: sender
{
    [human discard];
    [self prepareForCut];
    
    return self;
}


- prepareForCut
{
    [self installButton: cutButton];
    if( [deck dealer] == comp ) {
	msg( STR_CUT_THE_DECK );
	[human disableHand];
    } else {
	// comp cuts automatically
	[button performClick: self];
    }
    
    return self;
}


- cut: sender
{
    [deck cut];
    if( !done ) [self prepareForPegging];
    
    return self;
}


- startPegging
{
    [table clearSum];
    [comp prepareForPegging];
    [human prepareForPegging];
    
    return self;
}


- prepareForPegging
{
    [self installButton: goButton];
    [self startPegging];
    return [self continueGame];
}


- continueGame
//
// Determine next step in game and execute it.
//
{
    if( done ) return self;
    
    if( [human anyCards] || [comp anyCards] ) {
	if( ([table sumPlayed] == 31) || ([player didGo]) ) {
	    [self startPegging];
	    if( player == lastPlayer ) {
		[self swapPlayers];
	    }
	}
	[player playOneCard];
    } else {
	if( [table sumPlayed] < 31 ) {
	    [self lastCard: lastPlayer];
	}
	if( !done ) [self prepareForScoring];
    }
    
    return self;
}


- prepareForScoring
{
    [table empty];
    scoreWhat = SCORE_NONDEALER_HAND;

    // This is not absolutely correct but it works.
    // We will either score the comp hand first (no button) and then the
    // human hand and crib; or we'll score a human hand first and replace
    // the button after scoring the comp hand.
    if( !score ) [self showScore: nil];
    [self installButton: doneButton];
    
    return [self continueScoring];
}


- continueScoring
{
    if( done ) return self;
    
    switch( scoreWhat++ )
    {
      case SCORE_NONDEALER_HAND:
	[[deck nondealer] scoreHand];
	break;
	
      case SCORE_DEALER_HAND:
	[[deck dealer] scoreHand];
	break;
	
      case SCORE_DEALER_CRIB:
	if( [deck dealer] == comp ) {
	    // Wait for acknowledgement.
	    [self installButton: okButton];
	    break;
	} else {
	    // It may be necessary to re-install the done button
	    // if the ok button was installed to acknowledge an
	    // explanation.
	    [self installButton: doneButton];
	    ++ scoreWhat;
	    // Fall through to next case.
	}
	
      case SCORE_COMP_CRIB:
	[[deck dealer] scoreCrib: [deck getCrib]];
	break;
	
      default: 
	[self prepareForDealing: nil];
	break;
    }
    
    return self;
}


- ok: sender
// Acknowledge computer's score or explanation.
{
    return [self continueScoring];
}


- didGo: aPlayer
{
    assert( player == aPlayer );
    
    if( ![nextPlayer didGo] ) {
	sprintf( local_buf, "%s GO", [aPlayer pronoun] );
	msg( LOCAL_BUF );
	[self swapPlayers];
    } else {
	strcpy( local_buf, [player pronoun] );
	strcat( local_buf, quiet ? " get one" : " get one point" );
	msg( LOCAL_BUF );
	[player peg: 1];
    }
    
    return [self continueGame];
}


- didPlay: aPlayer card: (CARD) aCard
{
    assert( player == aPlayer );
    
    if( aPlayer == human ) {
	[comp makeknown: aCard];
    }
    
    lastPlayer = aPlayer;
    [self swapPlayers];
    return [self continueGame];
}


- lastCard: aPlayer
{
    assert( lastPlayer == aPlayer );
    
    strcpy( local_buf, [aPlayer pronoun] );
    strcat( local_buf, quiet ? " get one" : " get one point" );
    addmsg( LOCAL_BUF );
    addmsg( STR_FOR_LAST );
    endmsg();
    [aPlayer peg: 1];
    
    return self;
}


- swapPlayers
{
    id		t;
    
    if( ! [nextPlayer didGo] ) {
	t = player;
	player = nextPlayer;
	nextPlayer = t;
    }
    
    return self;
}


- winner: aPlayer
{
    float	fraction;
    int		value;
    
    [[Sound findSoundFor: "winner"] play];
    
    done = YES;
    ++ games;
    loser = (aPlayer == human ? comp : human);
    fraction = (float) [loser score] / (float) [aPlayer score];
    
    strcpy( local_buf, [aPlayer pronoun] );
    if( fraction < 0.5 ) {
	sprintf( local_buf+strlen(local_buf), " double skunked %s!",
	  [loser reflexivePronoun] );
	value = 4;
    } else if( fraction < 0.75 ) {
	sprintf( local_buf+strlen(local_buf), " skunked %s!",
	  [loser reflexivePronoun] );
	value = 2;
    } else {
	strcat( local_buf, " won this game!" );
	value = 1;
    }
    msg( LOCAL_BUF );
    
    [aPlayer win: value];
    
    [human disableHand];
    [button setEnabled: NO];
    msg( STR_ANOTHER_GAME );
    [self installButton: newButton];
    [preferences unfreezeLimit];
    
    return self;
}


- wrongScore: aPlayer correct: (int) points incorrect: (int) claimed
{
    assert( aPlayer == human );		// comp doesn't err
    
    if( claimed > points ) {
	msg( STR_ONLY_X_POINTS, points );
	[aPlayer peg: points];
	if( !done ) {
	    [[Sound findSoundFor: "overscored"] play];
	    sprintf( local_buf, "%s get 2", [comp pronoun] );
	    msg( LOCAL_BUF );
	    [comp peg: 2];
	}
    } else {
	[aPlayer peg: claimed];
	if( !done ) {
	    sprintf( local_buf, "%s should have taken %%d, not %%d!",
		    [aPlayer pronoun] );
	    msg( LOCAL_BUF, points, claimed );
	    [[Sound findSoundFor: "underscored"] play];
	}
    }
    
    if( explain && !done ) {
	[self explainCurrentScore];
	[self installButton: okButton];
    } else {
	[self continueScoring];
    }
    strcpy( prev_expl, expl ); *expl = '\0';

    
    return self;
}


- explainCurrentScore
{
    if( *expl ) {
	msg( STR_EXPLANATION, expl );
	[[messages window] orderFront: nil];
	*expl = *prev_expl = '\0';
    }
    return self;
}


- explainPreviousScore
{
    if( *prev_expl ) {
	msg( STR_EXPLANATION, prev_expl );
	[[messages window] orderFront: nil];
	*prev_expl = '\0';
    }
    return self;
}


////////////////////////////////////////////////////////////////////////


- installButton: aButton
{
    NXRect	boxRect, buttonRect;
    
    [[buttonBox contentView] getFrame: &boxRect];
    [aButton getFrame: &buttonRect];
    
    // We should not replace a button while it is being clicked.
    // It's better to move the current buuton out of the visible area.
    [button moveTo: boxRect.origin.x + boxRect.size.width + 1 : 0];
    
    if( ![aButton isDescendantOf: buttonBox] ) {
	[buttonBox addSubview: aButton];
    }
    button = aButton;
    
    // Center button in box.
    NX_X(&buttonRect) = (NX_WIDTH(&boxRect) - NX_WIDTH(&buttonRect)) / 2.0;
    NX_Y(&buttonRect) = (NX_HEIGHT(&boxRect) - NX_HEIGHT(&buttonRect)) / 2.0;
    [aButton setFrame: &buttonRect];
    [aButton setEnabled: YES];
    
    [buttonBox display];
    
    return self;
}


@end
