package logbook.gui;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
import logbook.config.AppConfig;
import logbook.data.Data;
import logbook.data.DataType;
import logbook.data.EventListener;
import logbook.data.context.GlobalContext;
import logbook.dto.ShipDto;
import logbook.internal.EvaluateExp;
import logbook.internal.ExpTable;
import logbook.internal.SeaExp;
import logbook.util.CalcExpUtils;
import logbook.util.SwtUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
/**
* 経験値計算機
*
*/
public final class CalcExpDialog extends WindowBase {
private final List<ShipDto> shiplist = new ArrayList<ShipDto>();
private final Shell parent;
private Shell shell;
private Combo shipcombo;
private Spinner beforelv;
private Text beforexp;
private Spinner afterlv;
private Text afterexp;
private Combo seacombo;
private Combo evalcombo;
private Button flagbtn;
private Button mvpbtn;
private Text getexp;
private Text needexp;
private Text battlecount;
/**
* Create the dialog.
* @param parent
*/
public CalcExpDialog(Shell parent, MenuItem menuItem) {
super(menuItem);
this.parent = parent;
}
/**
* 「新しいウィンドウを開く」用
*/
public CalcExpDialog() {
super();
// アプリ上での親はないが、メインウィンドウの子ウィンドウとして作成する
this.parent = ApplicationMain.main.getShell();
}
/**
* Open the dialog.
*/
@Override
public void open() {
// 初期化済みの場合
if (this.isWindowInitialized()) {
this.setShipComboData();
this.preset();
this.calc();
this.setVisible(true);
return;
}
this.createContents();
this.registerEvents();
this.setWindowInitialized(true);
this.setVisible(true);
}
@Override
protected boolean moveWithDrag() {
return true;
}
/**
* Create contents of the dialog.
*/
private void createContents() {
// シェルを作成
super.createContents(this.parent, SWT.CLOSE | SWT.TITLE | SWT.RESIZE, false);
this.shell = this.getShell();
this.shell.setText("経験値計算機");
this.shell.setLayout(new GridLayout(1, false));
Composite select = new Composite(this.shell, SWT.NONE);
select.setLayout(new RowLayout());
select.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
this.shipcombo = new Combo(select, SWT.READ_ONLY);
Button secretary = new Button(select, SWT.NONE);
secretary.setText("秘書艦");
Composite plan = new Composite(this.shell, SWT.NONE);
plan.setLayout(new GridLayout(5, false));
plan.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Label label1 = new Label(plan, SWT.NONE);
label1.setText("今のレベル");
this.beforelv = new Spinner(plan, SWT.BORDER);
GridData gdBeforelv = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gdBeforelv.widthHint = SwtUtils.DPIAwareWidth(45);
this.beforelv.setLayoutData(gdBeforelv);
this.beforelv.setMaximum(ExpTable.MAX_LEVEL);
this.beforelv.setMinimum(1);
Label label2 = new Label(plan, SWT.NONE);
label2.setText("Lv");
this.beforexp = new Text(plan, SWT.BORDER | SWT.READ_ONLY);
GridData gdBeforexp = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gdBeforexp.widthHint = SwtUtils.DPIAwareWidth(60);
this.beforexp.setLayoutData(gdBeforexp);
this.beforexp.setText("0");
Label label3 = new Label(plan, SWT.NONE);
label3.setText("Exp");
Label label4 = new Label(plan, SWT.NONE);
label4.setText("目標レベル");
this.afterlv = new Spinner(plan, SWT.BORDER);
GridData gdAfterlv = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gdAfterlv.widthHint = SwtUtils.DPIAwareWidth(45);
this.afterlv.setLayoutData(gdAfterlv);
this.afterlv.setMaximum(ExpTable.MAX_LEVEL);
this.afterlv.setMinimum(1);
Label label5 = new Label(plan, SWT.NONE);
label5.setText("Lv");
this.afterexp = new Text(plan, SWT.BORDER | SWT.READ_ONLY);
GridData gdAfterexp = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gdAfterexp.widthHint = SwtUtils.DPIAwareWidth(60);
this.afterexp.setLayoutData(gdAfterexp);
this.afterexp.setText("0");
Label label6 = new Label(plan, SWT.NONE);
label6.setText("Exp");
Composite plan2 = new Composite(this.shell, SWT.NONE);
plan2.setLayout(new RowLayout());
plan2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Label label7 = new Label(plan2, SWT.NONE);
label7.setText("海域");
this.seacombo = new Combo(plan2, SWT.READ_ONLY);
for (Entry<String, Integer> entry : SeaExp.get().entrySet()) {
this.seacombo.add(entry.getKey());
}
Label label8 = new Label(plan2, SWT.NONE);
label8.setText("評価");
this.evalcombo = new Combo(plan2, SWT.READ_ONLY);
for (Entry<String, Double> entry : EvaluateExp.get().entrySet()) {
this.evalcombo.add(entry.getKey());
}
Composite plan3 = new Composite(this.shell, SWT.NONE);
plan3.setLayout(new RowLayout());
plan3.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
this.flagbtn = new Button(plan3, SWT.CHECK);
this.flagbtn.setText("旗艦");
this.mvpbtn = new Button(plan3, SWT.CHECK);
this.mvpbtn.setText("MVP");
Composite plan4 = new Composite(this.shell, SWT.NONE);
plan4.setLayout(new FillLayout());
plan4.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
new Label(plan4, SWT.SEPARATOR | SWT.HORIZONTAL);
Composite result = new Composite(this.shell, SWT.NONE);
result.setLayout(new GridLayout(4, false));
result.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Label label10 = new Label(result, SWT.NONE);
label10.setText("1回あたり");
this.getexp = new Text(result, SWT.BORDER | SWT.READ_ONLY);
GridData gdGetexp = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gdGetexp.widthHint = SwtUtils.DPIAwareWidth(55);
this.getexp.setLayoutData(gdGetexp);
new Label(result, SWT.NONE);
new Label(result, SWT.NONE);
Label label11 = new Label(result, SWT.NONE);
label11.setText("必要経験値");
this.needexp = new Text(result, SWT.BORDER | SWT.READ_ONLY);
GridData gdNeedexp = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gdNeedexp.widthHint = SwtUtils.DPIAwareWidth(55);
this.needexp.setLayoutData(gdNeedexp);
Label label12 = new Label(result, SWT.NONE);
label12.setText("戦闘回数");
this.battlecount = new Text(result, SWT.BORDER | SWT.READ_ONLY);
GridData gdBattlecount = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gdBattlecount.widthHint = SwtUtils.DPIAwareWidth(55);
this.battlecount.setLayoutData(gdBattlecount);
// 海域のインデックス値を復元
for (int i = 0; i < this.seacombo.getItemCount(); i++) {
if (this.seacombo.getItem(i).equals(AppConfig.get().getDefaultSea())) {
this.seacombo.select(i);
}
}
// 評価のインデックス値を復元
for (int i = 0; i < this.evalcombo.getItemCount(); i++) {
if (this.evalcombo.getItem(i).equals(AppConfig.get().getDefaultEvaluate())) {
this.evalcombo.select(i);
}
}
this.shipcombo.addSelectionListener(new PresetListener());
secretary.addSelectionListener(new SecretaryListener());
SelectionListener beforeLvListener = new BeforeLvListener(this.beforexp, this.beforelv);
this.beforelv.addSelectionListener(beforeLvListener);
this.beforelv.addMouseWheelListener(new WheelListener(this.beforelv, beforeLvListener));
SelectionListener afterLvListener = new BeforeLvListener(this.afterexp, this.afterlv);
this.afterlv.addSelectionListener(afterLvListener);
this.afterlv.addMouseWheelListener(new WheelListener(this.afterlv, afterLvListener));
this.seacombo.addSelectionListener(new UpdateListener());
this.evalcombo.addSelectionListener(new UpdateListener());
this.flagbtn.addSelectionListener(new UpdateListener());
this.mvpbtn.addSelectionListener(new UpdateListener());
final EventListener updateListener = new EventListener() {
@Override
public void update(DataType type, Data data) {
switch (type) {
case PORT:
case SHIP2:
case SHIP3:
CalcExpDialog.this.reload();
break;
default:
break;
}
}
};
GlobalContext.addEventListener(updateListener);
this.getShell().addListener(SWT.Dispose, new Listener() {
@Override
public void handleEvent(Event event) {
GlobalContext.removeEventListener(updateListener);
}
});
// 選択する項目はドラックで移動できないようにする
for (Control c : new Control[] { this.shipcombo, secretary, this.beforexp, this.afterexp, this.getexp,
this.needexp, this.beforelv, this.afterlv, this.seacombo, this.evalcombo, this.flagbtn,
this.mvpbtn }) {
c.setData("disable-drag-move", true);
}
// 「新しいウィンドウを作る」を右クリックメニューに追加する
final MenuItem createNewWin = new MenuItem(this.getPopupMenu(), SWT.PUSH, 0);
createNewWin.setText("新しいウィンドウを開く(&N)\tCtrl+N");
createNewWin.setAccelerator(SWT.CTRL + 'N');
createNewWin.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
new CalcExpDialog().open();
}
});
new MenuItem(this.getPopupMenu(), SWT.SEPARATOR, 1);
this.shell.pack();
this.setShipComboData();
this.preset();
this.calc();
}
/**
* プリセットを行う
*/
public void preset() {
if (this.shipcombo.getSelectionIndex() > -1) {
ShipDto ship = this.shiplist.get(this.shipcombo.getSelectionIndex());
if (ship != null) {
int before = ship.getLv();
int after = this.afterlv.getSelection();
// 改造Lv
int afterlv = ship.getShipInfo().getAfterlv();
if (before < afterlv) {
after = afterlv;
}
if (before > after) {
after = before + 1;
}
// 目標レベルが150を超える場合は150に設定
after = Math.min(after, ExpTable.MAX_LEVEL);
String beforeexpstr = String.valueOf(ship.getExp());
String afterexpstr = String.valueOf(ExpTable.get().get(after));
this.beforelv.setSelection(before);
this.afterlv.setSelection(after);
this.beforexp.setText(beforeexpstr);
this.afterexp.setText(afterexpstr);
}
}
}
/**
* 計算を行う
*/
private void calc() {
if ((this.seacombo.getSelectionIndex() < 0) || (this.evalcombo.getSelectionIndex() < 0)) {
return;
}
// 必要経験値
int needexpint = Integer.parseInt(this.afterexp.getText()) - Integer.parseInt(this.beforexp.getText());
// 基礎経験値
int baseexp = SeaExp.get().get(this.seacombo.getItem(this.seacombo.getSelectionIndex()));
// 評価
double eval = EvaluateExp.get().get(this.evalcombo.getItem(this.evalcombo.getSelectionIndex()));
// 得られる経験値
int getexp = CalcExpUtils.getExp(baseexp, eval, this.flagbtn.getSelection(), this.mvpbtn.getSelection());
// 戦闘回数
int count = BigDecimal.valueOf(needexpint).divide(BigDecimal.valueOf(getexp), RoundingMode.CEILING).intValue();
// 1回の戦闘
this.getexp.setText(String.valueOf(getexp));
// 必要経験値
this.needexp.setText(Integer.toString(needexpint));
// 戦闘回数
this.battlecount.setText(Integer.toString(count));
}
/**
* コンボボックスに艦娘をセットします
*
* @param combo
*/
private void setShipComboData() {
int selected = -1;
if (this.shipcombo.getSelectionIndex() != -1) {
selected = this.shiplist.get(this.shipcombo.getSelectionIndex()).getId();
}
else {
ShipDto secretary = GlobalContext.getSecretary();
if (secretary != null) {
selected = secretary.getId();
}
}
// コンボボックスから全ての艦娘を削除
this.shipcombo.removeAll();
// 表示用文字列と艦娘の紐付けを削除
this.shiplist.clear();
// 艦娘IDの最大を取得してゼロ埋め長さを算出
for (ShipDto ship : GlobalContext.getShipMap().values()) {
this.shiplist.add(ship);
}
// 艦娘を経験値順でソート
Collections.sort(this.shiplist, new Comparator<ShipDto>() {
@Override
public int compare(ShipDto o1, ShipDto o2) {
return Integer.compare(o2.getExp(), o1.getExp());
}
});
// コンボボックスに追加
for (int i = 0; i < this.shiplist.size(); i++) {
ShipDto ship = this.shiplist.get(i);
this.shipcombo.add(ship.getFriendlyName());
if (ship.getId() == selected) {
this.shipcombo.select(i);
}
}
// コントロールを再配置
this.shipcombo.pack();
this.shipcombo.getParent().pack();
}
/**
* 艦娘の状態を更新する
*
*/
private void reload() {
this.setShipComboData();
this.preset();
this.calc();
}
/**
* 計算結果を更新する
*
*/
private final class UpdateListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
CalcExpDialog.this.calc();
}
}
/**
* 秘書艦をセット
*
*/
private final class SecretaryListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
CalcExpDialog.this.shipcombo.deselectAll();
CalcExpDialog.this.reload();
}
}
/**
* プリセットを行う
*
*/
private final class PresetListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
CalcExpDialog.this.preset();
CalcExpDialog.this.calc();
}
}
/**
* 今のレベルが変更された
*
*/
private final class BeforeLvListener extends SelectionAdapter {
private final Text beforexp;
private final Spinner beforelv;
/**
* @param beforexp
* @param beforelv
*/
private BeforeLvListener(Text beforexp, Spinner beforelv) {
this.beforexp = beforexp;
this.beforelv = beforelv;
}
@Override
public void widgetSelected(SelectionEvent e) {
String beforeexpstr = String.valueOf(ExpTable.get().get(this.beforelv.getSelection()));
this.beforexp.setText(beforeexpstr);
CalcExpDialog.this.calc();
}
}
/**
* 目標レベルが変更された
*
*/
private final class AfterLvListener extends SelectionAdapter {
private final Text afterexp;
private final Spinner afterlv;
/**
* @param afterexp
* @param afterlv
*/
private AfterLvListener(Text afterexp, Spinner afterlv) {
this.afterexp = afterexp;
this.afterlv = afterlv;
}
@Override
public void widgetSelected(SelectionEvent e) {
String afterexpstr = String.valueOf(ExpTable.get().get(this.afterlv.getSelection()));
this.afterexp.setText(afterexpstr);
CalcExpDialog.this.calc();
}
}
/**
* ホイールでレベルを動かす
*/
private final class WheelListener implements MouseWheelListener {
private final Spinner spinner;
private final SelectionListener listener;
public WheelListener(Spinner spinner, SelectionListener listener) {
this.spinner = spinner;
this.listener = listener;
}
@Override
public void mouseScrolled(MouseEvent e) {
if (e.count > 0) {
int cur = this.spinner.getSelection();
if (cur < this.spinner.getMaximum()) {
this.spinner.setSelection(cur + 1);
this.listener.widgetSelected(null);
}
}
else if (e.count < 0) {
int cur = this.spinner.getSelection();
if (cur > this.spinner.getMinimum()) {
this.spinner.setSelection(cur - 1);
this.listener.widgetSelected(null);
}
}
}
}
}