import java.util.*;

import java.applet.Applet;

import java.awt.*;

import java.awt.event.*;

import java.io.*;



public class Poker extends Applet implements ActionListener, MouseMotionListener {

	final int CARD_BACK = 64,

			  START_DOLLARS = 100;

	

	String TableStatus = new String("");

	Timer Timer1;

	Font fnt;

	Font fntOdds, fntDollars;

	Deck Dealer;

	Hand Player1;

	

	String imgFileName;

	Image imgCards[];

	Image imgBack, imgOddsBack;

	MediaTracker imageTracker;



	Button cmdDeal, cmdDiscard, cmdBet1, cmdBet5;	

	Label lblBet;

	Panel controlPanel;

	

	Graphics gBuffer;  //used for graphics buffering

	Image imgBuffer;   //



	public void init() {	

		setBackground(new Color(255,255,255));



		Timer1 = new Timer(this, 400);

		Timer1.start();	

		

		fnt = new Font("Courier New", Font.BOLD, 18);

		fntOdds = new Font("Courier New", Font.PLAIN, 14);

		fntDollars = new Font("Courier New", Font.BOLD, 24);



		imgBuffer = createImage(600, 172);

		gBuffer = imgBuffer.getGraphics();



		cmdDeal = new Button("New Game");

		cmdDiscard = new Button("Discard");

		cmdBet1 = new Button("$1");

		cmdBet5 = new Button("$5");

		

		lblBet = new Label("Bet:0", lblBet.CENTER);

		lblBet.setFont(fnt);



		controlPanel = new Panel();

		controlPanel.setLayout(new GridLayout(1, 10));  

		controlPanel.add(new Label(""));		

		controlPanel.add(cmdDeal);

		controlPanel.add(cmdDiscard);		

		controlPanel.add(new Label(""));

		controlPanel.add(cmdBet1);

		controlPanel.add(cmdBet5);

		controlPanel.add(lblBet);

		controlPanel.add(new Label(""));

		

		

		setLayout(new BorderLayout());

		add(controlPanel, BorderLayout.SOUTH);



		cmdDeal.addActionListener(this);

		cmdDiscard.addActionListener(this);

		cmdBet1.addActionListener(this);

		cmdBet5.addActionListener(this);

		

		cmdDeal.requestFocus();

		cmdDiscard.setEnabled(false);

		cmdBet1.setEnabled(false);

		cmdBet5.setEnabled(false);



		addMouseListener(new MouseHandler(this));

		addMouseMotionListener(this);



		imgCards = new Image[66];

		imgFileName = new String();

		imageTracker = new MediaTracker(this);



		//Load images

		for(int i=1; i<imgCards.length; i++) {

			imgFileName = Integer.toString(i) + ".gif";

			imgCards[i] = getImage(getDocumentBase(), imgFileName);

			imageTracker.addImage(imgCards[i], i);

			

			showStatus("Loading " + imgFileName + "...");

			try {

				imageTracker.waitForID(i);

			}

			catch(InterruptedException e) {}

		}



		imgBack = getImage(getDocumentBase(), "Back.gif");

		imgOddsBack = getImage(getDocumentBase(), "oddsback.gif");

		imageTracker.addImage(imgBack, 100);		

		imageTracker.addImage(imgOddsBack, 101);		

		try {

			showStatus("Loading Back.gif...");

			imageTracker.waitForID(100);

			showStatus("Loading OddsBack.gif...");

			imageTracker.waitForID(101);

		}

		catch(InterruptedException e) {}



		Dealer = new Deck();				

		Player1 = new Hand(5);		

		Player1.setDollars(0);

		for(int i=0; i<5; i++) {					

			Player1.setCard(i, CARD_BACK);	 

			Player1.setCardStatus(i, Player1.STATUS_DISCARD);

		}

	}





