import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;


public class Game {
	/*
	 * COLORS:
	 * GREEN
	 * RED
	 * YELLOW
	 * WHITE
	 * BLUE
	 * (COLORED)
	 */
	final static int GREEN = 0;
	final static int RED = 1;
	final static int YELLOW = 2;
	final static int WHITE = 3;
	final static int BLUE = 4;
	final static int COLORS = 5;
	final static int NR_PLAYERS = 2;
	static int CARDS_PER_PLAYER;
	final static int NR_OF_CARDS = 50;
	static int CARDS_IN_DECK;
	final static int MAX_HINTS = 8;
	final static int MAX_THUNDERS = 3;
	int hints;
	int thunders;
	static ArrayList<Card> cards;
	static Card[][] deck;
	static int[] deckCounter;
	static ArrayList<Card> trash;
	static Player[] players;
	static int currentPlayer;
	
	static int selectedPlayer;
	static int selectedCards;
	static int chosenColor;
	static int chosenValue;
	static boolean useColor;
	static boolean useValue;
	static boolean placeCard;
	static boolean trashCard;
	static boolean won;
	static boolean lost;
	
	//UI STUFF
	JFrame mainFrame;
	JPanel gamePanel;
	JPanel hintPanel;
	JPanel thunderPanel;
	JPanel buttonPanel;
	JPanel deckPanel;
	JLabel[][] deckUI;
	JButton colorButton;
	JButton valueButton;
	JButton nextMove;
	JButton placeCardButton;
	JButton trashCardButton;
	JLabel[] hintUI;
	JLabel[] thunderUI;
	static JPanel[] playersUI;
	
	
	public static void main(String[] args) {
		Game game = new Game();
		System.out.println("Player " + currentPlayer + "'s turn");
	}

	public Game(){
		init();
	}
	
	private void init(){
		CARDS_PER_PLAYER = (NR_PLAYERS < 3) ? 5 : 4;
		hints = MAX_HINTS;
		thunders = 0;
		createPlayers();
		currentPlayer = 0;
		resetMoveValues();
		addCards();
		CARDS_IN_DECK = cards.size();
		createUI();
		dealCards();
	}
	
	private void resetMoveValues(){
		selectedPlayer = -1;
		chosenColor = -1;
		chosenValue = -1;
		selectedCards = 0;
		useColor = useValue = trashCard = placeCard = false;
	}
	
	private void createPlayers(){
		players = new Player[NR_PLAYERS];
		playersUI = new JPanel[NR_PLAYERS];
		for(int i=0; i<NR_PLAYERS; ++i){
			players[i] = new Player(CARDS_PER_PLAYER);
			playersUI[i] = new JPanel();
		}
	}
	
	private void addCards(){
		cards = new ArrayList<>();
		deck = new Card[COLORS][5];
		deckCounter = new int[5];
		trash = new ArrayList<>();
		int[] dist = new int[]{3, 2, 2, 2, 1};
		int[][] tmp_cards = new int[][]{
				dist.clone(),
				dist.clone(),
				dist.clone(),
				dist.clone(),
				dist.clone()
			};
		for(int i=0; i<NR_OF_CARDS; ++i){
			Point p = null;
			do{
				p = randomPosition();
			} while(tmp_cards[p.x][p.y] == 0);
			tmp_cards[p.x][p.y]--;
			cards.add(new Card(p.x, (p.y + 1)));
		}
	}
	
	private boolean checkValidMove(){
		if(selectedCards == 0) return false;
		if(!hintAvailable() && (useColor || useValue)) return false;
		if((trashCard || placeCard) && selectedCards == 1) return true;
		int countCards = 0;
		for(int i=0; i<CARDS_PER_PLAYER; ++i){
			Player p = players[selectedPlayer];
			if(useColor){
				if(p.getCard(i).getColorInt() == chosenColor) countCards++;
			} else if(useValue){
				if(p.getCard(i).getValue() == chosenValue) countCards++;
			}
		}
		return (countCards == selectedCards);
	}
	
