/** Experiment to see how fast I could develop a card game in Java.
  * Feel free to use the source code in any way you like. However
  * it is poorly doucumented and the badly designed so be careful.
  * The card images are under the GPL. Follow the links from
  * the directions page.
  */
import java.net.URL;
import java.awt.*;
import java.awt.image.*;
import java.applet.Applet;
import java.util.Vector;

//Like a record for a card. Holds value, color and the image.
class Card {
    int value;
    Image image; 
    public Card (Image image, int value) {
        this.image = image;
        this.value = value;
    }
}

class Player {
    Vector cards;
    
    public Player()
    {
        cards = new Vector();
    }
    
    public void removeAllCards()
    {
        cards.removeAllElements();
    }
    
    public void addCard(Card card)
    {
        cards.addElement(card);
    }
    
    public void removeCard(int index)
    {
        cards.removeElementAt(index);
    }
    
    
    public Card throwCard(int value)
    {
        int length = cards.size();
        for (int c=0;c<length;c++)
        {
            Card temp = (Card)(cards.elementAt(c));
            if (temp.value == value || (value >= 10 && temp.value >= 10))
            {
                removeCard(c);
                //System.out.println("success");
                return temp;
            }
        }
        return null;
    }
    
    public boolean throwNextCard(Vector cardPile)
    {
        int start = cards.size()-1;
        int value;
        // This should never happen
        if (cardPile == null) return false;
        
        int size = cardPile.size();
        
        if (size<=0) value = -1;
        else {
            value = ((Card)cardPile.elementAt(0)).value;
        } 
        //If wildCard
        if (value >= 10) return false;
        
        int suggestedValue = -1;
        for (int i = (value+1); i<13; i++)
        {
            int count = numberOfCards(i);
            
            if (count <= 0) continue;
            
            if (value < 0)
            {
                suggestedValue = i;
                size = count;
                break;
            }
            
            if ( count == size) {
                suggestedValue = i;
                break;
            }
            if (i>=10 && count > size) 
            {
                suggestedValue = i;
                break;
            }

        }
        if (suggestedValue < 0) return false;

        cardPile.removeAllElements();
        
        for (int c = 0; c<size; c++)
        {
            Card temp = throwCard(suggestedValue);
            cardPile.addElement(temp);
        }
        return true;   
    }
    
    /** Finds the number of cards that this player has a the specified
      * value. @param value The value of the card
      */
    private int numberOfCards(int value)
    {
        int length = cards.size();
        int total = 0;
        for (int i=0;i<length;i++)
        {
            int tempValue = getCard(i).value;
            if ( tempValue == value )
                total++;
            else if ( value >= 10 && tempValue >= 10) total++;  
        }
        return total;
    }
    
    public Card getCard(int index)
    {
        if (cards.size() <= 0) return null;
        return (Card)(cards.elementAt(index));
    }
    
    public void setCard(Card card,int index)
    {
        cards.setElementAt(card,index);
    }
    
    void drawHand(int x, int y, boolean user, Vector selected, Graphics g)
    {
        int length = cards.size();
        for (int c = 0; c<length; c++)
        {
            if (user) {
                boolean drawn = false;
                if (selected!=null)
                {
                    for (int j=0; j<selected.size(); j++)
                    {
                        if (selected.elementAt(j) == cards.elementAt(c))
                        {
                            g.drawImage( ((Card)(cards.elementAt(c))).image,x,y-25,null);
                            drawn = true;
                            break;
                        }
                    }
                }
                if (!drawn) {                 
                    g.drawImage( ((Card)(cards.elementAt(c))).image,x,y,null);
                }
                x+=Richman.dX/2;
            }
            else {
                //g.drawImage( ((Card)(cards.elementAt(c))).image,x,y,null);
                g.drawImage(Richman.back,x,y,null);
                x+=15;
            }
        }
    }
  
}

public class Richman extends Applet implements Runnable {

    final String suits [] = {"h", "d", "c", "s"};
    final String values [] = {"06", "07", "08", "09", "10"
      , "11", "12", "13", "01","02","03","04","05"};
    final static int dX = 71;                     // 1/2 of picture width
    final static int dY = 96;                     // picture height
    Card cards [];                         // cards in top rows
                                         // cards [i] == null => card moved to last row
    Image pictures [];                     // cards' images
    static Image back;                   // image of a card's back
    //int nPictures;
    
    Image all = null;
    Graphics buffer = null;
    Player players[];
    Vector pile;
    
