package com.kartoflane.superluminal2.ui;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import com.kartoflane.superluminal2.Superluminal;
import com.kartoflane.superluminal2.components.Hotkey;
import com.kartoflane.superluminal2.components.enums.Hotkeys;
import com.kartoflane.superluminal2.components.enums.WeaponStats;
import com.kartoflane.superluminal2.components.enums.WeaponTypes;
import com.kartoflane.superluminal2.components.interfaces.Predicate;
import com.kartoflane.superluminal2.core.Database;
import com.kartoflane.superluminal2.core.Manager;
import com.kartoflane.superluminal2.ftl.AnimationObject;
import com.kartoflane.superluminal2.ftl.WeaponList;
import com.kartoflane.superluminal2.ftl.WeaponObject;
import com.kartoflane.superluminal2.mvc.views.Preview;
import com.kartoflane.superluminal2.utils.UIUtils;
public class WeaponSelectionDialog {
private static WeaponSelectionDialog instance = null;
private static final int defaultBlueTabWidth = 200;
private static final int defaultNameTabWidth = 150;
private static final int minTreeWidth = defaultBlueTabWidth + defaultNameTabWidth + 5;
private static final int defaultDataWidth = 250;
private static final Predicate<WeaponObject> defaultFilter = new Predicate<WeaponObject>() {
public boolean accept(WeaponObject object) {
return true;
}
};
private static WeaponObject selection = Database.DEFAULT_WEAPON_OBJ;
private static WeaponList selectionList = Database.DEFAULT_WEAPON_LIST;
private WeaponObject result = null;
private WeaponList resultList = null;
private int response = SWT.NO;
private boolean listMode = false;
private boolean sortByBlueprint = true;
private HashMap<WeaponTypes, TreeItem> treeItemMap = null;
private Preview preview = null;
private Predicate<WeaponObject> filter = defaultFilter;
private Shell shell = null;
private Text txtDesc;
private StatTable statTable;
private Button btnCancel;
private Button btnConfirm;
private Tree tree;
private Canvas canvas;
private TreeColumn trclmnBlueprint;
private TreeColumn trclmnName;
private Button btnSearch;
public WeaponSelectionDialog(Shell parent) {
if (instance != null)
throw new IllegalStateException("Previous instance has not been disposed!");
instance = this;
treeItemMap = new HashMap<WeaponTypes, TreeItem>();
shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.APPLICATION_MODAL);
shell.setText(Superluminal.APP_NAME + " - Weapon Selection");
shell.setLayout(new GridLayout(1, false));
SashForm sashForm = new SashForm(shell, SWT.SMOOTH);
sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
tree = new Tree(sashForm, SWT.BORDER | SWT.FULL_SELECTION);
tree.setHeaderVisible(true);
// remove the horizontal bar so that it doesn't flicker when the tree is resized
tree.getHorizontalBar().dispose();
trclmnBlueprint = new TreeColumn(tree, SWT.LEFT);
trclmnBlueprint.setWidth(defaultBlueTabWidth);
trclmnBlueprint.setText("Blueprint");
trclmnName = new TreeColumn(tree, SWT.RIGHT);
trclmnName.setWidth(defaultNameTabWidth);
trclmnName.setText("Name");
ScrolledComposite scrolledComposite = new ScrolledComposite(sashForm, SWT.BORDER | SWT.V_SCROLL);
scrolledComposite.setExpandHorizontal(true);
scrolledComposite.setExpandVertical(true);
Composite compData = new Composite(scrolledComposite, SWT.NONE);
GridLayout gl_compData = new GridLayout(1, false);
gl_compData.marginHeight = 0;
gl_compData.marginWidth = 0;
compData.setLayout(gl_compData);
canvas = new Canvas(compData, SWT.DOUBLE_BUFFERED);
GridData gd_canvas = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1);
gd_canvas.heightHint = 100;
canvas.setLayoutData(gd_canvas);
preview = new Preview();
RGB rgb = canvas.getBackground().getRGB();
preview.setBackgroundColor((int) (0.9 * rgb.red), (int) (0.9 * rgb.green), (int) (0.9 * rgb.blue));
preview.setDrawBackground(true);
canvas.addPaintListener(preview);
Label lblDescription = new Label(compData, SWT.NONE);
lblDescription.setText("Description:");
txtDesc = new Text(compData, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI);
GridData gd_txtDesc = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1);
gd_txtDesc.heightHint = 60;
txtDesc.setLayoutData(gd_txtDesc);
scrolledComposite.setContent(compData);
scrolledComposite.setMinSize(compData.computeSize(SWT.DEFAULT, SWT.DEFAULT));
sashForm.setWeights(new int[] { minTreeWidth, defaultDataWidth });
statTable = new StatTable(compData);
statTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
Composite compButtons = new Composite(shell, SWT.NONE);
GridLayout gl_compButtons = new GridLayout(3, false);
gl_compButtons.marginWidth = 0;
gl_compButtons.marginHeight = 0;
compButtons.setLayout(gl_compButtons);
compButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
btnSearch = new Button(compButtons, SWT.NONE);
GridData gd_btnSearch = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gd_btnSearch.widthHint = 80;
btnSearch.setLayoutData(gd_btnSearch);
btnSearch.setText("Search");
btnConfirm = new Button(compButtons, SWT.NONE);
GridData gd_btnConfirm = new GridData(SWT.RIGHT, SWT.CENTER, true, false, 1, 1);
gd_btnConfirm.widthHint = 80;
btnConfirm.setLayoutData(gd_btnConfirm);
btnConfirm.setText("Confirm");
btnCancel = new Button(compButtons, SWT.NONE);
GridData gd_btnCancel = new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1);
gd_btnCancel.widthHint = 80;
btnCancel.setLayoutData(gd_btnCancel);
btnCancel.setText("Cancel");
tree.addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(MouseEvent e) {
if (e.button == 1 && tree.getSelectionCount() != 0) {
TreeItem selectedItem = tree.getSelection()[0];
if (selectedItem.getItemCount() == 0 && btnConfirm.isEnabled())
btnConfirm.notifyListeners(SWT.Selection, null);
else if (selectedItem.getBounds().contains(e.x, e.y))
selectedItem.setExpanded(!selectedItem.getExpanded());
}
}
@Override
public void mouseDown(MouseEvent e) {
}
@Override
public void mouseUp(MouseEvent e) {
}
});
tree.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (tree.getSelectionCount() != 0) {
TreeItem selectedItem = tree.getSelection()[0];
Object o = selectedItem.getData();
if (o instanceof WeaponList) {
selectionList = (WeaponList) o;
resultList = selectionList;
result = null;
btnConfirm.setEnabled(listMode && resultList != null);
} else if (o instanceof WeaponObject) {
resultList = null;
selection = (WeaponObject) o;
result = selection;
btnConfirm.setEnabled(!listMode && result != null);
} else {
resultList = null;
result = null;
btnConfirm.setEnabled(false);
}
} else {
resultList = null;
result = null;
btnConfirm.setEnabled(false);
}
updateData();
}
});
trclmnBlueprint.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Sort by blueprint name
if (!listMode && !sortByBlueprint) {
sortByBlueprint = true;
updateTree();
}
}
});
trclmnName.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Sort by title
if (!listMode && sortByBlueprint) {
sortByBlueprint = false;
updateTree();
}
}
});
btnSearch.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
WeaponSearchDialog wsDialog = new WeaponSearchDialog(shell);
Predicate<WeaponObject> result = wsDialog.open();
if (result == AbstractSearchDialog.RESULT_DEFAULT) {
filter = defaultFilter;
} else if (result == AbstractSearchDialog.RESULT_UNCHANGED) {
// Do nothing
} else {
filter = result;
}
updateTree();
tree.notifyListeners(SWT.Selection, null);
}
});
btnCancel.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
response = SWT.NO;
dispose();
}
});
btnConfirm.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
response = SWT.YES;
dispose();
}
});
shell.addListener(SWT.Close, new Listener() {
@Override
public void handleEvent(Event e) {
btnCancel.notifyListeners(SWT.Selection, null);
e.doit = false;
}
});
shell.addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_RETURN && tree.getSelectionCount() != 0) {
TreeItem selectedItem = tree.getSelection()[0];
if (selectedItem.getItemCount() == 0 && btnConfirm.isEnabled())
btnConfirm.notifyListeners(SWT.Selection, null);
else
selectedItem.setExpanded(!selectedItem.getExpanded());
}
}
});
canvas.addControlListener(new ControlListener() {
@Override
public void controlMoved(ControlEvent e) {
}
@Override
public void controlResized(ControlEvent e) {
updatePreview();
canvas.redraw();
}
});
ControlAdapter resizer = new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
final int BORDER_OFFSET = tree.getBorderWidth();
if (trclmnBlueprint.getWidth() > tree.getClientArea().width - BORDER_OFFSET)
trclmnBlueprint.setWidth(tree.getClientArea().width - BORDER_OFFSET);
trclmnName.setWidth(tree.getClientArea().width - trclmnBlueprint.getWidth() - BORDER_OFFSET);
}
};
tree.addControlListener(resizer);
trclmnBlueprint.addControlListener(resizer);
shell.setMinimumSize(minTreeWidth + defaultDataWidth, 300);
shell.pack();
shell.setSize(600, 400);
Point size = shell.getSize();
shell.setSize(size.x + 5, size.y);
Point parSize = parent.getSize();
Point parLoc = parent.getLocation();
shell.setLocation(parLoc.x + parSize.x / 3 - size.x / 2, parLoc.y + parSize.y / 3 - size.y / 2);
// Register hotkeys
Hotkey h = new Hotkey(Manager.getHotkey(Hotkeys.SEARCH));
h.addNotifyAction(btnSearch, true);
Manager.hookHotkey(shell, h);
}
private void open() {
response = SWT.NO;
if (listMode)
updateTreeList();
else
updateTree();
shell.open();
updateData();
Display display = UIUtils.getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
public WeaponList open(WeaponList current) {
listMode = true;
resultList = current;
result = null;
if (current == null || current == Database.DEFAULT_WEAPON_LIST) {
resultList = selectionList;
} else {
selectionList = resultList;
}
btnSearch.setEnabled(false);
open();
if (response == SWT.YES) {
return resultList;
} else {
return null;
}
}
public WeaponObject open(WeaponObject current) {
listMode = false;
resultList = null;
result = current;
if (current == null || current == Database.DEFAULT_WEAPON_OBJ) {
result = selection;
} else {
selection = result;
}
btnSearch.setEnabled(true);
open();
if (response == SWT.YES) {
return result;
} else {
return null;
}
}
private void updateTree() {
for (TreeItem trtm : tree.getItems())
trtm.dispose();
TreeItem trtm = new TreeItem(tree, SWT.NONE);
trtm.setText("No Weapon");
trtm.setData(Database.DEFAULT_WEAPON_OBJ);
trtm = new TreeItem(tree, SWT.NONE);
trtm.setText("Beams");
treeItemMap.put(WeaponTypes.BEAM, trtm);
trtm = new TreeItem(tree, SWT.NONE);
trtm.setText("Bombs");
treeItemMap.put(WeaponTypes.BOMB, trtm);
trtm = new TreeItem(tree, SWT.NONE);
trtm.setText("Burst");
treeItemMap.put(WeaponTypes.BURST, trtm);
trtm = new TreeItem(tree, SWT.NONE);
trtm.setText("Lasers / Ions");
treeItemMap.put(WeaponTypes.LASER, trtm);
trtm = new TreeItem(tree, SWT.NONE);
trtm.setText("Missiles");
treeItemMap.put(WeaponTypes.MISSILES, trtm);
TreeItem selection = null;
for (WeaponTypes type : WeaponTypes.values()) {
TreeItem typeItem = treeItemMap.get(type);
WeaponIterator it = new WeaponIterator(Database.getInstance().getWeaponsByType(type), sortByBlueprint);
for (it.first(); it.hasNext(); it.next()) {
WeaponObject weapon = it.current();
trtm = new TreeItem(typeItem, SWT.NONE);
trtm.setText(0, weapon.getBlueprintName());
trtm.setText(1, weapon.getTitle());
trtm.setData(weapon);
if (result == weapon)
selection = trtm;
}
if (typeItem.getItemCount() == 0)
typeItem.dispose();
}
tree.layout();
if (selection != null) {
treeItemMap.get(result.getType()).setExpanded(true);
tree.select(selection);
tree.setTopItem(selection);
} else {
tree.select(tree.getItem(0));
}
}
private void updateTreeList() {
for (TreeItem trtm : tree.getItems())
trtm.dispose();
TreeItem trtm = new TreeItem(tree, SWT.NONE);
trtm.setText("No Weapon List");
trtm.setData(Database.DEFAULT_WEAPON_LIST);
TreeItem selection = null;
for (WeaponList list : Database.getInstance().getWeaponLists()) {
trtm = new TreeItem(tree, SWT.NONE);
trtm.setText(list.getBlueprintName());
trtm.setData(list);
if (resultList == list)
selection = trtm;
for (WeaponObject weapon : list) {
TreeItem weaponItem = new TreeItem(trtm, SWT.NONE);
weaponItem.setText(0, weapon.getBlueprintName());
weaponItem.setText(1, weapon.getTitle());
weaponItem.setData(weapon);
}
}
tree.layout();
if (selection != null) {
tree.select(selection);
tree.setTopItem(selection);
} else {
tree.select(tree.getItem(0));
}
}
private void updateData() {
String path = null;
if (result != null) {
AnimationObject anim = result.getAnimation();
path = anim.getSheetPath();
Point frameSize = anim.getFrameSize();
preview.setSourceSize(frameSize.x, frameSize.y);
preview.setOverrideSourceSize(true);
preview.setImage(path);
preview.setSize(frameSize.x, frameSize.y);
statTable.setVisible(false);
statTable.clear();
for (WeaponStats stat : WeaponStats.values()) {
if (!stat.doesApply(result.getType()))
continue;
float value = result.getStat(stat);
statTable.addEntry(stat.toString(), stat.formatValue(value));
}
statTable.setVisible(true);
statTable.updateColumnWidth();
txtDesc.setText(result.getDescription());
} else {
preview.setImage(null);
statTable.clear();
txtDesc.setText("");
}
preview.setDrawBackground(path != null);
preview.setLocation(canvas.getSize().x / 2, canvas.getSize().y / 2);
canvas.redraw();
}
private void updatePreview() {
Point cSize = canvas.getSize();
preview.setLocation(cSize.x / 2, cSize.y / 2);
}
public static WeaponSelectionDialog getInstance() {
return instance;
}
public void dispose() {
Manager.unhookHotkeys(shell);
shell.dispose();
instance = null;
}
public boolean isActive() {
return !shell.isDisposed() && shell.isVisible();
}
private class WeaponIterator implements Iterator<WeaponObject> {
private final ArrayList<WeaponObject> list;
private final WeaponComparator comparator;
private WeaponObject current = null;
public WeaponIterator(ArrayList<WeaponObject> list, boolean byBlueprint) {
comparator = new WeaponComparator(byBlueprint);
if (filter == defaultFilter) {
this.list = list;
} else {
this.list = new ArrayList<WeaponObject>();
for (WeaponObject w : list) {
if (filter.accept(w))
this.list.add(w);
}
}
}
private WeaponObject getSmallestElement() {
WeaponObject result = null;
for (WeaponObject weapon : list) {
if (result == null || comparator.compare(weapon, result) < 0)
result = weapon;
}
return result;
}
public void first() {
current = getSmallestElement();
}
public WeaponObject current() {
return current;
}
@Override
public boolean hasNext() {
return !list.isEmpty();
}
@Override
public WeaponObject next() {
remove();
current = getSmallestElement();
return current;
}
@Override
public void remove() {
list.remove(current);
}
}
private class WeaponComparator implements Comparator<WeaponObject> {
private final boolean byBlueprint;
public WeaponComparator(boolean byBlueprint) {
this.byBlueprint = byBlueprint;
}
@Override
public int compare(WeaponObject o1, WeaponObject o2) {
if (byBlueprint) {
// Just compare the two blueprints together for alphanumerical ordering
return o1.getBlueprintName().compareTo(o2.getBlueprintName());
} else {
int result = o1.getTitle().compareTo(o2.getTitle());
if (result == 0) // If titles are the same, fall back to sorting by blueprint
result = o1.getBlueprintName().compareTo(o2.getBlueprintName());
return result;
}
}
}
}