package server;

import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

import org.json.JSONArray;

import card.Card;
import card.CardInfo;
import player.BasePlayer;

public class SwingClientGame extends ClientGame {
	//UI STUFF
	JFrame readyFrame;
	JButton readyButton;
	
	JFrame mainFrame;
	JPanel gamePanel;
	JPanel hintPanel;
	JPanel flashPanel;
	JPanel playingDeckPanel;
	JLabel[][] playingDeckUi;
	JLabel[] hintUi;
	JLabel[] flashUi;
	GridBagConstraints layoutConstr;
	// Hints
	JFrame hintFrame;
	JLabel[] hintTypes;

	int clickedPlayer;

	Font font;
	final static Color BACKGROUND = Color.black;
	private static final ImageIcon HINT_0 = new ImageIcon(new ImageIcon("hint_0.png").getImage().getScaledInstance(40, 40, Image.SCALE_SMOOTH));
	private static final ImageIcon HINT_1 = new ImageIcon(new ImageIcon("hint_1.png").getImage().getScaledInstance(40, 40, Image.SCALE_SMOOTH));
	private static final ImageIcon FLASH_0 = new ImageIcon(new ImageIcon("flash_0.png").getImage().getScaledInstance(40, 40, Image.SCALE_SMOOTH));
	private static final ImageIcon FLASH_1 = new ImageIcon(new ImageIcon("flash_1.png").getImage().getScaledInstance(40, 40, Image.SCALE_SMOOTH));

	public static void main(String[] args){
		int port = 1337;
		String hostname = "localhost";
		Connector c = null;
		try {
			c = new TcpConnector(hostname, port);			
		} catch(UnknownHostException uhe) {
			//FIXME
			Utils.log(Utils.DEBUG, "Host not known!");
			System.exit(0);
		} catch(IOException ex) {
			//FIXME
			Utils.log(Utils.DEBUG, "IOException!");
			System.exit(0);
		}
		Communicator comm = new VibeDumb(c);
		Utils.log(Utils.DEBUG, comm.getConnector().toString());
		try {
			new SwingClientGame(comm);
		} catch(IOException ex) {
			//TODO inform user
			// comm = ...
			// new SwingClientGame(comm);
		}
	}

	public SwingClientGame(Communicator comm) throws IOException {
		super(comm);
		readyFrame = new JFrame("Are you ready?");
		readyFrame.setSize(100, 100);
		
		readyButton = new JButton("Ready!");
		readyButton.addMouseListener(new MouseListener() {			
			@Override
			public void mouseReleased(MouseEvent e) {
				if(readyButton.getText().equals("Ready!")){
					readyButton.setText("You are ready.");
					sendReady();
				} else {
					readyButton.setText("Ready!");
					sendUnReady();
				}
			}
			
			@Override
			public void mousePressed(MouseEvent e) {
			}
			
			@Override
			public void mouseExited(MouseEvent e) {
			}
			
			@Override
			public void mouseEntered(MouseEvent e) {
			}
			
			@Override
			public void mouseClicked(MouseEvent e) {
			}
		});
		
		readyFrame.add(readyButton);
		readyFrame.validate();
		readyFrame.setVisible(true);
	}

	@Override
	protected void sendReady() {
		while(true) {
			try {
				super.sendReady();
				break;
			} catch(IOException ex) {
				if(!handleIOException(ex)) break;
			}
		}
	}

	@Override
	protected void sendUnReady() {
		while(true) {
			try {
				super.sendUnReady();
				break;
			} catch(IOException ex) {
				if(!handleIOException(ex)) break;
			}
		}
	}