	private void createUI(){
		try {
			UIManager.setLookAndFeel( "javax.swing.plaf.nimbus.NimbusLookAndFeel" );
		} catch (ClassNotFoundException | InstantiationException
				| IllegalAccessException | UnsupportedLookAndFeelException e) {
			e.printStackTrace();
		}
		mainFrame = new JFrame("Hanabi");
		mainFrame.setSize(1000, 1000);
		mainFrame.setLocation(0, 0);
		mainFrame.setLayout(new BorderLayout());
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainFrame.getContentPane().setBackground(Color.black);
		mainFrame.setVisible(true);
		gamePanel = new JPanel();
		gamePanel.setLayout(new BoxLayout(gamePanel, BoxLayout.Y_AXIS));
		GridLayout deckLayout = new GridLayout(COLORS, 5);
		deckPanel = new JPanel(deckLayout);
		deckPanel.setBackground(Color.black);
		deckUI = new JLabel[COLORS][5];
		for(int i=0; i<COLORS; ++i){
			for(int j=0; j<5; ++j){
				JLabel tmp = new JLabel("Not set");
				tmp.setBackground(Color.black);
				tmp.setForeground(Color.white);
				deckUI[i][j] = tmp;
				deckPanel.add(deckUI[i][j]);
			}
		}
		hintPanel = new JPanel();
		thunderPanel = new JPanel();
		buttonPanel = new JPanel();
		colorButton = new JButton("Color");
		valueButton = new JButton("Value");
		nextMove = new JButton("Next");
		placeCardButton = new JButton("Place");
		trashCardButton = new JButton("Trash");
		buttonPanel.add(colorButton);
		buttonPanel.add(valueButton);
		buttonPanel.add(nextMove);
		buttonPanel.add(placeCardButton);
		buttonPanel.add(trashCardButton);
		mainFrame.add(gamePanel, BorderLayout.CENTER);
		gamePanel.add(hintPanel);
		gamePanel.add(thunderPanel);
		gamePanel.add(buttonPanel);
		gamePanel.add(deckPanel);
		hintUI = new JLabel[MAX_HINTS];
		for(int i=0; i<MAX_HINTS; ++i){
			JLabel tmp = new JLabel();
			tmp.setText("1");
			tmp.setBackground(Color.white);
			tmp.setName("Hint_" + i);
			hintPanel.add(tmp);
			hintUI[i] = tmp;			
		}
		thunderUI = new JLabel[MAX_THUNDERS];
		for(int i=0;i<MAX_THUNDERS; ++i){
			JLabel tmp = new JLabel();
			tmp.setText("0");
			tmp.setBackground(Color.cyan);
			tmp.setName("Thunder_" + i);
			thunderPanel.add(tmp);
			thunderUI[i] = tmp;
		}
		
		mainFrame.addKeyListener(new KeyListener(){			
			@Override
			public void keyTyped(KeyEvent e) {				
			}			
			@Override
			public void keyReleased(KeyEvent e) {
				if(e.getKeyCode() == KeyEvent.VK_SPACE){
					//TODO place card
				} else if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE){
					//TODO card to trash
				} else if(e.getKeyCode() == KeyEvent.VK_ENTER){
				}
			}			
			@Override
			public void keyPressed(KeyEvent e) {				
			}
		});
		colorButton.addMouseListener(new MouseListener(){			
			@Override
			public void mouseReleased(MouseEvent e) {
				if(selectedCards > 0){
					System.out.println("Please deselect all cards first");
					return;
				}
				useColor = true;
				useValue = false;
			}			
			@Override
			public void mousePressed(MouseEvent e) {
			}			
			@Override
			public void mouseExited(MouseEvent e) {
			}			
			@Override
			public void mouseEntered(MouseEvent e) {
			}			
			@Override
			public void mouseClicked(MouseEvent e) {
			}
		});
		valueButton.addMouseListener(new MouseListener(){			
			@Override
			public void mouseReleased(MouseEvent e) {
				if(selectedCards > 0){
					System.out.println("Please deselect all cards first");
					return;
				}
				useValue = true;
				useColor = false;
			}			
			@Override
			public void mousePressed(MouseEvent e) {
			}			
			@Override
			public void mouseExited(MouseEvent e) {
			}			
			@Override
			public void mouseEntered(MouseEvent e) {
			}			
			@Override
			public void mouseClicked(MouseEvent e) {
			}
		});
		nextMove.addMouseListener(new MouseListener(){			
			@Override
			public void mouseReleased(MouseEvent e) {
				if(!checkValidMove()){
					System.out.println("Please select all cards of the selected color/value");
					return;
				}
				//TODO get cards, reset background, update tooltips
				JPanel playerContainer = playersUI[selectedPlayer];
				int labels = 0;
				int indexLabel = 0;
				int index = 0;
				for(int i=0; i<playerContainer.getComponentCount(); ++i){
					if(playerContainer.getComponent(i).getClass() == JLabel.class){
						JLabel card = (JLabel) playerContainer.getComponent(i);
						Player p = players[selectedPlayer];
						if(card.getBackground() == Color.pink){
							indexLabel = labels;
							index = i;
							card.setBackground(Color.black);
							if(useColor){
								String color = null;
								switch(chosenColor){
								case 0:
									color = "green";
									break;
								case 1:
									color = "red";
									break;
								case 2:
									color = "yellow";
									break;
								case 3:
									color = "white";
									break;
								case 4:
									color = "blue";
									break;
								}
								p.setCardInfo(labels, p.getCardInfo(labels) + ", " + color);
							} else if(useValue){
								String value = "is " + chosenValue;
								p.setCardInfo(labels, p.getCardInfo(labels) + ", " + value);
							}
						} else {
							if(useColor){
								String color = "not ";
								switch(chosenColor){
								case 0:
									color += "green";
									break;
								case 1:
									color += "red";
									break;
								case 2:
									color += "yellow";
									break;
								case 3:
									color += "white";
									break;
								case 4:
									color += "blue";
									break;
								}
								p.setCardInfo(labels, p.getCardInfo(labels) + ", " + color);
							} else if(useValue){
								String value = "is not " + chosenValue;
								p.setCardInfo(labels, p.getCardInfo(labels) + ", " + value);
							}
						}
						card.setToolTipText(p.getCardInfo(labels));
						labels++;
					}
				}
				if(useColor || useValue){
					hintUI[hints-1].setText("0");
					hints--;
				} else if(trashCard){
					moveCardToTrash(indexLabel, index, playerContainer);
					if(hints != MAX_HINTS){
						hintUI[hints].setText("1");
						hints++;
						if(hints > MAX_HINTS) hints = MAX_HINTS;
					}
				} else if(placeCard){
					//trash.add(players[currentPlayer].getCard(indexLabel));
					Card c = players[currentPlayer].getCard(indexLabel);
					int color = c.getColorInt();
					int value = c.getValue();
					int deckValue = deckCounter[color] + 1;
					if(deckValue == value){
						//TODO good
						deckCounter[color]++;
						deck[color][value-1] = c;
						deckUI[color][value-1].setText("Value: " + value);
						deckUI[color][value-1].setForeground(c.getColor());
					} else {
						//TODO bad
						thunderUI[thunders++].setText("1");
						setWonOrLost();
						if(lost) return;
						moveCardToTrash(indexLabel, index, playerContainer);
					}
					dealCard(indexLabel, currentPlayer);
					players[currentPlayer].setCardInfo(indexLabel, "");
					c = players[currentPlayer].getCard(indexLabel);
					JLabel card = (JLabel) playerContainer.getComponent(index);
					card.setText("Value: " + c.getValue());
					card.setForeground(c.getColor());
				}
				System.out.println("Player " + currentPlayer + " has finished his move!");
				currentPlayer++;
				if(currentPlayer == NR_PLAYERS) currentPlayer = 0;
				resetMoveValues();
				System.out.println("Player " + currentPlayer + "'s turn");
			}			
			@Override
			public void mousePressed(MouseEvent e) {
			}			
			@Override
			public void mouseExited(MouseEvent e) {
			}			
			@Override
			public void mouseEntered(MouseEvent e) {
			}			
			@Override
			public void mouseClicked(MouseEvent e) {
			}
		});
		trashCardButton.addMouseListener(new MouseListener(){			
			@Override
			public void mouseReleased(MouseEvent e) {
				if(selectedCards > 0){
					System.out.println("Please deselect all cards first");
					return;
				}
				useColor = false;
				useValue = false;
				placeCard = false;
				trashCard = true;
			}			
			@Override
			public void mousePressed(MouseEvent e) {
			}			
			@Override
			public void mouseExited(MouseEvent e) {
			}			
			@Override
			public void mouseEntered(MouseEvent e) {
			}			
			@Override
			public void mouseClicked(MouseEvent e) {
			}
		});
		placeCardButton.addMouseListener(new MouseListener(){			
			@Override
			public void mouseReleased(MouseEvent e) {
				if(selectedCards > 0){
					System.out.println("Please deselect all cards first");
					return;
				}
				useColor = false;
				useValue = false;
				trashCard = false;
				placeCard = true;
			}			
			@Override
			public void mousePressed(MouseEvent e) {
			}			
			@Override
			public void mouseExited(MouseEvent e) {
			}			
			@Override
			public void mouseEntered(MouseEvent e) {
			}			
			@Override
			public void mouseClicked(MouseEvent e) {
			}
		});
	}
	
	private void moveCardToTrash(int indexLabel, int index, JPanel playerContainer){
		trash.add(players[currentPlayer].getCard(indexLabel));
		dealCard(indexLabel, currentPlayer);
		players[currentPlayer].setCardInfo(indexLabel, "");
		Card c = players[currentPlayer].getCard(indexLabel);
		JLabel card = (JLabel) playerContainer.getComponent(index);
		card.setText("Value: " + c.getValue());
		card.setForeground(c.getColor());
	}
	
	private void dealCard(int cardID, int playerID){
		int index = random(CARDS_IN_DECK--);
		Card card = cards.remove(index);
		players[playerID].setCard(cardID, card);
	}
	
	private void dealCards(){
		int dealtCards = 0;
		for(; dealtCards < CARDS_PER_PLAYER; ++dealtCards){
			for(int i=0; i<NR_PLAYERS; ++i){
				int index = random(CARDS_IN_DECK--);
				Card card = cards.remove(index);
				players[i].setCard(dealtCards, card);
			}
		}
		/*
		 * PLAYER 0: SOUTH
		 * PLAYER 1: EAST
		 * PLAYER 2: NORTH
		 * PLAYER 3: WEST
		 */
		for(int i=0; i<NR_PLAYERS; ++i){
			JPanel playerContainer = playersUI[i];
			playerContainer.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
			BoxLayout containerLayout = null;
			String playerSide = null;
			switch(i){
			case 0:
				playerSide = BorderLayout.SOUTH;
				containerLayout = new BoxLayout(playerContainer, BoxLayout.X_AXIS);
				break;
			case 1:
				playerSide = BorderLayout.EAST;
				containerLayout = new BoxLayout(playerContainer, BoxLayout.Y_AXIS);
				break;
			case 2:
				playerSide = BorderLayout.NORTH;
				containerLayout = new BoxLayout(playerContainer, BoxLayout.X_AXIS);
				break;
			case 3:
				playerSide = BorderLayout.WEST;
				containerLayout = new BoxLayout(playerContainer, BoxLayout.Y_AXIS);
				break;
			}
			playerContainer.setLayout(containerLayout);
			playerContainer.setBackground(Color.black);
			for(int j=0; j<CARDS_PER_PLAYER; ++j){
				Card card = players[i].getCard(j);
				JLabel curr = new JLabel("Value: " + card.getValue());
				curr.setName(i + "_card" + j);
				//curr.setBorder(BorderFactory.createMatteBorder(1, 2, 1, 1, card.getColor()));
				curr.setToolTipText(players[i].getCardInfo(j));
				curr.setHorizontalAlignment(SwingConstants.CENTER);
				curr.setSize(50, 50);
				Font f = curr.getFont();
				curr.setFont(new Font(f.getFontName(), f.getStyle(), f.getSize()*2));
				curr.setForeground(card.getColor());
				curr.setBackground(Color.black);
				curr.setOpaque(true);
				curr.addMouseListener(new MouseListener() {					
					@Override
					public void mouseReleased(MouseEvent e) {
						if(!hintAvailable()){
							System.out.println("No hints left!");
							return;
						}
						if(!useColor && !useValue && !trashCard && !placeCard){
							System.out.println("Please choose move type first");
							return;
						}
						JLabel label = (JLabel) e.getComponent();
						String name = label.getName();
						int clickedPlayer = Integer.parseInt(name.substring(0, 1));
						if(clickedPlayer == currentPlayer){
							if(useColor || useValue){
								System.out.println("Please don't select your own cards, asshole!");
								return;
							} else if((trashCard || placeCard) && selectedCards == 1){
								System.out.println("You already selected a card!");
								return;
							}
						} else if(clickedPlayer != currentPlayer && (trashCard || placeCard)){
							System.out.println("Please select your own cards");
							return;
						}
						int clickedCard = Integer.parseInt(name.substring(name.length()-1));
						Card card = players[clickedPlayer].getCard(clickedCard);
						int clickedValue = card.getValue();
						int clickedColor = card.getColorInt();
						if(selectedPlayer == -1){
							selectedPlayer = clickedPlayer;
							chosenColor = clickedColor;
							chosenValue = clickedValue;
							selectedCards++;
							System.out.println("Selected player #" + selectedPlayer);
							label.setBackground(Color.pink);
							//selectedCards.add(clickedCard);
							System.out.println("Added card #" + clickedCard + "(" + card.getColorInt() + ", " + card.getValue() + ")");
							return;
						}
						if(selectedPlayer != clickedPlayer){
							System.out.println("Please choose a card of the selected player or unselect all cards and choose another player.");
							return;
						}
						if(chosenColor == -1){
							chosenColor = clickedColor;
						}
						if(chosenValue == -1){
							chosenValue = clickedValue;
						}
						/*if(!useColor && !useValue){
							if(chosenValue == clickedValue && chosenColor == clickedColor){
								//return; //TODO nothing to do here...
							} else if(chosenValue == clickedValue && chosenColor != clickedColor){
								//chosenColor = -2;
								useValue = true;
								//TODO different color => select all 4's
							} else if(chosenValue != clickedValue && chosenColor == clickedColor){
								//chosenValue = -2;
								useColor = true;
								//TODO same color => select all reds
							}
						}*/
						if(label.getBackground() == Color.pink){
							selectedCards--;
							label.setBackground(Color.black);
							System.out.println("Removed card #" + clickedCard + "(" + card.getColorInt() + ", " + card.getValue() + ")");
							if(selectedCards == 0){
								resetMoveValues();
							} else if(selectedCards == 1){
								useColor = useValue = false;
							}
						} else {
							if(useColor){
								if(chosenColor == clickedColor){
									//TODO add card
									//selectedCards.add(clickedCard);
									if(label.getBackground() != Color.pink){
										selectedCards++;
										label.setBackground(Color.pink);
										System.out.println("Added card #" + clickedCard + "(" + card.getColorInt() + ", " + card.getValue() + ")");
									}
								}
							} else if(useValue){
								if(chosenValue == clickedValue){
									//TODO add card
									//selectedCards.add(clickedCard);
									if(label.getBackground() != Color.pink){
										selectedCards++;
										label.setBackground(Color.pink);
										System.out.println("Added card #" + clickedCard + "(" + card.getColorInt() + ", " + card.getValue() + ")");
									}
								}
							}
						}
					}
					
					@Override
					public void mousePressed(MouseEvent e) {
					}
					
					@Override
					public void mouseExited(MouseEvent e) {
					}
					
					@Override
					public void mouseEntered(MouseEvent e) {
					}
					
					@Override
					public void mouseClicked(MouseEvent e) {
					}
				});
				playerContainer.add(curr, playerSide);
				playerContainer.add(Box.createRigidArea(new Dimension(15,15)));
			}
			mainFrame.add(playerContainer, playerSide);
		}
	}
	
	private boolean hintAvailable(){
		return (hints > 0);
	}
	
	private void setWonOrLost(){
		won = true;
		lost = false;
		for(int i=0; i<COLORS; ++i){
			if(deckCounter[i] != 5){
				won = false;
				break;
			}
		}
		if(thunders == MAX_THUNDERS){
			lost = true;
			won = false;
		}
		if(won) System.out.println("YOU WON!");
		if(lost) System.out.println("YOU LOST...");
	}
	
	private Point randomPosition(){
		Point p = new Point();
		p.x = random(5);
		p.y = random(5);
		return p;
	}
	
	private int random(int max){
		return (int) (Math.random() * max);
	}
	
	private void printArray(int[][] arr){
		for(int j=0; j<arr.length; ++j){
			for(int k=0; k<arr[0].length; ++k){
				System.out.print(arr[j][k] + ", ");
			}
			System.out.println("");
		}
	}
}
