import edu.neu.ccs.*;
import edu.neu.ccs.gui.*;
import edu.neu.ccs.codec.*;
import edu.neu.ccs.console.*;
import edu.neu.ccs.filter.*;
import edu.neu.ccs.jpf.*;
import edu.neu.ccs.parser.*;
import edu.neu.ccs.pedagogy.*;
import edu.neu.ccs.quick.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import java.util.*;
import java.math.*;
import java.beans.*;
import java.lang.reflect.*;
import java.net.URL;
import java.util.regex.*;
import java.text.ParseException;

public class Thomas_CardTricks extends DisplayPanel implements JPTConstants
{
    protected final int BUFFERSIZE = 650;
    
    // square window for painting
    protected BufferedPanel window
        = new BufferedPanel(BUFFERSIZE, BUFFERSIZE);

    
    protected Action resetCards
        = new SimpleAction("Reset") {
            public void perform() { ResetCards(); }
        };
    
    protected Action randomRotateTranslate
		= new SimpleAction("Translate and Rotate") {
			public void perform() { RandomTranslateAndRotate(); }
		};
		
    protected Action rotateAsGroup
    	= new SimpleAction("Rotate as Group") {
    		public void perform() { RandomRotateAsGroup(); }
    	};
    
    protected Action clear
        = new SimpleAction("Clear") {
            public void perform() { clear(); }
        };

    // button panel
    protected ActionsPanel buttonPanel
        = new ActionsPanel(new Action[] {randomRotateTranslate, rotateAsGroup, resetCards, clear});

    
    
    protected MouseAction mouseMoved
    = new MouseAction("Mouse Moved") {
        public void mouseActionPerformed(MouseEvent mevt) {
            mouseMoved(mevt);
        }
    };

	// mouse pressed action
	protected MouseAction mousePressed
	    = new MouseAction("Mouse Pressed") {
	        public void mouseActionPerformed(MouseEvent mevt) {
	            mousePressed(mevt);
	        }
	    };
	
	// mouse dragged action
	protected MouseAction mouseDragged
	    = new MouseAction("Mouse Dragged") {
	        public void mouseActionPerformed(MouseEvent mevt) {
	            mouseDragged(mevt);
	        }
	    };
	
	// mouse released action
	protected MouseAction mouseReleased
	    = new MouseAction("Mouse Released") {
	        public void mouseActionPerformed(MouseEvent mevt) {
	            mouseReleased(mevt);
	        }
	    };
    
    // main panel
    protected TablePanel mainPanel
        = new TablePanel(
            new Object[] {
                new Display(buttonPanel, null, "Controls"),
                new Display(window,   null, "Graphics")},
            VERTICAL, 5, 5, NORTH);
    
    MouseActionAdapter adapter = window.getMouseActionAdapter();
    ImagePaintableLite[] cards;
    PaintableSequence cardSequence;
    Paintable rotatePoint = new ShapePaintable(new XCircle(getCenter().x, getCenter().y, 4), PaintMode.FILL, Color.BLUE);

    public Thomas_CardTricks() {
    	window.installSimpleMouseActions(true);
    	ResetCards();
    	add(mainPanel);
    	addActions();
    }
    
    
    // method: mouseMoved
    protected void mouseMoved(MouseEvent mevt) {
        // Make sure the rotatePoint stays at the top
    	window.getPaintableSequence().shiftPaintableToTop(rotatePoint);
    }
    
    
    // method: mousePressed
    protected void mousePressed(MouseEvent mevt) {
        mouseMoved(mevt);
    }
    
    // method: mouseDragged
    protected void mouseDragged(MouseEvent mevt) {
        mouseMoved(mevt);
    }
    
    protected void mouseReleased(MouseEvent mevt) {
        mouseMoved(mevt);
    }
    
    // add various actions
    protected void addActions() {
        // add the mouse actions for the graphics window
        adapter.addMouseMovedAction(mouseMoved);
        adapter.addMousePressedAction(mousePressed);
        adapter.addMouseDraggedAction(mouseDragged);
        adapter.addMouseReleasedAction(mouseReleased);
    }
    
    
    
    
    private void ResetCards()
    {
    	ResetCards(true);
    }
    
