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.*;
import java.util.regex.*;



public class CardFun extends DisplayPanel {
    
    static String urlString =
        "http://www.ccs.neu.edu/jpt/images/jfitz_cards/";
    
    static ImagePaintableLite[] paintables =
        WebImageTools.readImagesAsPaintableLite(urlString,"");
    
    static int decksize = 52;
    
    PaintableSequence sequence;
    
    PaintableComponent envelope;
    
    MouseActionAdapter adapter;
    
    int baseX;  // minimum X in sequence bounds
    
    int baseY;  // minimum Y in sequence bounds
    
    int mouseX; // mouse X position adjusted for base X
    
    int mouseY; // mouse Y position adjusted for base Y
    
    int oldX;   // old mouse X position in drag operation
    
    int oldY;   // old mouse Y position in drag operation
    
    int newX;   // new mouse X position in drag operation
    
    int newY;   // new mouse Y position in drag operation
    
    boolean dragging = false;   // are we dragging?
    
    MutatablePaintable movable;  // what we are dragging
    
    
    MouseAction mouseMovedAction =
        new MouseAction() {
            public void mouseActionPerformed(MouseEvent evt) {
                mouseMoved(evt);
            }
    };
    
    MouseAction mousePressedAction =
        new MouseAction() {
            public void mouseActionPerformed(MouseEvent evt) {
                mousePressed(evt);
            }
    };
        
    MouseAction mouseDraggedAction =
        new MouseAction() {
            public void mouseActionPerformed(MouseEvent evt) {
                mouseDragged(evt);
            }
    };
      
    
    TextFieldView xTFV = new TextFieldView(100);
    TextFieldView yTFV = new TextFieldView(100);
    
    TablePanel mouseEcho =
        new TablePanel(
            new Object[] { "x", xTFV, "y", yTFV },
            HORIZONTAL, 10, 10, CENTER);
    
    TablePanel main;
    
    
    public CardFun(int howmany) {
        this(howmany, cardWidth());
    }
    
    
    public CardFun(int howmany, int shift) {
        this(howmany, shift, -1, -1);
    }
    
    
    public CardFun
        (int howmany, int shift, double width, double height)
    {
        sequence = randomCardSequence(howmany, shift);
        adjustBounds2D(width, height);
        
        envelope = new PaintableComponent(sequence);
        
        installMouseActions();
        
        installPanels();
        
        frame("Card Fun");
    }
    
    
    private void adjustBounds2D(double width, double height)
    {
        Rectangle2D bounds = sequence.getBounds2D();
        
        double x = bounds.getMinX();
        double y = bounds.getMinY();
        double w = bounds.getWidth();
        double h = bounds.getHeight();
        
        if (width < w)
            width = w;
        
        if (height < h)
            height = h;
        
        x -= (width - w) / 2;
        y -= (height - h) / 2;
        
        bounds = new Rectangle2D.Double(x, y, width, height);
        
        sequence.setDefaultBounds2D(bounds);
    }
    
    
    private void installMouseActions() {
        Rectangle2D bounds = sequence.getBounds2D();
        
        baseX = (int) bounds.getMinX();
        baseY = (int) bounds.getMinY();
        
        adapter = envelope.getMouseActionAdapter();
        
        adapter.addMouseMovedAction(mouseMovedAction);
        adapter.addMousePressedAction(mousePressedAction);
        adapter.addMouseDraggedAction(mouseDraggedAction);
    }
    
    
    private void mouseMoved(MouseEvent evt)
    {
        mouseX = evt.getX() + baseX;
        mouseY = evt.getY() + baseY;
        
        xTFV.setViewState(mouseX + "");
        yTFV.setViewState(mouseY + "");
    }
    
    
    private void mousePressed(MouseEvent evt)
    {
        mouseMoved(evt);
        
        oldX = evt.getX() + baseX;
        oldY = evt.getY() + baseY;
        
        movable = sequence.hits(oldX, oldY);
        
        shiftToFront(movable);
        
        dragging = movable != null;
    }
    
    
    private void mouseDragged(MouseEvent evt)
    {
        mouseMoved(evt);
        
        if (dragging) {
            newX = evt.getX() + baseX;
            newY = evt.getY() + baseY;
            
            movable.move(newX - oldX, newY - oldY);
            
            oldX = newX;
            oldY = newY;
        }
    }
    
    
    private void installPanels()
    {
        main = new TablePanel(
            new Object[] { envelope, mouseEcho },
            VERTICAL, 10, 10, CENTER);
        
        addObject(main);
    }
    
    
    public MutatableWrapper[] getShuffledDeck() {
        MutatableWrapper[] mutatables =
            new MutatableWrapper[paintables.length];
        
        int[] indices =
            ProbStatTools.selectWithNoRepetition
                (decksize, decksize);
       
        for (int i = 0; i < decksize; i++)
            mutatables[i] =
                new MutatableWrapper
                    (paintables[indices[i]]);
        
        return mutatables;
    }
    
    
    private void shiftToFront(MutatablePaintable mp) {
        if (mp == null)
            return;
        
        int index = -1;
        int length = sequence.length();
        
        for (int i = 0; i < length; i++) {
            if (mp == sequence.getMutatablePaintable(i)) {
                index = i;
                break;
            }
        }
        
        if (index <= 0)
            return;
        
        sequence.removePaintable(index);
        sequence.addPaintable(0, mp);
    }
    
    
    public PaintableSequence randomCardSequence
        (int howmany, int shift)
    {
        if (howmany > decksize)
            howmany = decksize;
        
        PaintableSequence sequence = new PaintableSequence();
        
        MutatableWrapper[] mutatables = getShuffledDeck();
        
        for (int i = 0; i < howmany; i++) {
            mutatables[i].move(i * shift, 0);
            sequence.appendPaintable(mutatables[i]);
        }
            
        return sequence;
    }
    
    
    public static int cardWidth() {
        return paintables[0].getImageWidth();
    }
    
    
    public static int cardHeight() {
        return paintables[0].getImageHeight();
    }
    
    
}
