/* @(#)Methods.java   17 September 2005 */

/* Useful imports */

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 Goyne_Cardgame extends DisplayPanel implements JPTConstants
{
    protected Goyne_Card[] 	cards			= new Goyne_Card[52];
    protected Goyne_Card[] 	rowHeaders		= new Goyne_Card[16];
    protected Goyne_Card[] 	cardStacks		= new Goyne_Card[8];
    protected Goyne_Card[] 	holdPiles		= new Goyne_Card[4];
    protected Goyne_Card[] 	victoryPiles	= new Goyne_Card[4];
    protected boolean[] pilesUsed		= {false, false, false, false};
    protected Integer	pileCount		= 4;
    
    protected final int BUFFERSIZE = 800;
    
    protected BufferedPanel window
        = new BufferedPanel(BUFFERSIZE, BUFFERSIZE);

    protected TablePanel mainPanel
        = new TablePanel(
            new Object[] {
                new Display(window,   null, "Graphics") },
            HORIZONTAL, 5, 5, NORTH);

    /*
     * TODO: Register click actions, preferably in a working manner
     * (Use wrapper for stacks?)
     * Directly registering each card results in no way to get the stack other than stupid hacks like checking position
     * (In addition to simply not working)
     */
    public Goyne_Cardgame() {
        add(mainPanel);
    }
    public static void main(String[] args) {
    	Goyne_Cardgame game = new Goyne_Cardgame();
        JPTFrame.createQuickJPTFrame("Goyne_Card Game", game);
        game.loadCards();
        //game.moveStack(1, 0);
    }
    public void loadCards() {
        String cardsURL  = "http://www.ccs.neu.edu/jpt/images/jfitz_cards/";
        String cardsList = "imagelist.txt";
        
        ImagePaintableLite[] cardImages = WebImageTools.readImagesAsPaintableLite(cardsURL, cardsList);
        if(cardImages.length < 52)
        	return;

        for(int i = 0; i < 8; i++) {
        	rowHeaders[i] = new Goyne_Card(75 * i + 10, 100);
        	cardStacks[i] = rowHeaders[i];
        }
        for(int i = 8; i < 12; i++) {
        	rowHeaders[i] = new Goyne_Card(71 * i + 10, 10);
        	rowHeaders[i].setPlay(false); // ick
        	holdPiles[i - 8] = rowHeaders[i];
        }
        for(int i = 12; i < 16; i++) {
        	rowHeaders[i] = new Goyne_Card(71 * i + 400, 10);
        	// should have rowHeaders[i].setPlay(false);, but that's being used for magic in validateMove
        	holdPiles[i - 12] = rowHeaders[i];
        }
        int s = 0, v = 1;
        for(int i = 0; i < 52; i++) {
        	if(v > 13) {
        		v = 1;
        		s++;
        	}
        	cards[i] = new Goyne_Card(s, v, cardImages[i]);
        }
        
        Collections.shuffle(Arrays.asList(cards));
        /*
         * TODO: Introduce method of controlling shuffling for preset games, etc.
         */
        int currentStack = -1;
        for(int i = 0; i < 52; i++) {
        	if((i < 29 && i % 7 == 0) || (i > 29 && (i - 28) % 6 == 0))
        		currentStack++;
        	
        	cards[i].setParent(cardStacks[currentStack]);
        	cardStacks[currentStack] = cards[i];
        }
        /*
         * TODO: Replace this ugliness with CardStack class
         */
    	
    	showCards();
    }
    public void showCards() {
        window.clearSequence();
        
        for (int i = 0; i < 8; i++) {
        	cardStacks[i].draw(window);
        }
        
        window.repaint();
        return;
    }
    public void moveCard(Goyne_Card source, Integer stack, Integer target) {
    	cardStacks[stack]	= source.parent;
    	source.parent		= cardStacks[target];
    	cardStacks[target]	= source;
    }
    /*
     * TODO:
     * Return value magic is stupid
     * 
     * Introduce checking for suit
     * Scan for empty stacks, using them to store cards if needed
     * Condense logic?
     * 
     * Move to CardStack, and get all actual game logic out of the main class?
     * 		Removes need for special code for hold/victory, as they can be subclasses with different validation
     * 		Should the game even know they're special?
     */
    public Integer validateMove(Integer source, Integer target) {
    	Goyne_Card cs = cardStacks[source];
    	Goyne_Card ct = cardStacks[target];
    	if(ct.getValue() == 0)
    		return 0;
    	if(!ct.parent.getPlay()) // safe due to that value is 0 on parentless cards
    		return -1; // There's already a card in the targeted holding stack
    	for(int i = 0; cs.getValue() != 0; cs = cs.parent, i++) {
    		if(cs.getValue() == ct.getValue() - 1)
    			return i;
    		if(cs.getValue() != cs.parent.getValue() - 1)
    			return -1;
    	}
    	return -1;
    }

    public boolean validateVictoryMove(Integer source, Integer target) {
    	if(cardStacks[source].getSuit() != target - 12) // Wrong suit
    		return false;
    	if(cardStacks[target].getValue() + 1 != cardStacks[source].getValue()) // Not the next card
    		return false;
    	return true;
    }
    /* TODO:
     * Needs a way to check if the target stack is a holding stack, and if so mark it as used
     * Fails miserably on victory stacks, as does validate -- somewhat fixed
     */
    public boolean moveStack(Integer source, Integer target) {
    	if(target > 11) { // i.e. victory stack
    		if(validateVictoryMove(source, target)) {
    			moveCard(cardStacks[source], source, target);
    			return true;
    		}
    		else
    			return false;
    	}
    	Integer moveCount = validateMove(source, target);
    	if(moveCount > pileCount || moveCount == -1)
    		return false;
    	Goyne_Card ct = cardStacks[source];
    	while(moveCount > 0)  // This is stupid.  Merge this method with moveCard and make it so that so that holding stacks/victory piles work with it
    		ct = ct.parent;
    	moveCard(ct, source, target);
    	return true;
    }
    /*
     * Victory Piles:
     * 		Hide top card, then move card there.
     * 		Cards under the top do not matter due to lack of ability to move them down.
     */
    
    /*
     * Holding Spots:
     * 		Just a normal stack with a size limit of one
     * 		Stack size limit should be controlled by CardStack should it exist
     */
    
    /*
     * Click effector:
     * 		Click triggers on card -> sets card's stack as active
     * 		Next click on card results in deselect or validateMove/moveStack
     * 		Implement in CardStack?
     */
}