    final static int THROW_X = 20;
    final static int THROW_Y = 320;
    final static int PASS_X = 20;
    final static int PASS_Y = 380;
    final static int BOX_WIDTH = 72;
    final static int BOX_HEIGHT = 36;
    
    //Current number of people playing
    int currentPlayerNumber;
    // Starts at currentPlayerNumber if reaches 0 pile is emptied
    int countDown;
    
    int currentPlayer; // The current Player
    
    int richman = -1; // Player who finishes first
    int poorman = -1; // Player who finishes last
    
    public boolean left = true;
    
    Vector selected;
    Thread runner;
    


    // Creates the filenames and loads the images of the faces of the cards as well as the 
    //back. Then checks to see if all the pictures are loaded
    public void init() {
        int counter = 0; // used in media tracker
        int length; //length of picture array
        players = new Player[5]; 
        pile = new Vector();
        MediaTracker mt = new MediaTracker(this);
        setBackground(Color.black);
        back = getImage(getCodeBase(),"back.gif");
        mt.addImage(back,0);

        length = suits.length*values.length;
        pictures = new Image[length];
        Image cards = getImage(getCodeBase(),"cards.gif");
        mt.addImage(cards,0);
        
        if (mt == null) return;
        try {
            mt.waitForAll();
            if (mt.isErrorAny()) {
                System.out.println("Error loading images");
            }
        }
        catch (Exception e) {System.out.println(e.toString());}
        
        
        ImageProducer producer = cards.getSource();
        int nCount = 0;
        for (int sCount = 0; sCount < suits.length; sCount++ ) {
            for ( int vCount = 0; vCount < values.length; vCount++) {
            	CropImageFilter cropFilter = new CropImageFilter(vCount*dX,sCount*dY,dX,dY);
                pictures[nCount] = createImage(new FilteredImageSource(producer, cropFilter));
                //pictures[nCount] = createImage(dX,dY);
                //pictures[nCount].getGraphics().drawImage(cards,vCount*dX*-1,sCount*dY*-1,null);
                mt.addImage(pictures[nCount],2);
                nCount++;
            }
        }
        try {
        	mt.waitForID(2);
        }catch(Exception e){}

        all = createImage(size().width,size().height);
        buffer = all.getGraphics();
        
        execute();
    }
    


    public void execute () {
    
        //creates card objects using the class constructor for Card
        int number = 0;
        
        
        cards = new Card[pictures.length]; 
        for (int suit = 0; suit < suits.length; suit++) {
            for (int value = 8; value < 13; value++)
            {
                cards[number] = new Card(pictures[number], value);
                number++;
            }
            
            for (int value = 0; value <= 7; value++) {
                cards[number] = new Card(pictures[number], value);
                number++;
            }
        }
            
        shuffle ();
        
        // Create the players
        for (int i = 0; i<players.length; i++)
        {
            players[i] = new Player();
        }
        int c;
        if (richman > -1 && poorman > -1) c = poorman;
        else c = players.length-1;
        
        for (int i = 0; i<cards.length; i++)
        {
            players[c].addCard(cards[i]);
            c--;
            if (c < 0) c = players.length-1;
        }
                
        for (int i = 0; i<players.length; i++)
        {
            sort(players[i].cards);
        }       
        currentPlayerNumber = players.length;
        countDown = players.length;
        selected = new Vector(); //cards the user selects

        currentPlayer = 4;
        
        richman = -1;
        poorman = -1;

        runner = new Thread(this);
        runner.start();
        
        draw();
        //aroundTheTable(currentPlayer);
    }
    
