/*
 * Created on May 11, 2005
 */

import java.awt.*;
import javax.swing.*;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.awt.Font;
import java.lang.Math;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.font.FontRenderContext;
import javax.swing.*;

/**
 * @author James Albert
 */
public class Solitaire extends JApplet implements Runnable {

	private static class PlayingCards {	

		public static final int SUIT_CLUB = 0, SUIT_SPADE = 1, SUIT_DIAMOND = 2, SUIT_HEART = 3, NUM_SUITS = 4;
		public static final int CARD_BACK = -1, CARD_BLANK = 0, CARD_JACK = 11, CARD_QUEEN = 12, CARD_KING = 13, CARD_JOKER = 14;
			
		private static class Card {
						
			// Variables and Methods for the four suit symbols
			private static class Suit {

			private static Shape[] suitShapes = new Shape[4];
			
			static {
				shapeClub();
				shapeHeart();
				shapeSpade();
				shapeDiamond();
			}
					
			private static void shapeClub() {
			    	Area club; // the final club shape
			    	Ellipse2D.Float clubleftleaf, clubrightleaf, clubtopleaf; // the club's three leaves are ellipses
			    	GeneralPath clubstem; // the club's stem is a general path
				    	
			    	// Draw the club's stem
			    	clubstem = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
			    	clubstem.moveTo(5.0f/14.0f,1.0f);
			    	clubstem.quadTo(7.0f/14.0f,9.0f/11.0f,5.0f/14.0f,8.0f/11.0f);
			    	clubstem.lineTo(5.0f/14.0f,5.0f/11.0f);
			    	clubstem.lineTo(9.0f/14.0f,5.0f/11.0f);
			    	clubstem.lineTo(9.0f/14.0f,8.0f/11.0f);
			    	clubstem.quadTo(7.0f/14.0f,9.0f/11.0f,9.0f/14.0f,1.0f);
			    	clubstem.lineTo(5.0f/14.0f,1.0f);
			    	clubstem.closePath();
			    	
			    	// Draw the club's left leaf
			        clubleftleaf = new Ellipse2D.Float();
			        clubleftleaf.setFrame(0,4.0f/11.0f,6.0f/14.0f,6.0f/11.0f);
			        
			        // Draw the club's right leaf
			        clubrightleaf = new Ellipse2D.Float();
			        clubrightleaf.setFrame(8.0f/14.0f,4.0f/11.0f,6.0f/14.0f,6.0f/11.0f);
			        
			        // Draw the club's top leaf
			        clubtopleaf = new Ellipse2D.Float();
			        clubtopleaf.setFrame(4.0f/14.0f,0.0f/11.0f,6.0f/14.0f,6.0f/11.0f);  
			        
			        // Add all of the club's shapes together into the final club shape
			        club = new Area(clubstem);
			        club.add(new Area(clubleftleaf));
			        club.add(new Area(clubrightleaf));
			        club.add(new Area(clubtopleaf));
			        
			        suitShapes[SUIT_CLUB] = club;
			    }
				
				private static void shapeHeart() {
			    	Arc2D.Float heartleft, heartright;
			    	GeneralPath heartbase;
			    	
			    	heartbase = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
			        heartbase.moveTo(0,2.0f/7.0f);
			        heartbase.quadTo(0,4.0f/7.0f,0.5f,1);
			        heartbase.quadTo(1,4.0f/7.0f,1,2.0f/7.0f);
			        heartbase.lineTo(0,2.0f/7.0f);
			        heartbase.closePath();
					           
			        heartleft = new Arc2D.Float();
			        heartleft.setFrame(0,0,0.5f,4.0f/7.0f);
			        heartleft.setAngleStart(0.0f);
			        heartleft.setAngleExtent(180.0f);
			        
			        heartright = new Arc2D.Float();
			        heartright.setFrame(0.5f,0,0.5f,4.0f/7.0f);
			        heartright.setAngleStart(0);
			        heartright.setAngleExtent(180);
			        
			        Area heart = new Area(heartbase);
			        heart.add(new Area(heartleft));
			        heart.add(new Area(heartright));
			        
			        suitShapes[SUIT_HEART] = heart;
			   }
			    
