/*
* PostLevelUpDialog.java
* Copyright James Dempsey, 2012
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Created on 11/09/2012 7:16:42 AM
*
* $Id$
*/
package pcgen.gui2.dialog;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.SpinnerNumberModel;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.mutable.MutableInt;
import pcgen.facade.core.CharacterFacade;
import pcgen.facade.core.CharacterLevelFacade;
import pcgen.facade.core.CharacterLevelsFacade;
import pcgen.facade.core.CharacterLevelsFacade.CharacterLevelEvent;
import pcgen.facade.core.CharacterLevelsFacade.HitPointListener;
import pcgen.facade.core.ClassFacade;
import pcgen.gui2.tools.Utility;
import pcgen.gui2.util.table.TableCellUtilities.SpinnerEditor;
import pcgen.gui2.util.table.TableCellUtilities.SpinnerRenderer;
import pcgen.system.LanguageBundle;
/**
* The Class {@code PostLevelUpDialog} provides a display of the results
* of levelling up a character.
*
* <br>
*
* @author James Dempsey <jdempsey@users.sourceforge.net>
*/
@SuppressWarnings("serial")
public final class PostLevelUpDialog extends JDialog implements ActionListener
{
private final CharacterLevelsFacade levels;
private final LevelTableModel tableModel;
private final int oldLevel;
private int numLevels;
private PostLevelUpDialog(Frame frame, CharacterFacade character, int oldLevel)
{
super(frame, true);
this.oldLevel = oldLevel;
this.levels = character.getCharacterLevelsFacade();
numLevels = character.getCharacterLevelsFacade().getSize() - oldLevel;
this.tableModel = new LevelTableModel();
initComponents();
pack();
}
/**
* Display the post levelling dialog for a character. This will display a
* list of levels just added along with the hit points and skill points
* gained. The hit points gained may be edited.
*
* @param parent The component we should appear above.
* @param character The character that has been levelled up.
* @param oldLevel The character's level before the level up action.
*/
public static void showPostLevelUpDialog(Component parent,
CharacterFacade character, int oldLevel)
{
int size = character.getCharacterLevelsFacade().getSize();
if (size - oldLevel + 1 < 1)
{
return;
}
Frame frame = JOptionPane.getFrameForComponent(parent);
PostLevelUpDialog dialog =
new PostLevelUpDialog(frame, character, oldLevel);
Utility.setComponentRelativeLocation(frame, dialog);
dialog.setVisible(true);
}
private void initComponents()
{
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
Container pane = getContentPane();
pane.setLayout(new BorderLayout());
JTable table = new JTable(tableModel)
{
@Override
public TableCellEditor getCellEditor(int row, int column)
{
if (column == LevelTableModel.COL_ROLLED_HP && row < numLevels)
{//TODO: the max roll should be calculated in a different manner
String hd = levels.getClassTaken(levels.getElementAt(row+oldLevel)).getHD();
int max = NumberUtils.toInt(hd);
return new SpinnerEditor(new SpinnerNumberModel(1, 1, max, 1));
}
return super.getCellEditor(row, column);
}
@Override
public TableCellRenderer getCellRenderer(int row, int column)
{
if (column == LevelTableModel.COL_ROLLED_HP && row < numLevels)
{
return new SpinnerRenderer();
}
return super.getCellRenderer(row, column);
}
};
table.setCellSelectionEnabled(false);
table.setRowHeight(new JSpinner().getPreferredSize().height);
JTableHeader header = table.getTableHeader();
header.setReorderingAllowed(false);
JScrollPane scrollPane = new JScrollPane(table);
pane.add(scrollPane, BorderLayout.CENTER);
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
JButton button = new JButton(LanguageBundle.getString("in_close")); //$NON-NLS-1$
button.setMnemonic(LanguageBundle.getMnemonic("in_mn_close")); //$NON-NLS-1$
button.setActionCommand("Close"); //$NON-NLS-1$
button.addActionListener(this);
box.add(button);
pane.add(box, BorderLayout.SOUTH);
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosed(WindowEvent e)
{
//Make sure to remove the listeners so that the garbage collector can
//dispose of this dialog and prevent a memory leak
levels.removeHitPointListener(tableModel);
}
});
Utility.installEscapeCloseOperation(this);
}
@Override
public void actionPerformed(ActionEvent e)
{
// Close the dialog
dispose();
}
private class LevelTableModel extends AbstractTableModel implements HitPointListener
{
public static final int COL_LEVEL = 0;
public static final int COL_CLASS = 1;
public static final int COL_GAINED_HP = 2;
public static final int COL_ROLLED_HP = 3;
public static final int COL_SKILL_POINTS = 4;
private final Object[] columns;
private final Object[][] data;
private Map<ClassFacade, MutableInt> classLevelMap;
public LevelTableModel()
{
columns = new Object[]
{
LanguageBundle.getString("in_level"), //$NON-NLS-1$
LanguageBundle.getString("in_classString"), //$NON-NLS-1$
LanguageBundle.getString("in_luGainedHp"), //$NON-NLS-1$
LanguageBundle.getString("in_luRolledHp"), //$NON-NLS-1$
LanguageBundle.getString("in_luSkillPoints") //$NON-NLS-1$
};
data = new Object[numLevels + 1][5];
classLevelMap = new HashMap<>();
int gainedTotal = 0;
int rolledTotal = 0;
int pointTotal = 0;
for (int i = oldLevel; i < numLevels+oldLevel; i++)
{
CharacterLevelFacade level = levels.getElementAt(i);
Object[] dataRow = data[i - oldLevel];
dataRow[COL_LEVEL] = i + 1;
ClassFacade classFacade = levels.getClassTaken(level);
dataRow[COL_CLASS] = classFacade;
if (!classLevelMap.containsKey(classFacade))
{
classLevelMap.put(classFacade, new MutableInt(0));
}
classLevelMap.get(classFacade).increment();
gainedTotal += (Integer) (dataRow[COL_GAINED_HP] = levels.getHPGained(level));
rolledTotal += (Integer) (dataRow[COL_ROLLED_HP] = levels.getHPRolled(level));
pointTotal += (Integer) (dataRow[COL_SKILL_POINTS] = levels.getGainedSkillPoints(level));
}
data[numLevels][COL_LEVEL] = LanguageBundle.getString("in_sumTotal"); //$NON-NLS-1$
StringBuilder builder = new StringBuilder(100);
Iterator<ClassFacade> classes = classLevelMap.keySet().iterator();
while (classes.hasNext())
{
ClassFacade c = classes.next();
builder.append(c.getAbbrev()).append(' ');
builder.append('(').append(classLevelMap.get(c)).append(')');
if (classes.hasNext())
{
builder.append(", "); //$NON-NLS-1$
}
}
data[numLevels][COL_CLASS] = builder;
data[numLevels][COL_GAINED_HP] = gainedTotal;
data[numLevels][COL_ROLLED_HP] = rolledTotal;
data[numLevels][COL_SKILL_POINTS] = pointTotal;
levels.addHitPointListener(this);
}
@Override
public int getRowCount()
{
return numLevels+1;
}
@Override
public int getColumnCount()
{
return columns.length;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
switch (columnIndex)
{
case COL_ROLLED_HP:
return rowIndex < numLevels;
default:
return false;
}
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
switch (columnIndex)
{
case COL_GAINED_HP:
case COL_ROLLED_HP:
case COL_SKILL_POINTS:
return Integer.class;
default:
return Object.class;
}
}
@Override
public String getColumnName(int column)
{
return columns[column].toString();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
return data[rowIndex][columnIndex];
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
CharacterLevelFacade level = levels.getElementAt(rowIndex+oldLevel);
levels.setHPRolled(level, (Integer) aValue);
}
@Override
public void hitPointsChanged(CharacterLevelEvent e)
{
int gainedTotal = 0;
int rolledTotal = 0;
for (int i = oldLevel; i < numLevels+oldLevel; i++)
{
CharacterLevelFacade level = levels.getElementAt(i);
Object[] dataRow = data[i - oldLevel];
gainedTotal += (Integer) (dataRow[COL_GAINED_HP] = levels.getHPGained(level));
rolledTotal += (Integer) (dataRow[COL_ROLLED_HP] = levels.getHPRolled(level));
}
data[numLevels][COL_GAINED_HP] = gainedTotal;
data[numLevels][COL_ROLLED_HP] = rolledTotal;
fireTableRowsUpdated(0, data.length);
}
}
}