/*
 * MyFirst solo Project Spring 2014
 * Matching Card Game
 */

package view;

import imagedata.Image;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;

import sounddata.SoundEffect;
import model.MatchingGame;

/**
 * This class creates the panel where the deck of cards is layed out.
 * 
 * @author Brian Dennis
 * @version 3-22 2014
 */
@SuppressWarnings("serial")
public final class CardPanel extends JPanel implements Observer, ActionListener {
    
    /** the delay of the timer. */
    private static final int MY_DELAY = 1000;
    
    /** size of buttons. */                                 //92, 136
    private static final Dimension BUTTON_SIZE = new Dimension(75, 107); //50, 75
    
    /** size of panels behind buttons. */                           //102, 146
    private static final Dimension CARD_PANEL_SIZE = new Dimension(85, 117); //60, 85
    
    /** size of this JPanel. */
    private static final Dimension PANEL_SIZE = new Dimension(1175, 492);
    
    /** the object representing the MatchingGame. */
    private final MatchingGame myGame;
    
    /** the frame of this GUI. */
    private final MatchingGameGUI myFrame;
    
    /** This array holds all of the JButtons representing each card. */
    private JButton[] myButtonArray;
    
    /** the timer causing a delay when cards are matched. */
    private final Timer myTimer;
    
    /** a count down for the timer, to give a delay when matching cards. */
    private int myCountDown;
    
    /** This boolean checks to see if the timer is running. */
    private boolean myTimerIsRunning;
    
    /**
     * The constructor initializes variables and starts making the panel.
     * 
     * @param theGame the MatchingGame object
     * @param theFrame of the entire GUI
     */
    public CardPanel(final MatchingGame theGame, final MatchingGameGUI theFrame) {
        
        super();
        myGame = theGame;
        myFrame = theFrame;
        myTimer = new Timer(MY_DELAY, this);
        myTimer.setRepeats(true);
        myCountDown = 1;
        start();
    }
    
    /** start method. */
    public void start() {
        
        SoundEffect.SHUFFLE.play();
        myButtonArray = new JButton[myGame.getDeck().size()];
        final JPanel[] panelArray = new JPanel[myGame.getDeck().size()];
        setBackground(Color.GREEN);
        for (int i = 0; i < myButtonArray.length; i++) {
            
            myButtonArray[i] = new JButton();
            myButtonArray[i].setPreferredSize(BUTTON_SIZE);
            myButtonArray[i].setIcon(myGame.getDeck().get(i).getBackImage());
            myButtonArray[i].setLayout(new GridLayout(3, 1));
  
            addListener(i);
            panelArray[i] = new JPanel();
            panelArray[i].setBackground(Color.GREEN);
            panelArray[i].setPreferredSize(CARD_PANEL_SIZE);
            
            add(panelArray[i]);
            panelArray[i].add(myButtonArray[i]);   
        }
        setPreferredSize(new Dimension(PANEL_SIZE));
        setVisible(true); 
    }
    
    /**
     * This method adds an action listener to each button, with an inner class containing the 
     * logic for the action.
     * 
     * @param theIndex the index of the JButton and associated Card object
     */
    private void addListener(final int theIndex) {
        
        myButtonArray[theIndex].addActionListener(new ActionListener() {
            
            /** This method applies actions to the associated card buttons.
             * @param theAction the action that corresponds to when a user clicks a card*/
            public void actionPerformed(final ActionEvent theAction) {
 
                myButtonArray[theIndex].setIcon(
                                            myGame.getDeck().get(theIndex).getFrontImage());
                myGame.compare(theIndex);
            }
        });
    }

    @Override
    public void update(final Observable theMatchingGame, final Object theObject) {
        
        removeListener();
        
        if (!myTimerIsRunning) {
            myTimer.start();
            myTimerIsRunning = true;
        }
    }
    
    /**
     * This method removes the listener from the JButtons so you cant click them while timer 
     * is ticking.
     */
    private void removeListener() {
        
        for (int i = 0; i < myButtonArray.length; i++) {
            
            final ActionListener[] listener = myButtonArray[i].getActionListeners();  
            for (final ActionListener buttonListener : listener) {  
                
                myButtonArray[i].removeActionListener(buttonListener);  
            } 
        }
    }
    
    /**
     * This method reinstates the listeners that were removed when the timer was activated.
     */
    private void reinstateListener() {
        
        for (int i = 0; i < myButtonArray.length; i++) {
            
            addListener(i);
        }
    }
    
    /**
     * This method checks to see if the game has ended, and if it has prompts user to play 
     * another game or end.
     */
    private void checkForEndOfGame() {
        
        if (myGame.gameIsOver()) {
            
            SoundEffect.APPLAUSE.play();
            final int choice = JOptionPane.showConfirmDialog(myFrame, 
                                                             "Do you want to play "
                            + "again?", "ALL MATCHES GONE!!", JOptionPane.YES_NO_OPTION);
            
            if (choice == 1) {
                
                myFrame.dispatchEvent(new WindowEvent(myFrame, 
                                                      WindowEvent.WINDOW_CLOSING));
            } else {
                
                final List<String> emptyList = new ArrayList<String>();
                myFrame.dispose();
                MatchingGameMain.changeValues(emptyList);
                start();
            }
        }
    }
 
    /**
     * This action method is for the timer that controls the flow after two card are clicked.
     * @param theAction action for the timer
     */
    @Override
    public void actionPerformed(final ActionEvent theAction) {
        
        final int index1 = myGame.getStatus().getIndex1();
        final int index2 = myGame.getStatus().getIndex2();
        final Image backImage = myGame.getDeck().get(index1).getBackImage();
        
        myCountDown--;
        if (myCountDown == 0) {
            
            if (myGame.getStatus().myCardsMatch()) {
                
                myButtonArray[index1].setVisible(false);
                myButtonArray[index2].setVisible(false);
                SoundEffect.CHIME.play();
                        
            } else {
                myButtonArray[index1].removeAll();
                myButtonArray[index2].removeAll();
                myButtonArray[index1].setIcon(backImage);
                myButtonArray[index2].setIcon(backImage);
                SoundEffect.FLIP.play();
            }
            myTimer.stop();
            myCountDown = 1;
            myTimerIsRunning = false;
            reinstateListener();
            checkForEndOfGame();
        }
    }
}