    public void reDeal()
    {
        //Remove All Cards if any
        if (richman < 0 || poorman < 0)
        {
            System.out.println("Error");
            return;
        }
        
        for (int i = 0; i<players.length; i++)
        {
            players[i].removeAllCards();
        }

        shuffle(); //Shuffle the cards again
        int c = poorman;
        for (int i = 0; i<cards.length; i++)
        {
            players[c].addCard(cards[i]);
            c--;
            if (c < 0) c = players.length-1;
        }
                
        for (int i = 0; i<players.length; i++)
        {
            sort(players[i].cards);
        }
        
        currentPlayerNumber = players.length;
        countDown = players.length;        
        selected.removeAllElements(); //cards the user selects
        
        pile.removeAllElements();
        int leftDistance = richman-poorman;
        if (leftDistance < 0) leftDistance = players.length+leftDistance;
        int rightDistance = players.length-leftDistance;
        
        // Always go the shorter distance
        if (leftDistance < rightDistance) left = true;
        else left = false;
            
        currentPlayer = poorman;
        Player poorPlayer = players[poorman];
        Player richPlayer = players[richman];
        int poorLength = poorPlayer.cards.size();
        int richLength = richPlayer.cards.size();
        Card temp1 = poorPlayer.getCard(0);
        Card temp2 = poorPlayer.getCard(1);
        poorPlayer.setCard(richPlayer.getCard(richLength-1),0);
        poorPlayer.setCard(richPlayer.getCard(richLength-2),1);
        richPlayer.setCard(temp1,richLength-1);
        richPlayer.setCard(temp2,richLength-2);
        sort(poorPlayer.cards);
        sort(richPlayer.cards);
        richman = -1;
        poorman = -1;
        draw();
    }
    
    public void stop()
    {
        if (runner!=null)
        {
            runner.stop();
        }
    }
    
    
    public void run()
    {
        while(true)
        {
                 
            countDown--;
            
            // If player has no cards skip
            if (players[currentPlayer].cards.size() <= 0) {
                if (left) {
                    currentPlayer--;
                    if (currentPlayer<0) currentPlayer = players.length-1;
                }
                else currentPlayer = (currentPlayer+1)%players.length;
                continue;
            }
            
            // Only one player left so stop playing
            // Set last player as poor man
            if (currentPlayerNumber <= 1) {
                poorman = currentPlayer;
                draw();
                sleep(1000);
                reDeal();
                continue;
            }

            if (countDown <= 0) {
                pile.removeAllElements();
                //sleep(400);
                draw(); //show an empty pile
                sleep(400);
            }

            if (currentPlayer == 4) {
                // Wait for user input
                draw();
                suspend();
                while (sleeping) sleep(100);                
            }   

            
            else
            {
                                    
                if (players[currentPlayer].throwNextCard(pile) ) {
                    countDown = players.length;
                }
                //sleep(300);
                draw();
                sleep(400);
            }
            
            if (players[currentPlayer].cards.size()<=0) {
                if (richman < 0) richman = currentPlayer;
                currentPlayerNumber--;
            }
            
            if (pile.size() > 0)
            {
              
                if ( ((Card)pile.elementAt(0)).value >= 10)
                {
                    pile.removeAllElements();
                    //sleep(400);
                    draw();
                    sleep(400);
                    continue;
                }                    
            }
            if (left) {
                currentPlayer--;
                if (currentPlayer<0) currentPlayer = players.length-1;
            }
            else currentPlayer = (currentPlayer+1)%players.length;
            
        }    
        
    }
    
    public void update(Graphics g)
    {
        paint(g);
    }
    
    public void paint(Graphics g)
    {
        if (buffer!=null)
            g.drawImage(all,0,0,null);
    }
    
    public void draw()
    {

        Graphics g = getGraphics();
        if (buffer==null) return;
        buffer.setColor(getBackground());
        buffer.fillRect(0,0,size().width, size().height);
        if (currentPlayerNumber <= 1 && poorman >= 0 && richman>=0) {
            drawBorder(richman,Color.green,"RICHMAN",buffer);
            drawBorder(poorman,Color.red,"POORMAN",buffer);
        }
        else drawBorder(currentPlayer,Color.magenta,"",buffer);
        
        players[2].drawHand(50,20,false,null,buffer);
        players[1].drawHand(350,20,false,null,buffer);
        players[3].drawHand(10,170,false,null,buffer);
        players[0].drawHand(400,170,false,null,buffer);
        
        //long time = System.currentTimeMillis();
        players[4].drawHand(150,320,true,selected,buffer);
        //System.out.println(System.currentTimeMillis()-time);
        
        int x = 250;
        int y = 170;
       
       
        for (int i = 0; i<pile.size(); i++)
        {
            Card card = (Card)(pile.elementAt(i));
            if (card == null) continue;
            buffer.drawImage(card.image,x,y,null);
            x += 15;
        }
        
        
        buffer.setColor(Color.white);
        buffer.drawRect(THROW_X,THROW_Y,BOX_WIDTH,BOX_HEIGHT);
        buffer.drawString("THROW",THROW_X+10,THROW_Y+20);
        buffer.drawRect(PASS_X,PASS_Y,BOX_WIDTH,BOX_HEIGHT);
        buffer.drawString("PASS",PASS_X+10,PASS_Y+20);

        g.drawImage(all,0,0,null);
        
    }
    