				private static void shapeSpade() {
					GeneralPath spadetop, spadebase;
			    	Area spade;
			    	
			    	spadetop = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
			    	spadetop.moveTo(0.5f,0.0f);
			        spadetop.curveTo(17.0f/12.0f,8.0f/11.0f,0.75f,12.0f/11.0f,0.5f,7.0f/11.0f);
			        spadetop.curveTo(0.25f,12.0f/11.0f,-5.0f/12.0f,8.0f/11.0f,0.5f,0.0f);
			        spadetop.closePath();
			        
			        spadebase = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
			        spadebase.moveTo(4.0f/12.0f,1.0f);
			        spadebase.quadTo(0.5f,9.0f/11.0f,4.0f/12.0f,8.0f/11.0f);
			        spadebase.lineTo(0.5f,7.0f/11.0f);
			        spadebase.lineTo(8.0f/12.0f,8.0f/11.0f);
			        spadebase.quadTo(0.5f,9.0f/11.0f,8.0f/12.0f,1.0f);
			        spadebase.lineTo(4.0f/12.0f,1.0f);
			        spadebase.closePath();
			        
			        spade = new Area(spadetop);
			        spade.add(new Area(spadebase));
			        
			        suitShapes[SUIT_SPADE] = spade;
			   }
			    
				private static void shapeDiamond() {
			    	GeneralPath diamond;
			    	
			    	diamond = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
			    	diamond.moveTo(0.5f,0.0f);
			    	diamond.lineTo(1.0f,0.5f);
			    	diamond.lineTo(0.5f,1.0f);
			    	diamond.lineTo(0.0f,0.5f);
			    	diamond.lineTo(0.5f,0.0f);
			    	diamond.closePath();
			    	
			    	suitShapes[SUIT_DIAMOND] = diamond;
			    }
			    
			    protected static void draw(int suit, AffineTransform t, float x, float y, Graphics2D g2) {
			    	Color oldcolor;
			    	AffineTransform old_transform;
			    	Shape transformed_suit;
				    	
			    	old_transform = g2.getTransform();
			    	oldcolor = g2.getColor();

			    	g2.translate(x,y);		    	
			    	g2.setColor(suit==SUIT_HEART||suit==SUIT_DIAMOND ? Color.red : Color.black);
			    	g2.fill(t.createTransformedShape(suitShapes[suit]));
			    	
			    	g2.setColor(oldcolor);
			    	g2.setTransform(old_transform);
			    }
			}	
			