	private void createHintUi(){
		hintFrame = new JFrame("Hint UI");
		hintFrame.setSize(450, 150);
		hintFrame.setLayout(new GridLayout(0, 5));
		hintFrame.getContentPane().setBackground(BACKGROUND);
		hintTypes = new JLabel[10];
		hintTypes[0] = new JLabel("1", SwingConstants.CENTER);
		hintTypes[0].setForeground(Color.white);
		hintTypes[0].addMouseListener(new HintMouseListener(true, 0));
		hintTypes[1] = new JLabel("2", SwingConstants.CENTER);
		hintTypes[1].setForeground(Color.white);
		hintTypes[1].addMouseListener(new HintMouseListener(true, 1));
		hintTypes[2] = new JLabel("3", SwingConstants.CENTER);
		hintTypes[2].setForeground(Color.white);
		hintTypes[2].addMouseListener(new HintMouseListener(true, 2));
		hintTypes[3] = new JLabel("4", SwingConstants.CENTER);
		hintTypes[3].setForeground(Color.white);
		hintTypes[3].addMouseListener(new HintMouseListener(true, 3));
		hintTypes[4] = new JLabel("5", SwingConstants.CENTER);
		hintTypes[4].setForeground(Color.white);
		hintTypes[4].addMouseListener(new HintMouseListener(true, 4));
		hintTypes[5] = new JLabel("G", SwingConstants.CENTER);
		hintTypes[5].setForeground(Color.green);
		hintTypes[5].addMouseListener(new HintMouseListener(false, GREEN));
		hintTypes[6] = new JLabel("R", SwingConstants.CENTER);
		hintTypes[6].setForeground(Color.red);
		hintTypes[6].addMouseListener(new HintMouseListener(false, RED));
		hintTypes[7] = new JLabel("Y", SwingConstants.CENTER);
		hintTypes[7].setForeground(Color.yellow);
		hintTypes[7].addMouseListener(new HintMouseListener(false, YELLOW));
		hintTypes[8] = new JLabel("W", SwingConstants.CENTER);
		hintTypes[8].setForeground(Color.white);
		hintTypes[8].addMouseListener(new HintMouseListener(false, WHITE));
		hintTypes[9] = new JLabel("B", SwingConstants.CENTER);
		hintTypes[9].setForeground(Color.blue);
		hintTypes[9].addMouseListener(new HintMouseListener(false, BLUE));
		for(int i=0; i<hintTypes.length; ++i){
			hintTypes[i].setBackground(BACKGROUND);
			hintTypes[i].setFont(font);
			hintFrame.add(hintTypes[i]);
		}
		hintFrame.validate();
		hintFrame.setVisible(false);
	}

	private void toggleHintUi(int x, int y){
		hintFrame.setLocation(x, y);
		hintFrame.setVisible(!hintFrame.isVisible());
	}

