#import "Player.h"
#import "PlayerSpeaker.h"
#import "PlayerListener.h"
#import "PileOfChips.h"
#import "Random.h"
#include <mach.h>
#import <libc.h>
#import <sys/param.h>
#import <objc/NXStringTable.h>
#import <appkit/TextField.h>
#import <appkit/Application.h>
#import <appkit/SliderCell.h>
#import <appkit/Matrix.h>
#import <appkit/ButtonCell.h>
#import <appkit/Panel.h>
#import <appkit/Form.h>
#import <appkit/publicWraps.h>
#import <mp.h>

@implementation Player


- init
{
    [super init];
    [mySetPaymentButton setType:NX_TOGGLE];
    [mySetChoiceButton setType:NX_TOGGLE];
    [mySlider setEnabled:NO];
    [mySetChoiceButton setEnabled:NO];
    [mySetPaymentButton setEnabled:NO];
    [myRadioButton setEnabled:NO];
    [hisRadioButton setEnabled:NO];
    [hisSidePaymentToMeBox setEditable:NO];
    [hisSidePaymentToMeBox setSelectable:NO];
    [mySidePaymentToHimBox setEditable:NO];
    [mySidePaymentToHimBox setSelectable:NO];
    [messageBox setEditable:NO];
    [messageBox setSelectable:NO];
    [myPayoffBox setEditable:NO];
    [myPayoffBox setSelectable:NO];
    [myTotalWinningsBox setEditable:NO];
    [myTotalWinningsBox setSelectable:NO];
    [myTotalWinningsChips setChips:10];
    [myTotalWinningsBox setIntValue:10];
    icon[1] = "1";
    icon[2] = "2";
    icon[3] = "3";
    icon[4] = "4";
    icon[5] = "5";
    icon[6] = "6";
    icon[7] = "7";
    icon[8] = "8";
    icon[9] = "9";
    iconH[1] = "1H";
    iconH[2] = "2H";
    iconH[3] = "3H";
    iconH[4] = "4H";
    iconH[5] = "5H";
    iconH[6] = "6H";
    iconH[7] = "7H";
    iconH[8] = "8H";
    iconH[9] = "9H";
    inPhase1 = YES;
    myTotalWinnings = 0;
    demoMode = NO;
    return self;
}


/* called to start a new round */
- initNewGame
{
    double              sliderMax = 10.0;

    heHasSetPayment =
      IHaveSetPayment =
      heHasSetChoice =
      IHaveSetChoice =
      BothSidePaymentsSet =
      BothChoicesMade = alreadyDisplayed = NO;
    mySidePaymentToHim = hisSidePaymentToMe = 0;
    [potPileOfChips setChips:15];
    if (!inPhase1) {
	[mySlider setEnabled:YES];
	[mySlider setIntValue:0];
	if (myTotalWinnings < 10)
	    sliderMax = myTotalWinnings;
	[mySlider setMaxValue:sliderMax];
	[mySetPaymentButton setEnabled:YES];
	[mySetPaymentButton setState:0];
    }
    [mySetChoiceButton setState:0];
    [myRadioButton allowEmptySel:YES];
    [myRadioButton selectCellAt:-1:-1];	/* turn off both cells */
    [myRadioButton setEnabled:YES];
    [hisRadioButton allowEmptySel:YES];
    [hisRadioButton selectCellAt:-1:-1];
    [hisRadioButton setEnabled:NO];
    [hisSidePaymentToMeBox setIntValue:0];
    [hisSidePaymentToMeChips setChips:0];
    [mySidePaymentToHimBox setIntValue:0];
    [mySidePaymentToHimChips setChips:0];
    [messageBox setStringValue:BLANK];
    return self;
}

- playerReset;
{
    [myPayoffBox setIntValue:0];
    [myTotalWinningsChips setChips:0];
    [myTotalWinningsBox setIntValue:0];
    return self;
}

- setMyCards:(int)myPushValue :(int)myPullValue
{
    [[myRadioButton cellAt:0 :0] setIcon:icon[myPullValue]];
    [[myRadioButton cellAt:0 :1] setIcon:icon[myPushValue]];
    [[myRadioButton cellAt:0 :0] setAltIcon:iconH[myPullValue]];
    [[myRadioButton cellAt:0 :1] setAltIcon:iconH[myPushValue]];
    return self;
}