    boolean sleeping = false;
    
    void suspend()
    {
        //runner.suspend();
        sleeping = true;     
    }
    
    void resume()
    {
        //runner.resume();
        sleeping = false;
    }
    
    
    void drawBorder(int index, Color color, String text, Graphics g)
    {
        int x;
        int y;
        int width = 230;
        
        if (index == 0) { 
            x = 400;
            y = 170;
        }
        else if (index == 1) { 
            x = 350;
            y = 20;
        }
        else if (index == 2) { 
            x = 50;
            y = 20;
        }
        else if (index == 3) { 
            x = 10;
            y = 170;
        }
        else { 
            x = 150;
            y = 320;
            width = 435;
        }
        g.setColor(color);
        g.setFont(new Font("TimesRoman",Font.PLAIN,15));
        g.drawString(text,x+50,y-6);
        g.drawRect(x-5,y-5,width,106);
    }
    
  
    public boolean mouseDown(Event e, int x, int y)
    {
        if (x>=THROW_X && x<THROW_X+BOX_WIDTH &&
            y>=THROW_Y && y<THROW_Y+BOX_HEIGHT)
        {
            if (selected.size() <= 0) return true;
            
            if (pile.size() == selected.size() || pile.size()<=0)
            {
                if (pile.size() > 0)
                {
                    Card selectedCard = (Card)selected.elementAt(0);
                    Card pileCard = (Card)pile.elementAt(0);
                    if (pileCard.value >= selectedCard.value || pileCard.value >=10) return true;
                }
                pile.removeAllElements();
                for (int c=0;c<selected.size();c++)
                {
                    Card card = (Card)(selected.elementAt(c));
                    pile.addElement(card);
                    players[4].cards.removeElement(card);
                }
                selected.removeAllElements();
                countDown = players.length;                
                draw();
                resume();
                return true;
            }

        }
        
        else if (x>=PASS_X && x<PASS_X+BOX_WIDTH &&
                 y>=PASS_Y && y<PASS_Y+BOX_HEIGHT)
        {
            //System.out.println("PASS");
            selected.removeAllElements();
            draw();
            resume();;
            return true;
        }
        
        if (x<150 || y<320-25 || y > 320+dY) return false;
      
        for (int i = players[4].cards.size()-1; i>=0; i--)
        {
            int end = 150+((i+1)*dX/2)+dX/2;
            int start = 150+((i)*dX/2);
            if (x >= start && x < end ) {
                
                Card temp = players[4].getCard(i);
                if (temp == null) return true;
                
                for (int c=0;c<selected.size();c++)
                {    
                    if (selected.elementAt(c)==temp)
                    {
                        selected.removeElementAt(c); 
                        draw(); 
                        return true; 
                    }

                    // Note that to be in the loop size must be at least one
                    Card selectedCard = (Card)selected.elementAt(0);
                    if ( selectedCard.value != temp.value &&
                        (selectedCard.value < 10 || temp.value < 10))
                    {  return true;  } 
                }
                selected.addElement(temp);
                draw();
                return true;
            }
        }
      
        //pile[0] = players[4].cards
        return false;
    }



  // shuffles the cards. used in execute().
  void shuffle () {
      //System.out.println("shuffling");
      for (int oldNu = cards.length - 1; oldNu >= 1; oldNu--) { //oldNu is current card
          int newNu = (int)(cards.length * Math.random());   //newNu is the card counter 
                                                      //that switches with oldNu's
          Card temp = cards[oldNu];   //temp is a temporary variable
          cards[oldNu] = cards[newNu];
          cards[newNu] = temp;
      }

      /*for(int c = 0; c<cards.length; c++)
      {
          System.out.print(cards[c].value+",");
      }
      System.out.println("");*/
  }
  
  void sort(Vector cards)
  {
      int length = cards.size();
      int j;
      for (int i = 1; i<length; i++)
      {
          Card card = (Card)cards.elementAt(i);
          for (j = i-1; j >= 0 && card.value > ((Card)(cards.elementAt(j))).value; j--)
          {
             cards.setElementAt( cards.elementAt(j), j+1);
          }
          cards.setElementAt(card,j+1);
      }
  }

  void sleep(int time) {
      try {runner.sleep(time);}
      catch(InterruptedException e) {;}
  }
      



}