package com.esir.sr.sweetsnake.gui; import java.awt.Dimension; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.esir.sr.sweetsnake.api.IClientForGui; import com.esir.sr.sweetsnake.api.IGuiForClient; import com.esir.sr.sweetsnake.component.ImagePanel; import com.esir.sr.sweetsnake.component.Toast; import com.esir.sr.sweetsnake.constants.ClientGuiConstants; import com.esir.sr.sweetsnake.dto.GameBoardDTO; import com.esir.sr.sweetsnake.dto.GameRequestDTO; import com.esir.sr.sweetsnake.dto.GameSessionDTO; import com.esir.sr.sweetsnake.dto.PlayerDTO; import com.esir.sr.sweetsnake.enumeration.PlayerStatus; import com.esir.sr.sweetsnake.view.AbstractView; import com.esir.sr.sweetsnake.view.ConnectionView; import com.esir.sr.sweetsnake.view.GameView; import com.esir.sr.sweetsnake.view.LobbyView; import com.esir.sr.sweetsnake.view.PlayersView; import com.esir.sr.sweetsnake.view.SessionsView; import com.esir.sr.sweetsnake.view.UnreachableServerView; /** * * @author Herminaƫl Rougier * @author Damien Jouanno * */ @Component public class ClientGui extends JFrame implements IGuiForClient { /********************************************************************************************** * [BLOCK] STATIC FIELDS **********************************************************************************************/ /** The serial version UID */ private static final long serialVersionUID = -4189434181017519666L; /** The logger */ private static final Logger log = LoggerFactory.getLogger(ClientGui.class); /********************************************************************************************** * [BLOCK] FIELDS **********************************************************************************************/ /** The client */ @Autowired(required = false) private IClientForGui client; /** The unreachable server view */ @Autowired private UnreachableServerView unreachableServerView; /** The connection view */ @Autowired private ConnectionView connectionView; /** The sessions view */ @Autowired private SessionsView sessionsView; /** The players view */ @Autowired private PlayersView playersView; /** The lobby view */ @Autowired private LobbyView lobbyView; /** The game view */ @Autowired private GameView gameView; /** The gui current view */ private AbstractView currentView; /** The GUI dimension */ private Dimension dimension; /********************************************************************************************** * [BLOCK] CONSTRUCTOR & INIT **********************************************************************************************/ /** * */ protected ClientGui() { super(); } /** * */ @PostConstruct protected void init() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { log.info("Initializing the Client GUI"); initFrameParameters(); } }); } /** * */ @PreDestroy protected void destroy() { log.info("Destroying the Client GUI"); } /********************************************************************************************** * [BLOCK] PRIVATE METHODS **********************************************************************************************/ /** * */ private void initFrameParameters() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); setTitle("SweetSnake"); final Image icon = Toolkit.getDefaultToolkit().getImage(ClientGui.class.getResource(ClientGuiConstants.ICON_PATH)); setIconImage(icon); dimension = new Dimension(ClientGuiConstants.GUI_WIDTH, ClientGuiConstants.GUI_HEIGHT); setSize(dimension); setPreferredSize(dimension); final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); final int X = screen.width / 2 - dimension.width / 2; final int Y = screen.height / 2 - dimension.height / 2; setBounds(X, Y, dimension.width, dimension.height); setContentPane(new ImagePanel(ClientGuiConstants.BG_PATH)); pack(); setVisible(true); } /** * * @param view * @param unbuildPrevious */ private void switchView(final AbstractView view, final boolean unbuildPrevious) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (currentView != null && unbuildPrevious) { currentView.unbuild(); } view.build(); getContentPane().removeAll(); getContentPane().add(view); refreshUI(); currentView = view; log.debug("View switched to {}", view.getClass().getSimpleName()); } }); } /** * */ private void refreshUI() { revalidate(); repaint(); } /** * * @param parent * @return */ private JOptionPane getOptionPane(final JComponent parent) { JOptionPane pane = null; if (!(parent instanceof JOptionPane)) { pane = getOptionPane((JComponent) parent.getParent()); } else { pane = (JOptionPane) parent; } return pane; } // private void displayInfoMessage(final String title, final String message) { // new Thread(new Runnable() { // @Override // public void run() { // JOptionPane.showMessageDialog(Gui.this, message, title, JOptionPane.INFORMATION_MESSAGE); // } // }).start(); // } /** * * @param message */ private void displayToast(final String message) { Toast.displayToast(this, message); } /** * * @param message * @param buttonsText * @return */ private int displayCustomQuestion(final String title, final String message, final String[] buttonsText) { final Object[] buttons = new Object[buttonsText.length]; int i = 0; for (final String buttonText : buttonsText) { final JButton button = new JButton(buttonText); button.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { final JOptionPane pane = getOptionPane((JComponent) e.getSource()); pane.setValue(button); } }); buttons[i] = button; i++; } return JOptionPane.showOptionDialog(this, message, title, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, buttons, buttons[0]); } /** * * @param players * @param isStarted */ private void refreshLobbyView(final List<PlayerDTO> players, final boolean isStarted) { lobbyView.setPlayers(players); lobbyView.refreshPlayers(); boolean allReady = true; for (final PlayerDTO player : players) { if (player.getStatus() != PlayerStatus.READY) { allReady = false; } if (player.getName().equals(client.getUsername())) { lobbyView.setPlayerNb(player.getNumber()); } } lobbyView.refreshButtons(allReady, isStarted); } /********************************************************************************************** * [BLOCK] PUBLIC IMPLEMENTED METHODS **********************************************************************************************/ /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.ISweetSnakeIhm#serverReachable() */ @Override public void serverReachable() { if (currentView != connectionView) { switchView(connectionView, true); } } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.ISweetSnakeIhm#serverNotReachable() */ @Override public void serverNotReachable() { if (currentView != unreachableServerView) { switchView(unreachableServerView, true); } } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IClientGui#disconnectedFromServer() */ @Override public void disconnectedFromServer() { if (currentView != connectionView) { switchView(connectionView, true); } } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.ISweetSnakeIhm#successfullyConnected() */ @Override public void connectedToServer() { switchView(sessionsView, true); } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IGui#refreshPlayersList(java.util.List) */ @Override public void refreshPlayersList(final List<PlayerDTO> playersList) { if (currentView == playersView) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { playersView.refreshPlayersList(playersList); refreshUI(); } }); } } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IGui#refreshSessionsList(java.util.List) */ @Override public void refreshSessionsList(final List<GameSessionDTO> sessionsList) { if (currentView == sessionsView) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { sessionsView.refreshSessionsList(sessionsList); refreshUI(); } }); } } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IGui#gameRequested(com.esir.sr.sweetsnake.dto.GameRequestDTO) */ @Override public int requestReceived(final GameRequestDTO request) { return displayCustomQuestion("Someone wants to play with you", request.getRequestingPlayerDto().getName() + " wants to play with you", new String[] { "accept", "deny" }); } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IGui#requestSent(com.esir.sr.sweetsnake.dto.GameRequestDTO) */ @Override public void requestSent(final GameRequestDTO request) { if (currentView != lobbyView) { switchView(lobbyView, true); } } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IGui#requestRefused(com.esir.sr.sweetsnake.dto.GameRequestDTO) */ @Override public void requestDenied(final GameRequestDTO request) { displayToast(request.getRequestedPlayerDto().getName() + " has denied your request"); } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IGuiForClient#sessionJoined(com.esir.sr.sweetsnake.dto.GameSessionDTO, int) */ @Override public void sessionJoined(final GameSessionDTO session, final int playerNb) { if (currentView != lobbyView && currentView != gameView) { switchView(lobbyView, true); } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { lobbyView.setPlayerNb(playerNb); lobbyView.setPlayers(session.getPlayersDto()); lobbyView.refreshPlayers(); lobbyView.refreshButtons(session.allReady(), session.isStarted()); refreshUI(); } }); } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IGuiForClient#sessionStarted(com.esir.sr.sweetsnake.dto.GameSessionDTO, int) */ @Override public void sessionStarted(final GameSessionDTO session, final int playerNb) { gameView.setPlayerNb(playerNb); // TODO move to dto final Map<Integer, String> playersSnakes = new LinkedHashMap<Integer, String>(); for (final PlayerDTO player : session.getPlayersDto()) { playersSnakes.put(player.getNumber(), player.getSnakeId()); } gameView.setPlayersSnakesMap(playersSnakes); gameView.setGameboardDto(session.getGameBoardDto()); switchView(gameView, true); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { gameView.requestFocusInWindow(); } }); } /* * */ @Override // TODO move other parameters to sessiondto public void sessionLeft(final GameSessionDTO session, final PlayerDTO leaver, final boolean stopped, final boolean finished) { if (stopped && !finished) { if (leaver.getName().equals(client.getUsername())) { switchView(sessionsView, true); } else { switchView(lobbyView, false); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { refreshLobbyView(session.getPlayersDto(), session.isStarted()); refreshUI(); } }); } } else if (finished) { switchView(sessionsView, true); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { displayToast(leaver + " has left the game :("); if (currentView == gameView) { gameView.hideScore(leaver.getNumber()); } else if (currentView == lobbyView) { refreshLobbyView(session.getPlayersDto(), session.isStarted()); } refreshUI(); } }); } } // TODO @Override public void sessionFinished(final GameSessionDTO session) { switchView(lobbyView, true); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { lobbyView.setPlayers(session.getPlayersDto()); lobbyView.refreshPlayers(); refreshUI(); } }); } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IGui#refreshGameboard(com.esir.sr.sweetsnake.dto.GameBoardDTO) */ @Override public void refreshGameboard(final GameBoardDTO gameBoard) { if (currentView == gameView) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { gameView.setGameboardDto(gameBoard); gameView.drawGameboard(); refreshUI(); } }); } } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.IGui#refreshScores(java.util.Map) */ @Override public void refreshScores(final List<PlayerDTO> players) { if (currentView == gameView) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { gameView.refreshScores(players); refreshUI(); } }); } } /* * (non-Javadoc) * * @see com.esir.sr.sweetsnake.api.ISweetSnakeIhm#displayErrorMessage(java.lang.String) */ @Override public void displayErrorMessage(final String message) { new Thread(new Runnable() { @Override public void run() { JOptionPane.showMessageDialog(ClientGui.this, message, "Error", JOptionPane.ERROR_MESSAGE); } }).start(); } /********************************************************************************************** * [BLOCK] GETTERS **********************************************************************************************/ /** * * @return */ public Dimension getDimension() { return dimension; } }