package games.strategy.triplea.ui;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.RelationshipType;
import games.strategy.engine.data.RelationshipTypeList;
import games.strategy.sound.ClipPlayer;
import games.strategy.sound.SoundPath;
import games.strategy.triplea.attachments.PoliticalActionAttachment;
import games.strategy.triplea.delegate.remote.IPoliticsDelegate;
import games.strategy.triplea.util.PlayerOrderComparator;
import games.strategy.ui.SwingAction;
/**
* This panel is fired by ActionButtons and controls the selection of a valid
* political action to attempt.
*/
public class PoliticsPanel extends ActionPanel {
private static final long serialVersionUID = -4661479948450261578L;
private final JLabel m_actionLabel = new JLabel();
private JButton m_selectPoliticalActionButton = null;
private JButton m_doneButton = null;
private PoliticalActionAttachment m_choice = null;
private TripleAFrame m_parent = null;
private boolean m_firstRun = true;
protected List<PoliticalActionAttachment> m_validPoliticalActions = null;
public PoliticsPanel(final GameData data, final MapPanel map, final TripleAFrame parent) {
super(data, map);
m_parent = parent;
}
@Override
public String toString() {
return "Politics Panel";
}
@Override
public void display(final PlayerID id) {
super.display(id);
m_choice = null;
SwingUtilities.invokeLater(() -> {
removeAll();
m_actionLabel.setText(id.getName() + " Politics");
add(m_actionLabel);
m_selectPoliticalActionButton = new JButton(SelectPoliticalActionAction);
m_selectPoliticalActionButton.setEnabled(false);
add(m_selectPoliticalActionButton);
m_doneButton = new JButton(DontBotherAction);
m_doneButton.setEnabled(false);
SwingUtilities.invokeLater(() -> m_doneButton.requestFocusInWindow());
add(m_doneButton);
});
}
/**
* waits till someone calls release() and then returns the political action
* chosen.
*
* @return the choice of political action
*/
public PoliticalActionAttachment waitForPoliticalAction(final boolean firstRun,
final IPoliticsDelegate iPoliticsDelegate) {
m_firstRun = firstRun;
// Never use a delegate or bridge from a UI. Multiplayer games will not work.
m_validPoliticalActions = new ArrayList<>(iPoliticsDelegate.getValidActions());
Collections.sort(m_validPoliticalActions, new PoliticalActionComparator(getCurrentPlayer(), getData()));
if (m_firstRun && m_validPoliticalActions.isEmpty()) {
// No Valid political actions, do nothing
return null;
} else {
if (m_firstRun) {
ClipPlayer.play(SoundPath.CLIP_PHASE_POLITICS, getCurrentPlayer());
}
SwingUtilities.invokeLater(() -> {
m_selectPoliticalActionButton.setEnabled(true);
m_doneButton.setEnabled(true);
// press the politics button for us.
SelectPoliticalActionAction.actionPerformed(null);
});
}
waitForRelease();
return m_choice;
}
/**
* Fires up a JDialog showing the political landscape and valid actions,
* choosing an action will release this model and trigger waitForRelease().
*/
private final Action SelectPoliticalActionAction = SwingAction.of("Do Politics...", e -> {
final Dimension screenResolution = Toolkit.getDefaultToolkit().getScreenSize();
final int availHeight = screenResolution.height - 96;
final int availWidth = screenResolution.width - 30;
final int availHeightOverview = (int) ((float) availHeight * 2 / 3);
final int availHeightChoice = (int) ((float) availHeight / 3);
final JDialog politicalChoiceDialog = new JDialog(m_parent, "Political Actions", true);
final Insets insets = new Insets(1, 1, 1, 1);
final JPanel politicalChoicePanel = new JPanel();
politicalChoicePanel.setLayout(new GridBagLayout());
final PoliticalStateOverview overview = new PoliticalStateOverview(getData(), getMap().getUIContext(), false);
final JScrollPane overviewScroll = new JScrollPane(overview);
overviewScroll.setBorder(BorderFactory.createEmptyBorder());
// add 26 to height when the actions are empty, because for some stupid reason java calculates the pack size wrong
// (again)...
// add 20 to either when the opposite needs scroll bars, because that is how big scroll bars are..
overviewScroll.setPreferredSize(new Dimension(
(overviewScroll.getPreferredSize().width > availWidth ? availWidth
: (overviewScroll.getPreferredSize().width
+ (overviewScroll.getPreferredSize().height > availHeightOverview ? 20 : 0))),
(overviewScroll.getPreferredSize().height > availHeightOverview ? availHeightOverview
: (overviewScroll.getPreferredSize().height + (m_validPoliticalActions.isEmpty() ? 26 : 0)
+ (overviewScroll.getPreferredSize().width > availWidth ? 20 : 0)))));
final JScrollPane choiceScroll = new JScrollPane(PoliticalActionButtonPanel(politicalChoiceDialog));
choiceScroll.setBorder(BorderFactory.createEmptyBorder());
choiceScroll.setPreferredSize(new Dimension(
(choiceScroll.getPreferredSize().width > availWidth ? availWidth
: (choiceScroll.getPreferredSize().width
+ (choiceScroll.getPreferredSize().height > availHeightChoice ? 20 : 0))),
(choiceScroll.getPreferredSize().height > availHeightChoice ? availHeightChoice
: (choiceScroll.getPreferredSize().height)
+ (choiceScroll.getPreferredSize().width > availWidth ? 20 : 0))));
final JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, overviewScroll, choiceScroll);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerSize(8);
politicalChoicePanel.add(splitPane, new GridBagConstraints(0, 0, 1, 1, 100.0, 100.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, insets, 0, 0));
final JButton noActionButton =
new JButton(SwingAction.of("No Actions", event -> politicalChoiceDialog.setVisible(false)));
SwingUtilities.invokeLater(() -> noActionButton.requestFocusInWindow());
politicalChoicePanel.add(noActionButton, new GridBagConstraints(0, 1, 20, 1, 1.0, 1.0, GridBagConstraints.EAST,
GridBagConstraints.NONE, insets, 0, 0));
politicalChoiceDialog.add(politicalChoicePanel);
politicalChoiceDialog.pack();
politicalChoiceDialog.setLocationRelativeTo(m_parent);
politicalChoiceDialog.setVisible(true);
politicalChoiceDialog.dispose();
});
private JPanel PoliticalActionButtonPanel(final JDialog parent) {
final JPanel politicalActionButtonPanel = new JPanel();
politicalActionButtonPanel.setLayout(new GridBagLayout());
int row = 0;
final Insets insets = new Insets(1, 1, 1, 1);
for (final PoliticalActionAttachment paa : m_validPoliticalActions) {
politicalActionButtonPanel.add(getOtherPlayerFlags(paa), new GridBagConstraints(0, row, 1, 1, 1.0, 1.0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, insets, 0, 0));
final JButton button = new JButton(getActionButtonText(paa));
button.addActionListener(ae -> {
m_selectPoliticalActionButton.setEnabled(false);
m_doneButton.setEnabled(false);
m_validPoliticalActions = null;
m_choice = paa;
parent.setVisible(false);
release();
});
politicalActionButtonPanel.add(button, new GridBagConstraints(1, row, 1, 1, 1.0, 1.0, GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL, insets, 0, 0));
politicalActionButtonPanel.add(getActionDescriptionLabel(paa), new GridBagConstraints(2, row, 1, 1, 5.0, 1.0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, insets, 0, 0));
row++;
}
return politicalActionButtonPanel;
}
/**
* This will stop the politicsPhase.
*/
private final Action DontBotherAction = SwingAction.of("Done", e -> {
if (!m_firstRun || youSureDoNothing()) {
m_choice = null;
release();
}
});
private boolean youSureDoNothing() {
final int rVal = JOptionPane.showConfirmDialog(JOptionPane.getFrameForComponent(PoliticsPanel.this),
"Are you sure you dont want to do anything?", "End Politics", JOptionPane.YES_NO_OPTION);
return rVal == JOptionPane.YES_OPTION;
}
/**
* Convenient method to get a JCompenent showing the flags involved in this
* action.
*
* @param paa
* the political action attachment to get the "otherflags" for
* @return a JComponent with the flags involved.
*/
private JPanel getOtherPlayerFlags(final PoliticalActionAttachment paa) {
final JPanel panel = new JPanel();
for (final PlayerID p : paa.getOtherPlayers()) {
panel.add(new JLabel(new ImageIcon(this.getMap().getUIContext().getFlagImageFactory().getFlag(p))));
}
return panel;
}
private static String getActionButtonText(final PoliticalActionAttachment paa) {
final String costString = paa.getCostPU() == 0 ? "" : "[" + paa.getCostPU() + " PU] ";
return costString + PoliticsText.getInstance().getButtonText(paa.getText());
}
private static JLabel getActionDescriptionLabel(final PoliticalActionAttachment paa) {
final String chanceString = paa.getChanceToHit() >= paa.getChanceDiceSides() ? ""
: "[" + paa.getChanceToHit() + "/" + paa.getChanceDiceSides() + "] ";
return new JLabel(chanceString + PoliticsText.getInstance().getDescription(paa.getText()));
}
}
class PoliticalActionComparator implements Comparator<PoliticalActionAttachment> {
private final GameData m_data;
private final PlayerID m_player;
public PoliticalActionComparator(final PlayerID currentPlayer, final GameData data) {
m_data = data;
m_player = currentPlayer;
}
@Override
public int compare(final PoliticalActionAttachment paa1, final PoliticalActionAttachment paa2) {
if (paa1.equals(paa2)) {
return 0;
}
final String[] paa1RelationChange = paa1.getRelationshipChange().iterator().next().split(":");
final String[] paa2RelationChange = paa2.getRelationshipChange().iterator().next().split(":");
final RelationshipTypeList relationshipTypeList;
m_data.acquireReadLock();
try {
relationshipTypeList = m_data.getRelationshipTypeList();
} finally {
m_data.releaseReadLock();
}
final RelationshipType paa1NewType = relationshipTypeList.getRelationshipType(paa1RelationChange[2]);
final RelationshipType paa2NewType = relationshipTypeList.getRelationshipType(paa2RelationChange[2]);
// sort by player
final PlayerID paa1p1 = m_data.getPlayerList().getPlayerID(paa1RelationChange[0]);
final PlayerID paa1p2 = m_data.getPlayerList().getPlayerID(paa1RelationChange[1]);
final PlayerID paa2p1 = m_data.getPlayerList().getPlayerID(paa2RelationChange[0]);
final PlayerID paa2p2 = m_data.getPlayerList().getPlayerID(paa2RelationChange[1]);
final PlayerID paa1OtherPlayer = (m_player.equals(paa1p1) ? paa1p2 : paa1p1);
final PlayerID paa2OtherPlayer = (m_player.equals(paa2p1) ? paa2p2 : paa2p1);
if (!paa1OtherPlayer.equals(paa2OtherPlayer)) {
final int order = new PlayerOrderComparator(m_data).compare(paa1OtherPlayer, paa2OtherPlayer);
if (order != 0) {
return order;
}
}
// sort by achetype
if (!paa1NewType.equals(paa2NewType)) {
if (paa1NewType.getRelationshipTypeAttachment().isWar() && !paa2NewType.getRelationshipTypeAttachment().isWar()) {
return -1;
}
if (!paa1NewType.getRelationshipTypeAttachment().isWar() && paa2NewType.getRelationshipTypeAttachment().isWar()) {
return 1;
}
if (paa1NewType.getRelationshipTypeAttachment().isNeutral()
&& paa2NewType.getRelationshipTypeAttachment().isAllied()) {
return -1;
}
if (paa1NewType.getRelationshipTypeAttachment().isAllied()
&& paa2NewType.getRelationshipTypeAttachment().isNeutral()) {
return 1;
}
}
// sort by name of new relationship type
if (!paa1NewType.getName().equals(paa2NewType.getName())) {
return paa1NewType.getName().compareTo(paa2NewType.getName());
}
return 0;
}
}