- setHisCards:(int)hisPushValue :(int)hisPullValue
{
    [[hisRadioButton cellAt:0 :0] setIcon:icon[hisPullValue]];
    [[hisRadioButton cellAt:0 :1] setIcon:icon[hisPushValue]];
    [[hisRadioButton cellAt:0 :0] setAltIcon:iconH[hisPullValue]];
    [[hisRadioButton cellAt:0 :1] setAltIcon:iconH[hisPushValue]];
    return self;

}

- (int)payoffMatrix:(int)mychoice :(int)hischoice
{
    int                 payoff = 0;

    if (mychoice == 1 && hischoice == 1)
	payoff = hispushvalue;
    if (mychoice == 0 && hischoice == 0)
	payoff = pullvalue;
    if (mychoice == 1 && hischoice == 0)
	payoff = 0;
    if (mychoice == 0 && hischoice == 1)
	payoff = pullvalue + hispushvalue;
    return payoff;
}

- playGameWith:(char *)opponentName atHost:(char *)opponentHost
    myPullCard:(int)mypull
    myPushCard:(int)mypush
    hisPullCard:(int)hispull
    hisPushCard:(int)hispush;

{
    hisName = NXCopyStringBuffer(opponentName);
    hisHost = NXCopyStringBuffer(opponentHost);
    [self initNewGame];
    pushvalue = mypush;
    pullvalue = mypull;
    hispullvalue = hispull;
    hispushvalue = hispush;
    [self setMyCards:pushvalue :pullvalue];
    [self setHisCards:hispushvalue :hispullvalue];
/* don't enable choice button until payment has been set by both players */
    if (inPhase1)
	[mySetChoiceButton setEnabled:YES];
    if (!inPhase1) {
	[mySetPaymentButton setEnabled:YES];
	[mySlider setEnabled:YES];
    }
    NXBeep();
    if (!demoMode)
	NXRunAlertPanel("New round...",
			"New round is beginning.\n", "OK", 0, 0, NULL);

    return self;
}

- playRandomGameWith:(char *)opponentName atHost:(char *)opponentHost
    myPullCard:(int)mypull
    myPushCard:(int)mypush
    hisPullCard:(int)hispull
    hisPushCard:(int)hispush;

{
    Random             *roller;

    demoMode = YES;
    roller = [[Random alloc] init];
    [self playGameWith:opponentName atHost:opponentHost
     myPullCard:mypull
     myPushCard:mypush
     hisPullCard:hispull
     hisPushCard:hispush];
    /* may want to insert delay here in random test mode */ 
    if (!inPhase1) {
	[mySlider setIntValue:[roller randMin:1 max:10]];
	[self takeSidePaymentFrom:mySlider];
	[mySetPaymentButton performClick:self];
    }
    [myRadioButton selectCellAt:0 :[roller randMax:1]];
    [mySetChoiceButton setEnabled: YES];
    [mySetChoiceButton performClick:self];
    return self;
}

- displaySidePayments
{
    if (heHasSetPayment && IHaveSetPayment) {
	[hisSidePaymentToMeBox setIntValue:hisSidePaymentToMe];
	[hisSidePaymentToMeChips setChips:hisSidePaymentToMe];
	if (hisSidePaymentToMe == 0)
	    NXRunAlertPanel("Payment set...",
			    "The other player has set a payment to you of 0 chips.\n", "OK", 0, 0, NULL);
	BothSidePaymentsSet = YES;
	[mySetChoiceButton setEnabled:YES];
    }
    return self;
}


- displayChoice
{

    if (heHasSetChoice && IHaveSetChoice) {
	BothChoicesMade = YES;
	[hisRadioButton selectCellAt:0 :hisChoice];
	[self computePayoffs];
    }
    return self;
}