    protected void ResetCards(boolean doRepaint)
    {
    	this.cards = WebImageTools.readImagesAsPaintableLite("http://www.ccs.neu.edu/jpt/images/jfitz_cards_jpg/", "imagelist.txt");
    	this.cardSequence = new PaintableSequence(cards);
    	
    	
        // obtain a permutation for randomizing the cards
        int[] permutation = ProbStatTools.randomPermutation(cards.length);
        
        window.clearSequence();
        
        int wmax = getMaxWidth();
        int hmax = getMaxHeight();
        
        for (int i = 0; i < cards.length; i++) {
            // pick a random card using the permutation
            int k = permutation[i];

            // compute a random position for this card
            int x = MathUtilities.randomInt(5, wmax);
            int y = MathUtilities.randomInt(5, hmax);
            
            // move this card
            cards[k].move(x, y);
        }
        window.appendSequence(cards);
        
        // Add center point not in the main cardSequence
        rotatePoint.moveCenterTo(rotatePoint.getOriginalCenter());
        window.addPaintableAtTop(rotatePoint);
        
        if (doRepaint)
        	window.repaint();
    }
    
    protected void RandomTranslateAndRotate() {
        // exit if the cards could not be or have not been read
        if (this.cards == null)
            return;
        
        // Reshow cards if the graphics have been cleared
        if (window.getPaintableSequence().length() == 0)
        	ResetCards(false);

        cardSequence.setMutateAsItems();

        cardSequence.addPostMutation(Mutator.randomTranslateRotate(getMaxWidth(), 180));

        //window.clearSequence();
        
        EnsureAllCardsVisible();
        
        //window.appendSequence(cards);
        
        window.repaint();	
    }
    
    protected void RandomRotateAsGroup()
    {
        // exit if the cards could not be or have not been read
        if (this.cards == null)
            return;
        
        // Reshow cards if the graphics have been cleared
        if (window.getPaintableSequence().length() == 0)
        	ResetCards(false);
        
        cardSequence.setMutateAsGroup();

        // Get the coordinates of the rotate point
        int x = (int)rotatePoint.getCenter().getX();
        int y = (int)rotatePoint.getCenter().getY();
        
        System.out.println(x + ", " + y);
        
        
        // Rotate a random angle about the rotate point 
        cardSequence.addPostMutation(TransformFactory.randomRotate(
        		x,
        		y,
        		180));
        
        window.repaint();
    }
    
    // method: clear graphics window
    protected void clear() {
        window.clearPanelAndSequence();
        window.repaint();
    }

    
    private void EnsureAllCardsVisible()
    {
        int wmax = getMaxWidth();
        int hmax = getMaxHeight();

        // Make sure all cards are at least partially visible
        for (int i = 0; i < cards.length; i++) {
        	double cardX = cards[i].getCorner().x;
        	double cardY = cards[i].getCorner().y;
        	
        	if ((cardX < 0) || (cardY < 0))
        	{
        		if (cardX < 0)
        			cardX = -cardX;
        		if (cardY < 0)
        			cardY = -cardY;
        		cards[i].moveCenterTo(cardX, cardY);
        	}
            if ((cardX > wmax) && (cardY > hmax))
            	cards[i].move(wmax - cardX, hmax - cardY);
            else if (cardX > wmax)
            	cards[i].move(wmax - cardX, cardY);
            else if (cardY > hmax)
            	cards[i].move(cardX, hmax - cardY);
        }
    }
    
    private XPoint2D getCenter()
    {
    	return new XPoint2D(window.getBufferWidth() / 2.0, window.getBufferHeight() / 2.0);
    	
    }
    private int getMaxWidth()
    {
    	return getMaxDimension(window.getBufferWidth(), cards[0].getImageWidth(), 5);
    }

    private int getMaxHeight()
    {
    	return getMaxDimension(window.getBufferHeight(), cards[0].getImageHeight(), 5);
    }

    private int getMaxDimension(int windowDimension, int imageDimension, int minDimension)
    {
    	return windowDimension - imageDimension - minDimension;
    }
    
    public static void main(String[] args) { 
    	JPTFrame.createQuickJPTFrame("Card Tricks", new Thomas_CardTricks());
    }
}