	public void paint(Graphics g) {

		//Draw rectangles

		gBuffer.setColor(Color.white);

		gBuffer.fill3DRect(443, 0, 156, 172, true);

		gBuffer.fill3DRect(0, 0, 442, 172, true);

		gBuffer.setColor(Color.black);		

		gBuffer.setFont(fnt);

		

		//Draw background image

		gBuffer.drawImage(imgBack, 0, 0, this);

		gBuffer.drawImage(imgOddsBack, 443, 0, this);



		//Draw cards

		for(int i=0; i<5; i++) {

			gBuffer.drawImage(imgCards[Player1.getCard(i)], i*80 + 25, 25, this);			



			if(Player1.getCardStatus(i) == Player1.STATUS_HOLD) 

				if(Timer1.isEnabled() == false)

					gBuffer.drawString("HOLD", i*80 + 40, 145);

		}

		

		//Draw the odds

		gBuffer.setFont(fntOdds);

		gBuffer.drawString("Two Of A Kind...  1", 445, 15);

		gBuffer.drawString("Two Pair........  2", 445, 30);

		gBuffer.drawString("Three Of A Kind.  3", 445, 45);

		gBuffer.drawString("Straight........  4", 445, 60);

		gBuffer.drawString("Full House......  6", 445, 75);

		gBuffer.drawString("Flush...........  9", 445, 90);

		gBuffer.drawString("Four Of A Kind.. 25", 445, 105);

		gBuffer.drawString("Straight Flush.. 50", 445, 120);

		gBuffer.drawString("Royal Flush.....800", 445, 135);

		

		gBuffer.setFont(fntDollars);

		gBuffer.drawString("$" + Integer.toString(Player1.getDollars()), 490, 165);



		gBuffer.setFont(fnt);

		gBuffer.drawString(TableStatus, 25, 150);

		

		//flip the buffer to the visible screen

		g.drawImage(imgBuffer, 0, 0, this);

	}	



	

	//override the update method to avoid screen flicker

	public void update(Graphics g) { 

		paint(g);

	}





	public void actionPerformed(ActionEvent e) {

		if(e.getSource() == cmdDeal) {

			if(cmdDeal.getLabel() == "Deal" ) {

				cmdDeal.setEnabled(false);

				Dealer.shuffle();	



				Timer1.setDelay(400);

				Timer1.setStatus(Timer1.STATUS_DEALING);

				Timer1.enable(true);		

			}				

			else if(cmdDeal.getLabel() == "New Game" )

				actionNew();

		}

		else if(e.getSource() == cmdDiscard) {

			cmdDiscard.setEnabled(false);

			for(int i=0; i<5; i++) 

				if(Player1.getCardStatus(i) == Player1.STATUS_DISCARD)

					Player1.setCard(i, CARD_BACK);



			repaint();



			Timer1.setStatus(Timer1.STATUS_DISCARDING);

			Timer1.setDelay(600);

			Timer1.enable(true);

		}

		else if(e.getSource() == cmdBet1)

			actionBet1();

		else if(e.getSource() == cmdBet5)

			actionBet5();

	}	



	public void actionNew() {

		Player1.setBet(0);

		Player1.setDollars(START_DOLLARS);



		cmdDeal.setEnabled(false);

		cmdDeal.setLabel("Deal");

		cmdBet1.setEnabled(true);

		cmdBet1.requestFocus();

		cmdBet5.setEnabled(true);

		

		//turn cards over

		for(int i=0; i<5; i++) 					

			Player1.setCard(i, CARD_BACK);	 

		

		TableStatus = "";

		repaint();

	}



	public void actionDeal() {

		cmdDeal.setEnabled(false);

		cmdDiscard.setEnabled(true);

		cmdDiscard.requestFocus();

		cmdBet1.setEnabled(false);

		cmdBet5.setEnabled(false);

		

		for(int i=0; i<5; i++)

			Player1.setCardStatus(i, Player1.STATUS_DISCARD);



		TableStatus = "Click on cards to hold...";

		repaint();

	}