- displayPayoffs
{
    int                 speakerResult;
    port_t              refereePort;

    [hisRadioButton selectCellAt:0 :hisChoice];
    [myRadioButton setEnabled:NO];
    [myPayoffBox setIntValue:myPayoff];
    [myTotalWinningsChips setChips:myTotalWinnings];
    [myTotalWinningsBox setIntValue:myTotalWinnings];
    switch (myChoice) {
    case 0:
	{
	    switch (hisChoice) {
	    case 0:
		[messageBox setStringValue:"You pulled; other player pulled."];
		break;
	    case 1:
		[messageBox setStringValue:"You pulled; other player pushed."];
		break;
	    }
	    break;
	}
    case 1:
	{
	    switch (hisChoice) {
	    case 0:
		[messageBox setStringValue:"You pushed; other player pulled."];
		break;
	    case 1:
		[messageBox setStringValue:"You pushed; other player pushed."];
		break;
	    }
	    break;
	}
    }
    refereePort = [self getPortFor:"Referee" atHost:refereeHost];
    speakerResult = [[NXApp appSpeaker] remoteReportGameInfoFrom:myPlayerNumber
		     side1:mySidePaymentToHim side2:hisSidePaymentToMe
		     push1:pushvalue pull1:pullvalue
		     push2:hispushvalue pull2:hispullvalue
		     choice1:myChoice choice2:hisChoice
		     payoff:myPayoff];
    if (speakerResult != 0)
	[self speakerError:"Referee" in:"displayPayoffs" error:speakerResult];
/***********************************************	
    if (!demoMode)
	NXRunAlertPanel("Round finished...",
			"Round is done; please click on OK button and wait for other players to finish.\n", "OK", 0, 0, NULL); 
***********************************************/	
			
    return self;
}

- computePayoffs
{
    myPayoff = [self payoffMatrix:myChoice :hisChoice];
    if (myChoice == 1)
	myPayoff = myPayoff + hisSidePaymentToMe;
    if (hisChoice == 1)
	myPayoff = myPayoff - mySidePaymentToHim;
    myTotalWinnings = myTotalWinnings + myPayoff;
    [self displayPayoffs];
    return self;
}


- takeSidePaymentFrom:sender	/* sender is the slider */
{
    int                 temp;

    mySidePaymentToHim = [sender intValue];
    [mySidePaymentToHimBox setIntValue:mySidePaymentToHim];
    [mySidePaymentToHimChips setChips:mySidePaymentToHim];
    temp = myTotalWinnings - mySidePaymentToHim;
    [myTotalWinningsBox setIntValue:temp];
    [myTotalWinningsChips setChips:temp];
    return self;
}

/* send info about player to Refeee */
- reportPlayerInfo:sender
{
    port_t              refereePort;
    int                 speakerResult;

    refereePort = [self getPortFor:"Referee" atHost:refereeHost];
    speakerResult = [[NXApp appSpeaker]
		     remoteReportPlayerInfo:myPlayerNumber
		     Name:[myInfoForm stringValueAt:0]
		     Phone:[myInfoForm stringValueAt:1]
		     SS:[myInfoForm stringValueAt:2]];
    if (speakerResult != 0)
	[self speakerError:"Referee" in:"reportPlayerInfo" error:speakerResult];
    [myInfoPanel close];
    return self;
}

- reportSidepayment:sender
{
    int                 speakerResult;
    port_t              hisPort;
    port_t              refereePort;

    hisPort = [self getPortFor:hisName atHost:hisHost];
    speakerResult = [[NXApp appSpeaker] remoteMySidePaymentIs:mySidePaymentToHim];
    if (speakerResult != 0)
	[self speakerError:hisName in:"reportSidepayment" error:speakerResult];
    IHaveSetPayment = YES;
    [mySlider setEnabled:NO];
    [mySetPaymentButton setEnabled:NO];
    [self displaySidePayments];
    refereePort = [self getPortFor:"Referee" atHost:refereeHost];
    speakerResult = [[NXApp appSpeaker] remoteReportPaymentBy:myPlayerNumber
		     amount:mySidePaymentToHim];
    if (speakerResult != 0)
	[self speakerError:"Referee" in:"reportSidepayment" error:speakerResult];

    return self;
}


- mySidePaymentIs:(int)sidePayment
{
    hisSidePaymentToMe = sidePayment;
    heHasSetPayment = YES;
    [self displaySidePayments];
    return self;
}