	@Override
	protected void createUi() {
		readyFrame.setVisible(false);
		clickedPlayer = 0;
		layoutConstr = new GridBagConstraints();
		mainFrame = new JFrame("Hanabi - Player " + (ownId+1) + " (" + players[ownId].getName() + ")");
		mainFrame.setSize(1000, 650);
		mainFrame.setLocation(0, 0);
		mainFrame.setLayout(new GridBagLayout());
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainFrame.getContentPane().setBackground(BACKGROUND);
		mainFrame.setVisible(true);
		gamePanel = new JPanel();
		gamePanel.setLayout(new BoxLayout(gamePanel, BoxLayout.Y_AXIS));
		GridLayout deckLayout = new GridLayout(COLORS, 5);

		playingDeckPanel = new JPanel(deckLayout);
		playingDeckPanel.setBackground(BACKGROUND);
		playingDeckUi = new JLabel[COLORS][5];
		for(int i=0; i<COLORS; ++i){
			for(int j=0; j<5; ++j){
				JLabel tmp = new JLabel("X");
				tmp.setBackground(BACKGROUND);
				tmp.setForeground(Color.white);
				tmp.setFont(font);
				tmp.setHorizontalAlignment(JLabel.CENTER);
				playingDeckUi[i][j] = tmp;
				playingDeckPanel.add(playingDeckUi[i][j]);
			}
		}
		hintPanel = new JPanel();
		hintPanel.setBackground(BACKGROUND);
		flashPanel = new JPanel();
		flashPanel.setBackground(BACKGROUND);

		//first empty cell
		layoutConstr.gridx = 0;
		layoutConstr.gridy = 0;
		layoutConstr.weightx = 0.2;
		layoutConstr.weighty = 0.1;
		layoutConstr.gridwidth = 1;
		mainFrame.add(new JPanel(), layoutConstr);

		layoutConstr.gridx = 1;
		if(nPlayers < 5){
			layoutConstr.weightx = 0.6;
			layoutConstr.gridwidth = 1;
			int index = (nPlayers == 2) ? 1 : 2;
			JPanel playerPanel = ((SwingPlayer) players[getPlayersSightId(index)]).getPlayerUi();
			playerPanel.setLayout(new BoxLayout(playerPanel, BoxLayout.X_AXIS));
			mainFrame.add(playerPanel, layoutConstr);
		} else {
			layoutConstr.weightx = 0.3;
			layoutConstr.gridwidth = 1;
			JPanel playerPanel = ((SwingPlayer) players[getPlayersSightId(3)]).getPlayerUi();
			playerPanel.setLayout(new BoxLayout(playerPanel, BoxLayout.X_AXIS));
			mainFrame.add(playerPanel, layoutConstr);
			layoutConstr.gridx = 2;
			playerPanel = ((SwingPlayer) players[getPlayersSightId(2)]).getPlayerUi();
			playerPanel.setLayout(new BoxLayout(playerPanel, BoxLayout.X_AXIS));
			mainFrame.add(playerPanel, layoutConstr);
		}

		//second empty cell
		layoutConstr.gridx = (nPlayers < 5) ? 2 : 3;
		layoutConstr.weightx = 0.2;
		layoutConstr.gridwidth = 1;
		mainFrame.add(new JPanel(), layoutConstr);

		//4th or 5th player
		layoutConstr.gridx = 0;
		layoutConstr.gridy = 1;
		layoutConstr.weightx = 0.2;
		layoutConstr.weighty = 0.8;
		layoutConstr.gridwidth = 1;
		if(nPlayers == 2 || nPlayers == 3){
			mainFrame.add(new JPanel(), layoutConstr);
		} else {
			int index = (nPlayers == 4) ? 3 : 4;
			JPanel playerPanel = ((SwingPlayer) players[getPlayersSightId(index)]).getPlayerUi();
			playerPanel.setLayout(new BoxLayout(playerPanel, BoxLayout.Y_AXIS));
			mainFrame.add(playerPanel, layoutConstr);
		}

		layoutConstr.gridx = 1;
		layoutConstr.weightx = 0.6;
		layoutConstr.gridwidth = (nPlayers < 5) ? 1 : 2;
		mainFrame.add(gamePanel, layoutConstr);

		//2nd player
		layoutConstr.gridx = (nPlayers < 5) ? 2 : 3;
		layoutConstr.weightx = 0.2;
		layoutConstr.gridwidth = 1;
		if(nPlayers == 2){
			mainFrame.add(new JPanel(), layoutConstr);
		} else {
			JPanel playerPanel = ((SwingPlayer) players[getPlayersSightId(1)]).getPlayerUi();
			playerPanel.setLayout(new BoxLayout(playerPanel, BoxLayout.Y_AXIS));
			mainFrame.add(playerPanel, layoutConstr);
		}

		//third empty cell
		layoutConstr.gridx = 0;
		layoutConstr.gridy = 2;
		layoutConstr.weightx = 0.2;
		layoutConstr.weighty = 0.1;
		layoutConstr.gridwidth = 1;
		mainFrame.add(new JPanel(), layoutConstr);

		layoutConstr.gridx = 1;
		layoutConstr.weightx = 0.6;
		layoutConstr.gridwidth = (nPlayers < 5) ? 1 : 2;
		JPanel playerPanel = ((SwingPlayer) players[getPlayersSightId(0)]).getPlayerUi();
		playerPanel.setLayout(new BoxLayout(playerPanel, BoxLayout.X_AXIS));
		mainFrame.add(playerPanel, layoutConstr);

		//fourth empty cell
		layoutConstr.gridx = (nPlayers < 5) ? 2 : 3;
		layoutConstr.weightx = 0.2;
		layoutConstr.gridwidth = 1;
		mainFrame.add(new JPanel(), layoutConstr);

		gamePanel.add(hintPanel);
		gamePanel.add(flashPanel);
		gamePanel.add(playingDeckPanel);
		hintUi = new JLabel[MAX_HINTS];
		for(int i=0; i<MAX_HINTS; ++i){
			JLabel tmp = new JLabel();
			tmp.setIcon(HINT_1);
			tmp.setForeground(Color.white);
			tmp.setName("Hint_" + i);
			tmp.setFont(font);
			hintPanel.add(tmp);
			hintUi[i] = tmp;
		}
		flashUi = new JLabel[MAX_FLASHS];
		for(int i=0;i<MAX_FLASHS; ++i){
			JLabel tmp = new JLabel();
			tmp.setIcon(FLASH_0);
			tmp.setForeground(Color.white);
			tmp.setName("Flash_" + i);
			tmp.setFont(font);
			flashPanel.add(tmp);
			flashUi[i] = tmp;
		}
		mainFrame.validate();
		createHintUi();
	}