	public void actionDiscard() {

		cmdBet1.setEnabled(true);

		cmdBet5.setEnabled(true);

		cmdDiscard.setEnabled(false);			



		int Score = Player1.getScore();

		int ScoreOdds = Player1.getScoreOdds(Score);

		String ScoreText = new String(Player1.getScoreText(Score));			

		

		if(Score > 0) {

			TableStatus = ScoreText + " (Bet of " + Integer.toString(Player1.getBet()) + " wins $" + Integer.toString(ScoreOdds * Player1.getBet()) + "!)" ;

			Player1.addDollars(ScoreOdds);

			cmdBet1.requestFocus();

		}

		else {

			if(Player1.getDollars() > 0) {

				TableStatus = "Try again...";

				cmdBet1.requestFocus();

			}

			else {

				TableStatus = "You're Broke!";

				cmdDeal.setLabel("New Game");

				cmdDeal.setEnabled(true);

				cmdDeal.requestFocus();

				cmdBet1.setEnabled(false);

				cmdBet5.setEnabled(false);

			}

		}

		

		for(int i=0; i<5; i++)

			Player1.setCardStatus(i, Player1.STATUS_DISCARD);



		lblBet.setText("Bet:0");

		Player1.setBet(0);

	}

	

	public void actionBet1() {		

		cmdDeal.setEnabled(true);

		

		if(Player1.getDollars() == 0) {

			cmdBet1.setEnabled(false);

			cmdBet5.setEnabled(false);

			cmdDeal.requestFocus();

		}

		else {

			if(Player1.getDollars() < 5)

				cmdBet5.setEnabled(false);



			Player1.setBet(Player1.getBet() + 1);

			Player1.setDollars(Player1.getDollars() - 1);

			lblBet.setText("Bet:" + Integer.toString(Player1.getBet()));

		}



		//turn cards over

		for(int i=0; i<5; i++) 					

			Player1.setCard(i, CARD_BACK);	 



		TableStatus = "";

		repaint();

	}



	public void actionBet5() {		

		Player1.setBet(Player1.getBet() + 5);

		Player1.setDollars(Player1.getDollars() - 5);

		lblBet.setText("Bet:" + Integer.toString(Player1.getBet()));

		cmdDeal.setEnabled(true);



		if(Player1.getDollars() < 5) {

			cmdBet5.setEnabled(false);	

			cmdDeal.requestFocus();



			if(Player1.getDollars() == 0) {

				cmdBet1.setEnabled(false);

				cmdDeal.requestFocus();

			}

			else

				cmdBet1.requestFocus();

		}

		

		//turn cards over

		for(int i=0; i<5; i++) 					

			Player1.setCard(i, CARD_BACK);	 



		TableStatus = "";

		repaint();

	}





	public void mouseMoved(MouseEvent e) {

		int CardLeftPos;

		

		if(cmdDiscard.isEnabled()) {

			TableStatus = "";



			//If mouse has moved over a card, change the cursor

			for(int i=0; i<5; i++) {

				CardLeftPos = i*80 + 25;						

				

				if( (e.getX() > CardLeftPos && e.getX() < CardLeftPos + 70) && (e.getY() > 25 && e.getY() < 120) ) {

					setCursor(new Cursor(Cursor.HAND_CURSOR));

					break;

				}

				else

					setCursor(new Cursor(Cursor.DEFAULT_CURSOR));

			}

		}

	}



	public void mouseDragged(MouseEvent e) {}



	public void MouseClick(int x, int y) {

		int CardLeftPos;



		if(cmdDiscard.isEnabled()) {

			//Test to see if mouse was clicked on a card

			for(int i=0; i<5; i++) {

				CardLeftPos = i*80 + 25;

				

				if( (x > CardLeftPos && x < CardLeftPos + 70) && (y > 25 && y < 120) ) {

					if(Player1.getCardStatus(i) == Player1.STATUS_DISCARD)

						Player1.setCardStatus(i, Player1.STATUS_HOLD);

					else

						Player1.setCardStatus(i, Player1.STATUS_DISCARD);



					break;

				}

			}



			repaint();

		}		

	}