- myChoiceIs:(int)choice
{
    hisChoice = choice;
    heHasSetChoice = YES;
    if (IHaveSetChoice)
	[self displayChoice];
    return self;
}

- reportChoice:sender		/* connect to set choice button */
{
    int                 speakerResult;
    port_t              hisPort;
    port_t              refereePort;

    if (!IHaveSetPayment && !inPhase1) {
	NXRunAlertPanel("Choice button...",
			"You must first set payment.\n", "OK", 0, 0, NULL);
	return self;
    };
    myChoice = [myRadioButton selectedCol];
    if (myChoice < 0) {
	NXRunAlertPanel("Choice button...",
	      "You must first choose to push or pull.\n", "OK", 0, 0, NULL);
	return self;
    }
    [mySetChoiceButton setEnabled:NO];
    [myRadioButton setEnabled:NO];

/*tell other player your choice */
    hisPort = [self getPortFor:hisName atHost:hisHost];
    speakerResult = [[NXApp appSpeaker] remoteMyChoiceIs:myChoice];
    if (speakerResult != 0)
	[self speakerError:hisName in:"reportChoice" error:speakerResult];
    IHaveSetChoice = YES;

/*tell Referee choice */
    refereePort = [self getPortFor:"Referee" atHost:refereeHost];
    if (myChoice == 0)
	speakerResult = [[NXApp appSpeaker] remoteReportChoiceBy:myPlayerNumber
			 cardPlayed:pullvalue];
    else
	speakerResult = [[NXApp appSpeaker] remoteReportChoiceBy:myPlayerNumber
			 cardPlayed:pushvalue];
    if (speakerResult != 0)
	[self speakerError:"Referee" in:"reportChoice" error:speakerResult];

/*if both players have set choice, display choice */
    if (heHasSetChoice == YES)
	[self displayChoice];
    return self;
}

- refereeHostIs:(char *)name
{
    refereeHost = name;
    return self;
}

- startPhaseTwo
{
    if (!inPhase1)
	return self;
    if (!demoMode)
	NXRunAlertPanel("End of phase 1...",
			"Phase 1 is over; please read instructions for next phase.\n", "OK", 0, 0, NULL);
    inPhase1 = NO;
    [coverChips free];
    [coverSlider free];
    [coverButton free];
    [mySlider setEnabled:YES];
    [mySetPaymentButton setEnabled:YES];
    [mySetChoiceButton setEnabled:NO];
    [mySidePaymentToHimBox display];
    [hisSidePaymentToMeBox display];
    [mySidePaymentToHimLabel display];
    [hisSidePaymentToMeLabel display];
    [mySlider display];
    [mySetPaymentButton display];
    [mySlider display];
    IHaveSetChoice = heHasSetChoice = NO;
    IHaveSetPayment = heHasSetPayment = NO;
    return self;
}

- playerNumberIs:(int)num
{
    myPlayerNumber = num;
    return self;
}

- nudge
{
    if (!IHaveSetPayment && !inPhase1) {
	NXBeep();
	NXRunAlertPanel("Confirm payment...", "Please choose payment and press confirm payment button.\n", "OK", 0, 0);
	return self;
    }
    if (!IHaveSetChoice) {
	NXBeep();
	NXRunAlertPanel("Confirm choice...", "Please make choice and press confirm choice button.\n", "OK", 0, 0);
	return self;
    }
    return self;
}


/*----------------------------------------------------------------------*/
/* The following is boilerplate that can be inserted in any application */
/*----------------------------------------------------------------------*/
- (port_t)getPortFor:(char *)appName 
 atHost:(char *)appHost
{
    port_t              appPort;

    appPort = NXPortFromName(appName, appHost);
    if (appPort == PORT_NULL) {
	NXRunAlertPanel("Define...", "%s Port was not found.\n", 
			"OK", 0, 0, appName);
    }
    [[NXApp appSpeaker] setSendPort:appPort];
    return appPort;
} 

- speakerError:(char *)receiverName in:(char *)methodName error:(int)num 
{
    printf("Error...message to %s in method %s generated error code %d.\n",
	   receiverName, methodName, num);
    return self;
} 

@end