	@Override
	public void loadFont() {
		font = new Font("Shanghai", Font.PLAIN, fontSize);
	}

	@Override
	protected BasePlayer createPlayer(int id, int nrOfCards, String name){
		return new SwingPlayer(id, nrOfCards, name);
	}

	@Override
	protected BasePlayer createPlayer(Move m){
		return new SwingPlayer(m);
	}

	@Override
	protected void updateUi() {
		for(int i=0; i<hints; ++i){
			hintUi[i].setIcon(HINT_1);
		}
		for(int i=hints; i<MAX_HINTS; ++i){
			hintUi[i].setIcon(HINT_0);
		}
		for(int i=0; i<flashs; ++i){
			flashUi[i].setIcon(FLASH_1);
		}
		for(int i=flashs; i<MAX_FLASHS; ++i){
			flashUi[i].setIcon(FLASH_0);
		}
		for(int i=0; i<playingDeck.length; ++i){
			for(int j=0; j<playingDeck[i].length; ++j){
				String value = String.valueOf(playingDeck[i][j].getValue() + 1);
				playingDeckUi[i][j].setText(value);
				playingDeckUi[i][j].setForeground(getColor(playingDeck[i][j].getColorInt()));
			}
		}
	}

	private static Color getColor(int id){
		Color color = null;
		switch(id){
		case 0:
			color = Color.green;
			break;
		case 1:
			color = Color.red;
			break;
		case 2:
			color = Color.yellow;
			break;
		case 3:
			color = Color.white;
			break;
		case 4:
			color = Color.blue;
			break;
		}
		return color;
	}

	private class SwingPlayer extends BasePlayer {
		JPanel playerUi;
		JButton playerName;
		JLabel[] cardUi;
		PlaceListener placeListener;

		public SwingPlayer(int id, int nrOfCards, String name){
			super(id, nrOfCards, name);
			playerUi = new JPanel();
			playerUi.setBackground(BACKGROUND);
			playerName = new JButton();
			String text = name;
			if(ownId == id){
				playerName.setEnabled(false);
				text += " (You)";
			} else {
				playerName.addMouseListener(new HintListener(id));
			}
			playerName.setText(text);
			playerUi.add(playerName);
			cardUi = new JLabel[nrOfCards];
			for(int i=0; i<cardUi.length; ++i){
				cardUi[i] = new JLabel();
				cardUi[i].setFont(font);
				cardUi[i].setBackground(BACKGROUND);
				playerUi.add(cardUi[i]);
			}
		}