	public void TimerEvent() {	

		int NextDiscard;		

		

		NextDiscard = Player1.getNextDiscardIndex();		

		if(NextDiscard != -1) {

			Player1.setCard(NextDiscard, Dealer.getTopCard());

			Player1.setCardStatus(NextDiscard, Player1.STATUS_HOLD);			

		}		

		else {

			Timer1.enable(false);

			

			if(Timer1.getStatus() == Timer1.STATUS_DEALING)

				actionDeal();

			else if(Timer1.getStatus() == Timer1.STATUS_DISCARDING)

				actionDiscard();							

		}



		repaint();

	}



}









class MouseHandler extends MouseAdapter {

	private Poker refObj;



	public MouseHandler(Poker m) {

		refObj = m;

	}



	public void mouseClicked(MouseEvent e) {		

		refObj.MouseClick(e.getX(), e.getY());		

	}

}







class Deck {

	private int Cards[];

	private int CardPtr;



	//Constructor

	public Deck() {		

		Cards = new int[53];



		//fill up the deck with values

		for(int i=1; i<Cards.length; i++)

			Cards[i] = i;



		CardPtr=1;

	}





	public int getTopCard() {		

		int TopCard; 

		

		TopCard = Cards[CardPtr];

		CardPtr++;



		return TopCard;

	}





	public void shuffle() {

		int RandomNumber;

		int temp;

		

		CardPtr = 1;    



		for(int i=1; i<Cards.length; i++) {

			RandomNumber = 1+ (int) (Math.random()*Cards.length-1);



			//Swap the contents of the two array elements

			temp = Cards[RandomNumber];

			Cards[RandomNumber] = Cards[i];

			Cards[i] = temp;			

		}

	}

}







class Hand extends PokerScore {		

	private int Cards[];

	private int CardsStatus[];  

	private int Dollars;

	private int BetAmnt;



	public final int STATUS_HOLD = 1,

					 STATUS_DISCARD = 0;

					 	

	//Constructor

	public Hand(int NumCards) {

		super(NumCards);

		Cards = new int[NumCards];

		CardsStatus = new int[NumCards];



		for(int i=0; i<Cards.length; i++) {

			Cards[i] = 0;

			CardsStatus[i] = 0;

		}

	}



	public int getScore() {				

		return getScore(Cards);

	}



	public void addCard(int value) {

		//Add a new card in the first empty element of the array



		for(int i=0; i<Cards.length; i++) 

			if(Cards[i] == 0) {

				Cards[i] = value;

				return;

			}

	}



	public int getCard(int index) {

		return Cards[index];

	}

	

	public void setCard(int index, int value) {

		Cards[index] = value;

	}



	public void setCardStatus(int index, int value) {

		CardsStatus[index] = value;

	}



	public int getCardStatus(int index) {

		return CardsStatus[index];

	}



	public int getCardCount() {

		return Cards.length;

	}

	

	public int getDollars() {

		return Dollars;

	}



	public void setDollars(int amnt) {

		Dollars = amnt;

	}



	public void addDollars(int odds) {

		Dollars += BetAmnt * odds;

	}



	public int getBet() {

		return BetAmnt;

	}



	public void setBet(int b) {

		BetAmnt = b;

	}



	public int getNextDiscardIndex() {

		//returns -1 if no discard available

		for(int i=0; i< Cards.length; i++)

			if(CardsStatus[i] == STATUS_DISCARD)

				return i;



		return -1;

	}

}







class PokerScore {

	public final int SCORE_TWO_OF_A_KIND = 14,

				     SCORE_TWO_PAIR = 15,

					 SCORE_THREE_OF_A_KIND = 16,

					 SCORE_STRAIGHT = 17,

					 SCORE_FULL_HOUSE = 18,

					 SCORE_FLUSH = 19,

