package jrummikub.view.impl;

import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.List;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import jrummikub.model.PlayerSettings;
import jrummikub.model.PlayerSettings.Type;
import jrummikub.util.Event;
import jrummikub.util.Event1;
import jrummikub.util.GameData;
import jrummikub.util.IEvent;
import jrummikub.util.IEvent1;
import jrummikub.util.Pair;
import jrummikub.view.IGameListPanel;

class GameListPanel extends JPanel implements IGameListPanel {
	private static final long serialVersionUID = 1L;

	private JLabel title;
	private JList gameList;
	private JButton joinButton;
	private JButton openNewGameButton;
	private JButton cancelButton;

	private Event1<GameData> joinEvent = new Event1<GameData>();
	private Event openNewGameEvent = new Event();
	private Event cancelEvent = new Event();

	private List<GameData> games = Collections.emptyList();

	/**
	 * Creates new game list panel
	 */
	GameListPanel() {
		setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.BOTH;
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.weightx = 1;
		c.weighty = 0;

		title = new JLabel();
		title.setFont(title.getFont().deriveFont(16.0f));
		title.putClientProperty("html.disable", Boolean.TRUE);
		add(title, c);

		add(Box.createVerticalStrut(3), c);

		createGameList();

		add(Box.createVerticalStrut(3), c);

		addButtons(c);

		setBorder(new CompoundBorder(new LineBorder(Color.BLACK),
				new EmptyBorder(10, 10, 10, 10)));
	}

	/**
	 * Creates the game list to be displayed in the panel
	 */
	private void createGameList() {
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.BOTH;
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.weightx = 1;
		c.weighty = 1;

		gameList = new JList();
		gameList.setCellRenderer(new GameDataCellRenderer());

		gameList.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {
					join();
				}
			}
		});

		gameList.addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				Object data = gameList.getSelectedValue();

				if (data instanceof GameData) {
					GameData gameData = (GameData) data;
					Pair<Integer, Integer> playerSlotCount = getPlayerSlotCount(gameData);
					joinButton.setEnabled(playerSlotCount.getFirst() < playerSlotCount
							.getSecond());
				} else {
					joinButton.setEnabled(false);
				}
			}
		});

		add(new JScrollPane(gameList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), c);
	}

	/**
	 * Adds all three buttons to the panel
	 * 
	 * @param c
	 *            constraints for button positioning
	 */
	private void addButtons(GridBagConstraints c) {
		joinButton = new JButton("Beitreten");
		c.gridwidth = 1;
		joinButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				join();
			}
		});
		joinButton.setEnabled(false);
		add(joinButton, c);

		c.weightx = 0;
		add(Box.createHorizontalStrut(10), c);

		openNewGameButton = new JButton("Neues Spiel");
		c.weightx = 1;
		add(openNewGameButton, c);
		openNewGameButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				openNewGameEvent.emit();
			}
		});

		c.weightx = 0;
		add(Box.createHorizontalStrut(10), c);

		cancelButton = new JButton("Abbrechen");
		c.weightx = 1;
		c.weighty = 0;
		c.gridwidth = GridBagConstraints.REMAINDER;
		add(cancelButton, c);
		cancelButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				cancelEvent.emit();
			}
		});
	}

	/**
	 * Resets panel if all games have been removed from list
	 */
	void reset() {
		games.clear();
	}

	@Override
	public void setChannelName(String name) {
		title.setText("Spiele in " + name + ":");
	}

	@Override
	public void setGameList(List<GameData> games) {
		this.games = games;

		Object currentGame = gameList.getSelectedValue();

		gameList.setListData(games.toArray());

		if (games.contains(currentGame)) {
			gameList.setSelectedValue(currentGame, false);
		} else if (!games.isEmpty()) {
			gameList.setSelectedIndex(0);
		}
	}

	@Override
	public IEvent getOpenNewGameEvent() {
		return openNewGameEvent;
	}

	@Override
	public IEvent1<GameData> getJoinEvent() {
		return joinEvent;
	}

	@Override
	public IEvent getCancelEvent() {
		return cancelEvent;
	}

	/**
	 * Emits a join event if a user selects a game and dedides to join it
	 */
	private void join() {
		Object data = gameList.getSelectedValue();

		if (data instanceof GameData) {
			joinEvent.emit((GameData) data);
		}
	}

	/**
	 * Counts the empty slots for network players in a game
	 * 
	 * @param gameData
	 *            of the game
	 * @return Pair of occupied spaces and vacant spaces
	 */
	private static Pair<Integer, Integer> getPlayerSlotCount(GameData gameData) {
		int total = gameData.getGameSettings().getPlayerList().size();
		int occupied = total;

		for (PlayerSettings player : gameData.getGameSettings().getPlayerList()) {
			if (player.getType() == Type.VACANT) {
				occupied--;
			}
		}

		return new Pair<Integer, Integer>(occupied, total);
	}

	/**
	 * Class presenting the game data in a readable way
	 */
	private static class GameDataCellRenderer extends JPanel implements
			ListCellRenderer {
		private static final long serialVersionUID = -892701906163443927L;
		JLabel hostLabel, playerCountLabel;

		/**
		 * Create new game data cell renderer
		 */
		GameDataCellRenderer() {
			setLayout(new GridBagLayout());
			GridBagConstraints c = new GridBagConstraints();
			c.fill = GridBagConstraints.BOTH;
			c.gridwidth = 1;
			c.weightx = 1;
			c.weighty = 1;

			hostLabel = new JLabel();
			hostLabel.setOpaque(true);
			hostLabel.setHorizontalAlignment(JLabel.LEFT);
			add(hostLabel, c);

			playerCountLabel = new JLabel();
			playerCountLabel.setOpaque(true);
			playerCountLabel.setHorizontalAlignment(JLabel.RIGHT);

			c.gridwidth = GridBagConstraints.REMAINDER;
			add(playerCountLabel, c);
		}

		@Override
		public Component getListCellRendererComponent(JList list, Object value,
				int index, boolean isSelected, boolean cellHasFocus) {
			String host = "", playerCount = "";

			if (value instanceof GameData
					&& ((GameData) value).getGameSettings() != null) {
				GameData gameData = (GameData) value;

				host = gameData.getHost();
				Pair<Integer, Integer> playerSlotCount = getPlayerSlotCount(gameData);

				playerCount = playerSlotCount.getFirst() + "/"
						+ playerSlotCount.getSecond();
			} else {
				host = String.valueOf(value);
			}

			hostLabel.setText(host);
			playerCountLabel.setText(playerCount);

			if (isSelected) {
				hostLabel.setBackground(list.getSelectionBackground());
				hostLabel.setForeground(list.getSelectionForeground());
				playerCountLabel.setBackground(list.getSelectionBackground());
				playerCountLabel.setForeground(list.getSelectionForeground());
			} else {
				hostLabel.setBackground(list.getBackground());
				hostLabel.setForeground(list.getForeground());
				playerCountLabel.setBackground(list.getBackground());
				playerCountLabel.setForeground(list.getForeground());
			}

			setEnabled(list.isEnabled());
			setFont(list.getFont());
			return this;
		}
	}
}