		public SwingPlayer(Move m) {
			super(m);
			playerUi = new JPanel();
			playerUi.setBackground(BACKGROUND);
			playerName = new JButton(name);
			playerName.addMouseListener(new HintListener(id));
			playerUi.add(playerName);
			cardUi = new JLabel[cards.length];
			for(int i=0; i<cardUi.length; ++i){
				JLabel uiCard = new JLabel(String.valueOf(cards[i].getValue() + 1));
				uiCard.setForeground(getColor(cards[i].getColorInt()));
				uiCard.setBackground(BACKGROUND);
				uiCard.setFont(font);
				uiCard.addMouseListener(new PlaceListener(cards[i]));
				cardUi[i] = uiCard;
				playerUi.add(cardUi[i]);
			}
		}

		@Override
		public void setCards(JSONArray ja){
			super.setCards(ja);
			for(int i=0; i<cards.length; ++i){
				if(cardUi[i] == null) cardUi[i] = new JLabel();
				JLabel tmp = cardUi[i];
				String text = "";
				Color color = null;
				if(ownId == id){
					text = "?";
					color = Color.gray;
					tmp.removeMouseListener(placeListener);
					placeListener = new PlaceListener(cards[i]);
					tmp.addMouseListener(placeListener);
				} else {
					text = String.valueOf(cards[i].getValue()+1);
					color = getColor(cards[i].getColorInt());
				}
				tmp.setText(text);
				tmp.setForeground(color);
				List<CardInfo> infos = cards[i].getCardInfos();
				String toolTipText = "";
				for(CardInfo info : infos){
					toolTipText += info.isIs() ? "is " : "not ";
					if(info.getType() == Move.HINT_COLOR){
						toolTipText += Card.intColorToText(info.getWhat());
					} else {
						toolTipText += String.valueOf(info.getWhat() + 1);
					}
					toolTipText += " (From Player " + info.getFrom() + ")";
					toolTipText += "\n";
				}
				tmp.setToolTipText(toolTipText);
				cardUi[i] = tmp;
			}
		}

		public JPanel getPlayerUi(){
			return playerUi;
		}
	}

	private class HintMouseListener extends MouseAdapter {
		private boolean isValue;
		private int value;

		public HintMouseListener(boolean isValue, int value) {
			this.isValue = isValue;
			this.value = value;
		}

		@Override
		public void mouseReleased(MouseEvent e) {
				if(SwingUtilities.isLeftMouseButton(e)){
					while(true) {
						try {
							moveHint(isValue, value, clickedPlayer);
							break;
						} catch (IOException ex) {
							boolean retry = handleIOException(ex);
							Utils.log(Utils.DEBUG, "Could not execute your hint move.");
							if(!retry) break;
						}
						toggleHintUi(e.getXOnScreen(), e.getYOnScreen());
					}
				}
		}
	}

	private class HintListener extends MouseAdapter {
		private int playerId;

		public HintListener(int playerId){
			this.playerId = playerId;
		}

		@Override
		public void mouseReleased(MouseEvent e) {
			if(SwingUtilities.isLeftMouseButton(e)){
				clickedPlayer = playerId;
				toggleHintUi(e.getXOnScreen(), e.getYOnScreen());
			}
		}
	}

	private class PlaceListener extends MouseAdapter {
		private Card c;

		public PlaceListener(Card c){
			this.c = c;
		}

		@Override
		public void mouseReleased(MouseEvent e) {
			if(SwingUtilities.isLeftMouseButton(e)){
				while(true) {
					try {
						movePlaceCard(c);
						break;
					} catch (IOException ex) {
						boolean retry = handleIOException(ex);
						Utils.log(Utils.DEBUG, "Could not execute your hint move.");
						if(!retry) break;
					}
				}
			} else if(SwingUtilities.isRightMouseButton(e)){
				while(true) {
					try {
						moveTrashCard(c);
						break;
					} catch (IOException ex) {
						boolean retry = handleIOException(ex);
						Utils.log(Utils.DEBUG, "Could not execute your hint move.");
						if(!retry) break;
					}
				}
			}
		}
	}
	
	protected boolean handleIOException(IOException ex) { throw new RuntimeException(ex); }
	
}