					 SCORE_FOUR_OF_A_KIND = 20,

					 SCORE_STRAIGHT_FLUSH = 21,

					 SCORE_ROYAL_FLUSH = 22,



					 ODDS_TWO_OF_A_KIND = 1,

					 ODDS_TWO_PAIR = 2,

					 ODDS_THREE_OF_A_KIND = 3,

					 ODDS_STRAIGHT = 4,

					 ODDS_FULL_HOUSE = 6,

					 ODDS_FLUSH = 9,

					 ODDS_FOUR_OF_A_KIND = 25,

					 ODDS_STRAIGHT_FLUSH = 50,

					 ODDS_ROYAL_FLUSH = 800;



	private int Cards[];   //Original values of cards in the hand (sorted)

	private int Mods[];    //Modulus values of cards in the hand (sorted)



	//Constructor

	public PokerScore(int NumCards) {

		Cards = new int[NumCards];		

		Mods = new int[NumCards];		

	}





	protected int getScore(int c[]) {				

		//stuff card values in the private arrays

		for(int i=0; i<Cards.length; i++) {

			Cards[i] = c[i];

		    Mods[i] = c[i] % 13;			

		}

		

		sort(Cards);

		sort(Mods);

		

		//debugging code

		//for(int i=0; i<Cards.length; i++)

		//	System.out.println(Integer.toString(Mods[i]));

		//System.out.println("");



		if (isRoyalFlush())

			return SCORE_ROYAL_FLUSH;		

		if (isStraightFlush())

			return SCORE_STRAIGHT_FLUSH;		

		if (isFourOfAKind())

			return SCORE_FOUR_OF_A_KIND;	

		if (isFullHouse())

			return SCORE_FULL_HOUSE;	

		if (isFlush())

			return SCORE_FLUSH;				

		if (isStraight())

			return SCORE_STRAIGHT;		

		if (isThreeOfAKind()) 

			return SCORE_THREE_OF_A_KIND;	

		if (isTwoPair())

			return SCORE_TWO_PAIR;					

		if (isTwoOfAKind()) 

			return SCORE_TWO_OF_A_KIND;		





		//if all else fails

		return 0;

	}	

			

	

	public int getScoreOdds(int score) {

		switch (score) {

			case SCORE_TWO_OF_A_KIND: return ODDS_TWO_OF_A_KIND;

			case SCORE_TWO_PAIR: return ODDS_TWO_PAIR;

			case SCORE_THREE_OF_A_KIND: return ODDS_THREE_OF_A_KIND;

			case SCORE_STRAIGHT: return ODDS_STRAIGHT;

			case SCORE_FULL_HOUSE: return ODDS_FULL_HOUSE;

			case SCORE_FLUSH: return ODDS_FLUSH;

			case SCORE_FOUR_OF_A_KIND: return ODDS_FOUR_OF_A_KIND;

			case SCORE_STRAIGHT_FLUSH: return ODDS_STRAIGHT_FLUSH;

			case SCORE_ROYAL_FLUSH: return ODDS_ROYAL_FLUSH;

			default: return 0;

		}

	}



	public String getScoreText(int score) {

		switch (score) {

			case SCORE_TWO_OF_A_KIND: return "Two Of A Kind";

			case SCORE_TWO_PAIR: return "Two Pair";

			case SCORE_THREE_OF_A_KIND: return "Three Of A Kind";

			case SCORE_STRAIGHT: return "Straight";

			case SCORE_FULL_HOUSE: return "Full House";

			case SCORE_FLUSH: return "Flush";

			case SCORE_FOUR_OF_A_KIND: return "Four Of A Kind";

			case SCORE_STRAIGHT_FLUSH: return "Straight Flush";

			case SCORE_ROYAL_FLUSH: return "Royal Flush";

			default: return "";

		}

	}



