package logbook.gui;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.EnumMap;
import java.util.Map;
import java.util.Map.Entry;
import logbook.config.AppConfig;
import logbook.constants.AppConstants;
import logbook.dto.BattleAggDetailsDto;
import logbook.dto.BattleAggUnitDto;
import logbook.gui.listener.SaveWindowLocationAdapter;
import logbook.gui.listener.TreeKeyShortcutAdapter;
import logbook.gui.listener.TreeToClipboardAdapter;
import logbook.gui.logic.LayoutLogic;
import logbook.internal.BattleAggDate;
import logbook.internal.BattleAggUnit;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
/**
* 出撃統計
*
*/
public class BattleAggDialog extends Dialog {
/** ヘッダー */
private final String[] header = this.getTableHeader();
/** シェル */
private Shell shell;
/** ツリーテーブル */
private Tree tree;
/** メニューバー */
private Menu menubar;
/** [操作]メニュー */
private Menu opemenu;
private Menu tablemenu;
/**
* Create the dialog.
* @param parent
* @param style
*/
public BattleAggDialog(Shell parent) {
super(parent, SWT.SHELL_TRIM | SWT.MODELESS);
this.setText("SWT Dialog");
}
/**
* Open the dialog.
* @return the result
*/
public void open() {
this.createContents();
this.shell.open();
this.shell.layout();
Display display = this.getParent().getDisplay();
while (!this.shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
/**
* Create contents of the dialog.
*/
private void createContents() {
// シェルを作成
this.shell = new Shell(this.getParent(), this.getStyle());
this.shell.setSize(this.getSize());
// ウインドウ位置を復元
LayoutLogic.applyWindowLocation(this.getClass(), this.shell);
// 閉じた時にウインドウ位置を保存
this.shell.addShellListener(new SaveWindowLocationAdapter(this.getClass()));
this.shell.setText(this.getTitle());
this.shell.setLayout(new FillLayout(SWT.HORIZONTAL));
// メニューバー
this.menubar = new Menu(this.shell, SWT.BAR);
this.shell.setMenuBar(this.menubar);
// ツリーテーブル
this.tree = new Tree(this.shell, SWT.BORDER | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.MULTI);
this.tree.addKeyListener(new TreeKeyShortcutAdapter(this.header, this.tree));
this.tree.setLinesVisible(true);
this.tree.setHeaderVisible(true);
// メニューバーのメニュー
MenuItem operoot = new MenuItem(this.menubar, SWT.CASCADE);
operoot.setText("&Action");
this.opemenu = new Menu(operoot);
operoot.setMenu(this.opemenu);
MenuItem reload = new MenuItem(this.opemenu, SWT.NONE);
reload.setText("&Refresh\tF5");
reload.setAccelerator(SWT.F5);
reload.addSelectionListener(new TableReloadAdapter());
// テーブル右クリックメニュー
this.tablemenu = new Menu(this.tree);
this.tree.setMenu(this.tablemenu);
MenuItem sendclipbord = new MenuItem(this.tablemenu, SWT.NONE);
sendclipbord.addSelectionListener(new TreeToClipboardAdapter(this.header, this.tree));
sendclipbord.setText("&Copy");
MenuItem reloadtable = new MenuItem(this.tablemenu, SWT.NONE);
reloadtable.setText("&Refresh");
reloadtable.addSelectionListener(new TableReloadAdapter());
this.setTableHeader();
this.reloadTable();
}
/**
* タイトルを返します
* @return String
*/
private String getTitle() {
return "Battle Statistic";
}
/**
* ウインドウサイズを返します
* @return Point
*/
private Point getSize() {
return new Point(600, 350);
}
/**
* テーブルボディーをクリアする
*/
private void disposeTableBody() {
TreeItem[] items = this.tree.getItems();
for (int i = 0; i < items.length; i++) {
items[i].dispose();
}
}
/**
* テーブルヘッダーをセットする
*/
private void setTableHeader() {
for (int i = 0; i < this.header.length; i++) {
TreeColumn col = new TreeColumn(this.tree, SWT.LEFT);
col.setText(this.header[i]);
}
this.packTableHeader();
}
/**
* テーブルヘッダーの幅を調節する
*/
private void packTableHeader() {
TreeColumn[] columns = this.tree.getColumns();
for (int i = 0; i < columns.length; i++) {
columns[i].pack();
}
}
/**
* テーブルヘッダーを返します
* @return String[]
*/
private String[] getTableHeader() {
return new String[] { "Total", "Sortie Total", "Victory", "S-rank", "A-rank", "B-rank", "C-rank", "D-rank" };
}
/**
* テーブルをリロードする
*/
private void reloadTable() {
this.disposeTableBody();
boolean first = true;
Map<BattleAggUnit, BattleAggUnitDto> aggMap = this.load();
for (Entry<BattleAggUnit, BattleAggUnitDto> entry : aggMap.entrySet()) {
BattleAggUnitDto dto = entry.getValue();
BattleAggDetailsDto total = dto.getTotal();
TreeItem root = new TreeItem(this.tree, SWT.NONE);
// 合計
root.setText(new String[] { entry.getKey().toString(), Integer.toString(total.getStart()),
Integer.toString(total.getWin()), Integer.toString(total.getS()), Integer.toString(total.getA()),
Integer.toString(total.getB()), Integer.toString(total.getC()), Integer.toString(total.getD()) });
// ボス
TreeItem boss = new TreeItem(root, SWT.NONE);
boss.setText(new String[] { "Boss", "-", Integer.toString(total.getBossWin()),
Integer.toString(total.getBossS()), Integer.toString(total.getBossA()),
Integer.toString(total.getBossB()), Integer.toString(total.getBossC()),
Integer.toString(total.getBossD()) });
// 海域毎
for (Entry<String, BattleAggDetailsDto> areaEntry : dto.getAreaDetails()) {
BattleAggDetailsDto area = areaEntry.getValue();
TreeItem sub = new TreeItem(root, SWT.NONE);
sub.setText(new String[] { areaEntry.getKey(), Integer.toString(area.getStart()),
Integer.toString(area.getWin()), Integer.toString(area.getS()), Integer.toString(area.getA()),
Integer.toString(area.getB()), Integer.toString(area.getC()), Integer.toString(area.getD()) });
// ボス
TreeItem subBoss = new TreeItem(sub, SWT.NONE);
subBoss.setText(new String[] { "Boss", "-",
Integer.toString(area.getBossWin()), Integer.toString(area.getBossS()),
Integer.toString(area.getBossA()), Integer.toString(area.getBossB()),
Integer.toString(area.getBossC()), Integer.toString(area.getBossD()) });
}
if (first)
root.setExpanded(true);
first = false;
}
this.packTableHeader();
}
/**
* 報告書を読み込み、集計結果を返す
* @return 集計結果
*/
private Map<BattleAggUnit, BattleAggUnitDto> load() {
Map<BattleAggUnit, BattleAggUnitDto> aggMap = new EnumMap<>(BattleAggUnit.class);
// 日付書式
SimpleDateFormat format = new SimpleDateFormat(AppConstants.DATE_FORMAT);
// 今日
Calendar today = BattleAggDate.TODAY.get();
// 先週
Calendar lastWeek = BattleAggDate.LAST_WEEK.get();
// 先月
Calendar lastMonth = BattleAggDate.LAST_MONTH.get();
// 読み込む最小の日付(>=)
Calendar min = lastMonth;
// 海戦・ドロップ報告書読み込み
File report = new File(FilenameUtils.concat(AppConfig.get().getReportPath(), AppConstants.LOG_BATTLE_RESULT));
try {
LineIterator ite = new LineIterator(
new InputStreamReader(new FileInputStream(report), AppConstants.CHARSET));
try {
// ヘッダーを読み飛ばす
if (ite.hasNext()) {
ite.next();
}
while (ite.hasNext()) {
try {
String line = ite.next();
String[] cols = line.split(",", -1);
// 日付
Calendar date = DateUtils.toCalendar(format.parse(cols[0]));
date.setTimeZone(AppConstants.TIME_ZONE_MISSION);
date.setFirstDayOfWeek(Calendar.MONDAY);
// 読み込む最小の日付未満の場合は読み飛ばす
if (min.compareTo(date) > 0) {
continue;
}
// 海域
String area = cols[1];
// ランク
String rank = cols[4];
// 出撃
boolean isStart = StringUtils.indexOf(cols[3], "Sortie") > -1;
// ボス
boolean isBoss = StringUtils.indexOf(cols[3], "Boss") > -1;
// デイリー集計
this.agg(BattleAggUnit.DAILY, aggMap, today, Calendar.DAY_OF_YEAR, date, area, rank, isStart,
isBoss);
// ウィークリー集計
this.agg(BattleAggUnit.WEEKLY, aggMap, today, Calendar.WEEK_OF_YEAR, date, area, rank, isStart,
isBoss);
// マンスリー集計
this.agg(BattleAggUnit.MONTHLY, aggMap, today, Calendar.MONTH, date, area, rank, isStart,
isBoss);
// 先週の集計
this.agg(BattleAggUnit.LAST_WEEK, aggMap, lastWeek, Calendar.WEEK_OF_YEAR, date, area, rank,
isStart, isBoss);
// 先月の集計
this.agg(BattleAggUnit.LAST_MONTH, aggMap, lastMonth, Calendar.MONTH, date, area, rank,
isStart, isBoss);
} catch (Exception e) {
continue;
}
}
} finally {
ite.close();
}
} catch (Exception e) {
}
return aggMap;
}
/**
* 集計する
*
* @param unit 集計単位(デイリーなど)
* @param to 集計結果
* @param std 基準日
* @param field {@link Calendar#get(int)}のフィールド値
* @param target 集計対象の日付
* @param area 海域
* @param rank ランク
* @param isStart 出撃
* @param isBoss ボス
*/
private void agg(BattleAggUnit unit, Map<BattleAggUnit, BattleAggUnitDto> to, Calendar std, int field,
Calendar target, String area, String rank, boolean isStart, boolean isBoss) {
if (std.get(field) == target.get(field)) {
BattleAggUnitDto aggUnit = to.get(unit);
if (aggUnit == null) {
aggUnit = new BattleAggUnitDto();
to.put(unit, aggUnit);
}
aggUnit.add(area, rank, isStart, isBoss);
}
}
/**
* テーブルを再読み込みするリスナーです
*/
protected class TableReloadAdapter extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
BattleAggDialog.this.reloadTable();
}
}
}