			protected static void draw(int value, int suit, AffineTransform t, float x, float y, Graphics2D g2) {
		    	AffineTransform suit_transform, old_transform;
		    	Color old_color;
		    	Stroke old_stroke;
		    	RoundRectangle2D card_shape;
		    	
				AffineTransform face_transform;
				FontRenderContext frc;
				Font f;
				TextLayout face_text;
				Shape face_shape;
		    	
		    	float w, h;
		    	
		    	old_color = g2.getColor();
		    	old_stroke = g2.getStroke();
		    	old_transform = g2.getTransform();
		    	
		    	w = (float) t.getScaleX();
		    	h = (float) t.getScaleY();
		    	
		    	card_shape = new RoundRectangle2D.Float(x,y,w*1.0f,h*1.0f,(w+h)/2.0f*0.05f,(w+h)/2.0f*0.05f);	    	
		    	    	
		    	g2.setColor(Color.white);
		    	g2.fill(card_shape);
		    	g2.setStroke(new BasicStroke(1.0f));
		    	g2.setColor(Color.black);
		    	g2.draw(card_shape);

		    	if(value==CARD_JACK||value==CARD_QUEEN||value==CARD_KING||value==CARD_JOKER) {
					f = new Font("Lucida Bright",java.awt.Font.BOLD,1);
					frc = g2.getFontRenderContext();
					switch(value) {
					case CARD_JACK:
						face_text = new TextLayout("J",f,frc);
						break;
					case CARD_QUEEN:
						face_text = new TextLayout("Q",f,frc);
						break;
					case CARD_KING:
						face_text = new TextLayout("K",f,frc);
						break;
					case CARD_JOKER:
						face_text = new TextLayout("Jk",f,frc);
						break;
					default:
						face_text = new TextLayout("!",f,frc);
						break;
					}
					
					face_transform = new AffineTransform();
					face_transform.scale((1.0f/face_text.getBounds().getWidth())*w*(3.0f/5.0f),(1.0f/face_text.getBounds().getHeight())*h*(5.0f/7.0f));
					face_transform.translate(0.0f,-1*face_text.getBounds().getMinY());
					face_shape = face_text.getOutline(face_transform);
					g2.translate(x+w*(1.0f/5.0f),y+h*(1.0f/7.0f));
					g2.setColor(suit==SUIT_HEART||suit==SUIT_DIAMOND ? Color.red : Color.black);
					g2.draw(face_shape);
					g2.setColor(old_color);
					g2.setTransform(old_transform);
		    	}
		    	
		    	suit_transform = new AffineTransform();
		    	suit_transform.scale(w*(1.0f/5.0f),h*(1.0f/7.0f));
		    	
		    	if(value==2||value==3) {
		    		Suit.draw(suit,suit_transform,x+w*(2.0f/5.0f),y+h*(1.0f/7.0f),g2);
		    	}
		    	
		    	if(value==1||value==3||value==5||value==9||(value>=CARD_JACK&&value<=CARD_JOKER)) {
		    		Suit.draw(suit,suit_transform,x+w*(2.0f/5.0f),y+h*(3.0f/7.0f),g2);
		    	}
		    	
		    	if(value>=4&&value<=10) {
		    		Suit.draw(suit,suit_transform,x+w*(1.0f/5.0f),y+h*(1.0f/7.0f),g2);
		    		Suit.draw(suit,suit_transform,x+w*(3.0f/5.0f),y+h*(1.0f/7.0f),g2);
		    	}
		    	
		    	if(value==6||value==7) {
		    		Suit.draw(suit,suit_transform,x+w*(1.0f/5.0f),y+h*(3.0f/7.0f),g2);
		    		Suit.draw(suit,suit_transform,x+w*(3.0f/5.0f),y+h*(3.0f/7.0f),g2);
		    	}
		    	
		    	if(value==7) {
		    		Suit.draw(suit,suit_transform,x+w*(2.0f/5.0f),y+h*(2.0f/7.0f),g2);
		    	}
		    	
		    	if(value==10) {
		    		Suit.draw(suit,suit_transform,x+w*(2.0f/5.0f),y+h*(27.0f/112.0f),g2);
		    	}
		    	
		    	if(value>=8&&value<=10) {
		    		Suit.draw(suit,suit_transform,x+w*(1.0f/5.0f),y+h*(19.0f/56.0f),g2);
		    		Suit.draw(suit,suit_transform,x+w*(3.0f/5.0f),y+h*(19.0f/56.0f),g2);
		    	}
		    	
		    	suit_transform.rotate(java.lang.Math.PI);
		    	
		    	if(value==2||value==3) {
		    		Suit.draw(suit,suit_transform,x+(w*(3.0f/5.0f)),y+(h*(6.0f/7.0f)),g2);
		    	}
		    	
		    	if(value>=4&&value<=10) {
		    		Suit.draw(suit,suit_transform,x+w*(2.0f/5.0f),y+h*(6.0f/7.0f),g2);
		    		Suit.draw(suit,suit_transform,x+w*(4.0f/5.0f),y+h*(6.0f/7.0f),g2);
		    	}
		    	
		    	if(value>=8&&value<=10) {
		    		Suit.draw(suit,suit_transform,x+w*(2.0f/5.0f),y+h*(37.0f/56.0f),g2);
		    		Suit.draw(suit,suit_transform,x+w*(4.0f/5.0f),y+h*(37.0f/56.0f),g2);
		    	}
		    	
		    	if(value==10) {
		    		Suit.draw(suit,suit_transform,x+w*(3.0f/5.0f),y+h*(85.0f/112.0f),g2);
		    	}
		    	
		    	g2.setTransform(old_transform);
		    	g2.setStroke(old_stroke);
		    	g2.setColor(old_color);
			}
		}
			    