	private void sort(int Arr[]) {

		int Temp;

    

		for(int loop1=Arr.length-1; loop1>=0; loop1--) 

			for(int loop2=1; loop2<=loop1; loop2++) 

				if(Arr[loop2 - 1] > Arr[loop2]) {

					Temp = Arr[loop2 - 1];

					Arr[loop2 - 1] = Arr[loop2];

					Arr[loop2] = Temp;

				}		

	}





	private boolean isTwoOfAKind() {		     

		for(int i=0; i<=3; i++)

			if(Mods[i] == Mods[i+1])	

				if ((Mods[i] > 9) || (Mods[i] == 0))  //If jacks or better

					return true;			



		return false;

	}





	private boolean isTwoPair() {				

		if (Mods[0] == Mods[1] && ((Mods[2] == Mods[3]) || (Mods[3] == Mods[4])))

			return true;

		else if ((Mods[1] == Mods[2]) &&  (Mods[3] == Mods[4]))

			return true;

		

		return false;

	}





	private boolean isThreeOfAKind() {			

		for(int i=0; i<=2; i++)

			if ((Mods[i] == Mods[i+1]) && (Mods[i] == Mods[i+2]))	

				return true;

							

		return false;		

	}





	private boolean isFourOfAKind() {		

		for(int i=0; i<=1; i++)

			if ((Mods[i] == Mods[i+1]) && (Mods[i] == Mods[i+2]) && (Mods[i] == Mods[i+3]))

				return true;



		return false;		

	}





	private boolean isFullHouse() {    

		if ((Mods[0] == Mods[1]) && (Mods[1] == Mods[2]) && (Mods[3] == Mods[4]))

			return true;

		else if ((Mods[0] == Mods[1]) && (Mods[2] == Mods[3]) && (Mods[3] == Mods[4]))

			return true;

	

		return false;	

	}





	private boolean isStraight() {		

		int c[] = new int[5];

        

		for(int i=0; i<Cards.length; i++)

			c[i] = Mods[i];

			

		sort(c);

		

		//check for ace, 2, 3, 4, 5

		if ((c[0] == (c[1] - 1)) && (c[1] == (c[2] - 1)) && (c[2] == (c[3] - 1)) && (c[3] == (c[4] - 1)))

			return true;

		

		for(int i=0; i<Cards.length; i++) 

			if (c[i] == 0)  //handle the ace  

				c[i] = 13;

		

		sort(c);



		//check for ace, king, queen, jack, 10

		if ((c[0] == (c[1] - 1)) && (c[1] == (c[2] - 1)) && (c[2] == (c[3] - 1)) && (c[3] == (c[4] - 1)))

			return true;

		

		return false;

	}





	private boolean isFlush() {				

		for(int i=0; i<=3; i++)

			if((Cards[0] <= (i+1)*13) && (Cards[0] > i*13)) 

				if((Cards[4] <= (i+1)*13) && (Cards[4] > i*13))	

					return true;

		

		return false;

	}





	private boolean isStraightFlush() {

		if (isStraight() && isFlush())

			return true;

		

		return false;

	}





	private boolean isRoyalFlush() {		

		if (isStraight() && isFlush() && Mods[0] == 0 && Mods[4] == 12)

			return true;



		return false;

	}

}





class Timer extends Thread {

	private Poker refObj = null;  

    private boolean enabled = false;

    private int delay;

	private int status;



	public final int STATUS_DEALING = 1,

					 STATUS_DISCARDING = 2;



    Timer(Poker refObj, int delay) {

		this.refObj = refObj;

		this.delay = delay;

    }



    public void run() {

		while (true) {

			try {

				sleep(delay);	



				if (enabled) {								

					refObj.TimerEvent(); 				

				}								

			} 

			catch (InterruptedException ignored) {}

		}

    }



	public boolean isEnabled() {

		return enabled;

	}



	public void enable(boolean e) {		

		enabled = e;

	}



	public void setDelay(int del) {

		delay = del;

	}



	public void setStatus(int s) {		

		status = s;

	}



	public int getStatus() {

		return status;

	}

}     