		protected static void draw(int value, int suit, AffineTransform t, float x, float y, Graphics2D g2) {
			Card.draw(value,suit,t,x,y,g2);
		}
		
		protected void paintCards(int w, int h, Graphics2D g2) {
		        AffineTransform card_transform;
			                
		    	card_transform = new AffineTransform();
		    	
		    	card_transform.scale(w/2.0f,h/2.0f);
		    	Card.Suit.draw(PlayingCards.SUIT_CLUB,card_transform,0.0f,0.0f,g2);
		    	Card.Suit.draw(PlayingCards.SUIT_HEART,card_transform,w/2.0f,0.0f,g2);
		    	Card.Suit.draw(PlayingCards.SUIT_SPADE,card_transform,w/2.0f,h/2.0f,g2);
		    	Card.Suit.draw(PlayingCards.SUIT_DIAMOND,card_transform,0.0f,h/2.0f,g2);
		    }
	}
	
	private Thread thread;
	private BufferedImage bimg;	
	private PlayingCards cards;
	
	
	public void init() {
		setBackground(new Color(0.0f,0.5f,0.0f));
		cards = new Solitaire.PlayingCards();
	}
	
    public void paint(Graphics g) {
        AffineTransform card_transform;
        Graphics2D g2;
        int i;
        
        Dimension d = getSize();
        
        if (bimg == null || bimg.getWidth() != d.width || bimg.getHeight() != d.height) {
            bimg = (BufferedImage) createImage(d.width, d.height);
        } 
        g2 = bimg.createGraphics();
        
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, d.width, d.height);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        
        card_transform = new AffineTransform();
        card_transform.scale(d.width/13.0,d.height/4.0f);
        for(i=0;i<13;i++) {
        	PlayingCards.draw(i+1,PlayingCards.SUIT_SPADE,card_transform,d.width*(i/13.0f),0.0f,g2);
        	PlayingCards.draw(i+1,PlayingCards.SUIT_HEART,card_transform,d.width*(i/13.0f),d.height*(1.0f/4.0f),g2);
        	PlayingCards.draw(i+1,PlayingCards.SUIT_CLUB,card_transform,d.width*(i/13.0f),d.height*(2.0f/4.0f),g2);
        	PlayingCards.draw(i+1,PlayingCards.SUIT_DIAMOND,card_transform,d.width*(i/13.0f),d.height*(3.0f/4.0f),g2);
        }
        
        g2.dispose();
        g.drawImage(bimg, 0, 0, this);
    }
	
    public void start() {
        thread = new Thread(this);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    public synchronized void stop() {
        thread = null;
    }

    public void run() {
        Thread me = Thread.currentThread();
        while (thread == me) {
            repaint();
            try {
                thread.sleep(10);
            } catch (InterruptedException e) { break; }
        }
        thread = null;
    }
	
	public static void main(String[] args) {
    	final Solitaire game = new Solitaire();
    	game.init();
        JFrame f = new JFrame("Playing Cards");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.addWindowListener(new WindowAdapter() {
            public void windowDeiconified(WindowEvent e) { game.start(); }
            public void windowIconified(WindowEvent e) { game.stop(); }
        });
        f.add(game);
        f.pack();
        f.setSize(new Dimension(400,300));
        f.setVisible(true);
        game.start();
		
	}
}