1 package com.ranfa.main;
3 import java.awt.BorderLayout;
4 import java.awt.EventQueue;
6 import java.nio.file.Files;
7 import java.nio.file.Paths;
8 import java.util.ArrayList;
9 import java.util.Random;
10 import java.util.concurrent.CompletableFuture;
11 import java.util.concurrent.ExecutorService;
12 import java.util.concurrent.Executors;
13 import java.util.function.BiConsumer;
15 import javax.swing.DefaultComboBoxModel;
16 import javax.swing.JButton;
17 import javax.swing.JCheckBox;
18 import javax.swing.JComboBox;
19 import javax.swing.JFrame;
20 import javax.swing.JLabel;
21 import javax.swing.JOptionPane;
22 import javax.swing.JPanel;
23 import javax.swing.JScrollPane;
24 import javax.swing.JSpinner;
25 import javax.swing.JTextArea;
26 import javax.swing.border.EmptyBorder;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
31 import com.jgoodies.forms.layout.ColumnSpec;
32 import com.jgoodies.forms.layout.FormLayout;
33 import com.jgoodies.forms.layout.FormSpecs;
34 import com.jgoodies.forms.layout.RowSpec;
35 import com.ranfa.lib.CheckVersion;
36 import com.ranfa.lib.EstimateAlbumTypeCycle;
37 import com.ranfa.lib.ManualUpdateThreadImpl;
38 import com.ranfa.lib.Scraping;
39 import com.ranfa.lib.SettingJSONProperty;
40 import com.ranfa.lib.Settings;
41 import com.ranfa.lib.Song;
42 import com.ranfa.lib.TwitterIntegration;
43 import com.ranfa.lib.Version;
45 @Version(major = 2, minor = 0, patch = 3)
46 public class DelesteRandomSelector extends JFrame {
48 private static ArrayList<Song> selectedSongsList = new ArrayList<>();
50 private JPanel contentPane;
51 private JPanel panelNorth;
52 private JPanel panelWest;
53 private JLabel labelVersion;
54 private JLabel labelTitle;
55 private JLabel labelDifficulty;
56 private JLabel labelLevel;
57 private JSpinner spinnerLevel;
58 private JCheckBox checkMoreLv;
59 private JCheckBox checkLessLv;
60 private JPanel panelEast;
61 private JPanel panelCentre;
62 private JButton btnImport;
63 private JButton btnStart;
64 private JButton btnExit;
65 private JComboBox comboDifficultySelect;
66 private JLabel labelLvCaution;
67 private JComboBox comboAttribute;
68 private SettingJSONProperty property = new SettingJSONProperty();
69 private JButton btnTwitterIntegration;
70 private String[] integratorArray;
71 private boolean integratorBool = false;
72 private JTextArea textArea;
73 private JScrollPane scrollPane;
74 private CompletableFuture<Void> softwareUpdateFuture = null;
75 private CompletableFuture<Void> albumTypeEstimateFuture = null;
76 private String albumType = Messages.MSGAlbumTypeBeingCalculated.toString();
77 private Logger logger = LoggerFactory.getLogger(DelesteRandomSelector.class);
78 private ManualUpdateThreadImpl impl;
79 private Thread manualUpdateThread;
80 private JButton btnManualUpdate;
83 * Launch the application.
85 public static void main(String[] args) {
86 EventQueue.invokeLater(() -> {
88 DelesteRandomSelector frame = new DelesteRandomSelector();
89 frame.setVisible(true);
90 } catch (Exception e) {
98 * this.getClass() + ":[LEVEL]: " +
104 public DelesteRandomSelector() {
105 boolean isFirst = !Scraping.databaseExists();
107 JOptionPane.showMessageDialog(this, Messages.MSGDatabaseNotExist.toString());
108 if(!Scraping.writeToJson(Scraping.getWholeData())) {
109 JOptionPane.showMessageDialog(this, "Exception:NullPointerException\nCannot Keep up! Please re-download this Application!");
110 throw new NullPointerException("FATAL: cannot continue!");
113 ExecutorService es = Executors.newWorkStealingPool();
114 CompletableFuture<ArrayList<Song>> getFromJsonFuture = CompletableFuture.supplyAsync(() -> Scraping.getFromJson(), es);
115 CompletableFuture<ArrayList<Song>> getWholeDataFuture = CompletableFuture.supplyAsync(() -> Scraping.getWholeData(), es);
116 if(!Settings.fileExists() && !Settings.writeDownJSON()) {
117 JOptionPane.showMessageDialog(this, "Exception:NullPointerException\nCannot Keep up! Please re-download this Application!");
118 throw new NullPointerException("FATAL: cannot continue!");
120 this.logger.debug("Loading settings...");
121 this.property.setCheckLibraryUpdates(Settings.needToCheckLibraryUpdates());
122 this.property.setCheckVersion(Settings.needToCheckVersion());
123 this.property.setWindowWidth(Settings.getWindowWidth());
124 this.property.setWindowHeight(Settings.getWindowHeight());
125 this.property.setSongLimit(Settings.getSongsLimit());
126 this.property.setSaveScoreLog(Settings.saveScoreLog());
127 this.logger.debug("Load settings done.");
128 this.logger.debug("Version check: {}", this.property.isCheckVersion());
129 this.logger.debug("Library update check: {}", this.property.isCheckLibraryUpdates());
130 this.logger.debug("Window Width: {}", this.property.getWindowWidth());
131 this.logger.debug("Window Height: {}", this.property.getWindowHeight());
132 this.logger.debug("Song Limit: {}", this.property.getSongLimit());
133 this.logger.debug("SaveScoreLog: {}", this.property.isSaveScoreLog());
134 EstimateAlbumTypeCycle.Initialization();
135 if(Files.exists(Paths.get("generated/albumCycle.json"))) {
136 this.albumType = EstimateAlbumTypeCycle.getCurrentCycle();
138 if(this.property.isCheckVersion()) {
139 this.softwareUpdateFuture = CompletableFuture.runAsync(() -> CheckVersion.needToBeUpdated(), es);
141 BiConsumer<ArrayList<Song>, ArrayList<Song>> updateConsumer = (list1, list2) -> {
142 this.logger.info("Checking database updates...");
143 if(list1.size() > list2.size()) {
144 long time = System.currentTimeMillis();
145 this.logger.info("{} Update detected.", (list1.size() - list2.size()));
146 Scraping.writeToJson(list1);
147 this.logger.info("Update completed in {} ms", (System.currentTimeMillis() - time));
148 this.logger.info("Updated database size: {}", list1.size());
150 this.logger.info("database is up-to-date.");
153 Runnable setEnabled = () -> {
156 } catch (InterruptedException e1) {
157 this.logger.error("Thread has been interrupted during waiting cooldown.", e1);
159 this.btnImport.setEnabled(true);
160 this.btnImport.setText(Messages.MSGNarrowingDownSongs.toString());
162 getWholeDataFuture.thenAcceptAsync(list -> this.logger.info("Scraping data size:" + list.size()), es);
163 getFromJsonFuture.thenAcceptAsync(list -> this.logger.info("Currently database size:" + list.size()), es);
164 if(this.property.isCheckLibraryUpdates()) {
165 CompletableFuture<Void> updatedFuture = getWholeDataFuture.thenAcceptBothAsync(getFromJsonFuture, updateConsumer, es);
166 updatedFuture.thenRunAsync(setEnabled, es);
168 this.logger.debug("Version: {}", CheckVersion.getVersion());
169 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
170 // this.setBounds(100, 100, this.property.getWindowWidth(), this.property.getWindowHeight());
171 this.setBounds(100, 100, 640, 360);
172 this.contentPane = new JPanel();
173 this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
174 this.setContentPane(this.contentPane);
175 this.contentPane.setLayout(new BorderLayout(0, 0));
177 this.panelNorth = new JPanel();
178 this.contentPane.add(this.panelNorth, BorderLayout.NORTH);
179 this.panelNorth.setLayout(new FormLayout(new ColumnSpec[] {
180 ColumnSpec.decode("max(302dlu;default)"),
181 FormSpecs.RELATED_GAP_COLSPEC,
182 ColumnSpec.decode("40px"),},
184 RowSpec.decode("20px"),}));
186 this.labelTitle = new JLabel(Messages.MSGTitle.toString());
187 this.labelTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 16));
188 this.panelNorth.add(this.labelTitle, "1, 1, center, top");
190 this.labelVersion = new JLabel(CheckVersion.getVersion());
191 this.labelVersion.setFont(new Font("SansSerif", Font.BOLD, 12));
192 this.panelNorth.add(this.labelVersion, "3, 1, right, top");
194 this.panelWest = new JPanel();
195 this.contentPane.add(this.panelWest, BorderLayout.WEST);
196 this.panelWest.setLayout(new FormLayout(new ColumnSpec[] {
197 FormSpecs.LABEL_COMPONENT_GAP_COLSPEC,
198 ColumnSpec.decode("112px:grow"),},
200 FormSpecs.LINE_GAP_ROWSPEC,
201 RowSpec.decode("19px"),
202 FormSpecs.RELATED_GAP_ROWSPEC,
203 RowSpec.decode("max(12dlu;default)"),
204 FormSpecs.RELATED_GAP_ROWSPEC,
205 RowSpec.decode("max(12dlu;default)"),
206 FormSpecs.RELATED_GAP_ROWSPEC,
207 RowSpec.decode("12dlu"),
208 FormSpecs.RELATED_GAP_ROWSPEC,
209 RowSpec.decode("max(12dlu;default)"),
210 FormSpecs.RELATED_GAP_ROWSPEC,
211 RowSpec.decode("max(12dlu;default)"),
212 FormSpecs.RELATED_GAP_ROWSPEC,
213 RowSpec.decode("max(12dlu;default)"),
214 FormSpecs.RELATED_GAP_ROWSPEC,
215 RowSpec.decode("max(52dlu;default)"),}));
217 this.labelDifficulty = new JLabel(Messages.MSGSelectDifficulty.toString());
218 this.labelDifficulty.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
219 this.panelWest.add(this.labelDifficulty, "2, 2, center, default");
221 this.comboDifficultySelect = new JComboBox();
222 this.comboDifficultySelect.setFont(new Font("Dialog", Font.BOLD, 12));
223 this.comboDifficultySelect.setModel(new DefaultComboBoxModel(new String[] {Messages.MSGNonSelected.toString(), "DEBUT", "REGULAR", "PRO", "MASTER", "MASTER+", "ⓁMASTER+", "LIGHT", "TRICK", "PIANO", "FORTE", "WITCH"}));
224 this.panelWest.add(this.comboDifficultySelect, "2, 4, fill, default");
226 this.comboAttribute = new JComboBox();
227 this.comboAttribute.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
228 this.comboAttribute.setModel(new DefaultComboBoxModel(new String[] {Messages.MSGNonSelected.toString(), "全タイプ", "キュート", "クール", "パッション"}));
229 this.panelWest.add(this.comboAttribute, "2, 6, fill, default");
231 this.labelLevel = new JLabel(Messages.MSGSongLevel.toString());
232 this.labelLevel.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
233 this.panelWest.add(this.labelLevel, "2, 8, center, default");
235 this.spinnerLevel = new JSpinner();
236 this.panelWest.add(this.spinnerLevel, "2, 10");
238 this.checkLessLv = new JCheckBox(Messages.MSGBelowSpecificLevel.toString());
239 this.checkLessLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
240 this.panelWest.add(this.checkLessLv, "2, 12");
242 this.checkMoreLv = new JCheckBox(Messages.MSGOverSpecificLevel.toString());
243 this.checkMoreLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
244 this.panelWest.add(this.checkMoreLv, "2, 14");
246 this.labelLvCaution = new JLabel(Messages.MSGLevelCheckboxInfo.toString());
247 this.labelLvCaution.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
248 this.panelWest.add(this.labelLvCaution, "2, 16, fill, fill");
250 this.panelEast = new JPanel();
251 this.contentPane.add(this.panelEast, BorderLayout.EAST);
252 this.panelEast.setLayout(new FormLayout(new ColumnSpec[] {
253 ColumnSpec.decode("98px"),},
255 RowSpec.decode("26px"),
256 FormSpecs.RELATED_GAP_ROWSPEC,
257 RowSpec.decode("max(36dlu;default)"),
258 FormSpecs.RELATED_GAP_ROWSPEC,
259 FormSpecs.DEFAULT_ROWSPEC,
260 FormSpecs.RELATED_GAP_ROWSPEC,
261 RowSpec.decode("max(30dlu;default)"),
262 FormSpecs.RELATED_GAP_ROWSPEC,
263 RowSpec.decode("max(15dlu;default)"),
264 FormSpecs.RELATED_GAP_ROWSPEC,
265 RowSpec.decode("max(11dlu;default)"),
266 FormSpecs.RELATED_GAP_ROWSPEC,
267 FormSpecs.DEFAULT_ROWSPEC,
268 FormSpecs.RELATED_GAP_ROWSPEC,
269 FormSpecs.DEFAULT_ROWSPEC,
270 FormSpecs.RELATED_GAP_ROWSPEC,
271 FormSpecs.DEFAULT_ROWSPEC,}));
273 this.btnImport = new JButton(Messages.MSGUpdatingDatabase.toString());
274 this.btnImport.setEnabled(false);
275 this.btnImport.addActionListener(e -> {
276 if(this.impl != null) {
277 if(!this.impl.getFlag()) {
278 JOptionPane.showMessageDialog(null, Messages.MSGManualUpdateNotCompleteYet.toString());
281 ArrayList<Song> fromJson = Scraping.getFromJson();
282 ArrayList<Song> specificlevelList = Scraping.getSpecificLevelSongs(fromJson, (Integer)DelesteRandomSelector.this.spinnerLevel.getValue(), DelesteRandomSelector.this.checkLessLv.isSelected(), DelesteRandomSelector.this.checkMoreLv.isSelected());
283 ArrayList<Song> specificDifficultyList = Scraping.getSpecificDifficultySongs(specificlevelList, DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().toString());
284 ArrayList<Song> specificAttributeList = Scraping.getSpecificAttributeSongs(specificDifficultyList, DelesteRandomSelector.this.comboAttribute.getSelectedItem().toString());
285 ArrayList<Song> specificTypeList = Scraping.getSpecificAlbumTypeSongs(specificAttributeList, EstimateAlbumTypeCycle.getCurrentCycle());
286 if(!selectedSongsList.isEmpty()) {
287 selectedSongsList.clear();
289 selectedSongsList.addAll((DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.MASTERPLUS) || DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.LEGACYMASTERPLUS)) ? specificTypeList : specificAttributeList);
290 DelesteRandomSelector.this.logger.info("Songs are selected.We are Ready to go.");
291 JOptionPane.showMessageDialog(null, Messages.MSGCompleteNarrowDown.toString());
293 this.btnImport.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
294 this.panelEast.add(this.btnImport, "1, 3, fill, fill");
296 this.btnStart = new JButton(Messages.MSGCalcStart.toString());
297 this.btnStart.addActionListener(e -> {
298 Random random = new Random(System.currentTimeMillis());
299 String paneString = "";
300 DelesteRandomSelector.this.integratorArray = new String[DelesteRandomSelector.this.property.getSongLimit()];
301 for(int i = 0; i < DelesteRandomSelector.this.property.getSongLimit(); i++) {
302 int randomInt = random.nextInt(selectedSongsList.size());
303 String typeString = DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.MASTERPLUS) || DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.LEGACYMASTERPLUS) ? EstimateAlbumTypeCycle.getCurrentCycle() : "";
304 paneString = paneString + (i + 1) + Messages.MSGNumberOfSongs.toString() + " " + selectedSongsList.get(randomInt).getAttribute() + " [" + selectedSongsList.get(randomInt).getDifficulty() + "]「" + selectedSongsList.get(randomInt).getName() + "」!(Lv:" + selectedSongsList.get(randomInt).getLevel() + " " + typeString + ")\n\n";
305 DelesteRandomSelector.this.integratorArray[i] = selectedSongsList.get(randomInt).getName() + "(Lv" + selectedSongsList.get(randomInt).getLevel() + ")\n";
307 paneString = paneString + Messages.MSGThisPhrase.toString() + DelesteRandomSelector.this.property.getSongLimit() + Messages.MSGPlayPhrase.toString();
308 DelesteRandomSelector.this.textArea.setText(paneString);
309 DelesteRandomSelector.this.integratorBool = true;
310 DelesteRandomSelector.this.logger.info("show up completed.");
312 this.btnStart.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
313 this.panelEast.add(this.btnStart, "1, 7, fill, fill");
315 this.btnTwitterIntegration = new JButton(Messages.MSGTwitterIntegration.toString());
316 this.btnTwitterIntegration.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 11));
317 this.btnTwitterIntegration.addActionListener(e -> {
318 boolean authorizationStatus = TwitterIntegration.authorization();
319 String updatedStatus = Messages.MSGUsingThisAppPhrase.toString();
320 int lengthLimit = updatedStatus.length();
321 boolean isBroken = false;
322 if(!DelesteRandomSelector.this.integratorBool) {
323 JOptionPane.showMessageDialog(null, Messages.MSGNotPlayYet.toString());
326 for (String element : DelesteRandomSelector.this.integratorArray) {
327 updatedStatus = updatedStatus + element;
328 lengthLimit += element.length();
329 if(lengthLimit > 69) {
335 updatedStatus = updatedStatus + Messages.MSGTwitterPlayOtherwisePhrase.toString() + "\n#DelesteRandomSelector #デレステ ";
337 updatedStatus = updatedStatus + Messages.MSGTwitterPlayOnlyPhrase.toString() + "\n#DelesteRandomSelector #デレステ ";
339 DelesteRandomSelector.this.logger.info("status message constructed.");
340 lengthLimit = updatedStatus.length();
341 if(authorizationStatus) {
342 int option = JOptionPane.showConfirmDialog(null, Messages.MSGTwitterIntegrationConfirm.toString() + updatedStatus + Messages.MSGStringLength.toString() + lengthLimit);
343 DelesteRandomSelector.this.logger.info("user seletced: " + option);
345 case JOptionPane.OK_OPTION:
346 TwitterIntegration.PostTwitter(updatedStatus);
347 DelesteRandomSelector.this.logger.info("Success to update the status.");
348 JOptionPane.showMessageDialog(null, Messages.MSGCompletePost.toString());
350 case JOptionPane.NO_OPTION:
351 DelesteRandomSelector.this.logger.info("There is no will to post.");
353 case JOptionPane.CANCEL_OPTION:
354 DelesteRandomSelector.this.logger.info("The Operation was canceled by user.");
360 DelesteRandomSelector.this.logger.info("seems to reject the permission.it should need try again.");
364 this.btnManualUpdate = new JButton(Messages.MSGManualUpdate.toString());
365 this.btnManualUpdate.addActionListener(e -> {
366 this.impl = new ManualUpdateThreadImpl();
367 this.manualUpdateThread = new Thread(this.impl);
368 this.manualUpdateThread.setName("ManualUpdate-thread");
369 this.manualUpdateThread.setDaemon(false);
370 this.manualUpdateThread.start();
372 this.panelEast.add(this.btnManualUpdate, "1, 9");
373 this.panelEast.add(this.btnTwitterIntegration, "1, 11");
375 this.btnExit = new JButton(Messages.MSGTerminate.toString());
376 this.btnExit.addActionListener(e -> {
377 if(DelesteRandomSelector.this.softwareUpdateFuture.isDone() || DelesteRandomSelector.this.albumTypeEstimateFuture.isDone() || !this.impl.getFlag()) {
378 DelesteRandomSelector.this.logger.info("Requested Exit by Button");
381 JOptionPane.showMessageDialog(null, Messages.MSGInternalYpdateNotDoneYet.toString());
384 this.btnExit.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
385 this.panelEast.add(this.btnExit, "1, 13");
387 this.panelCentre = new JPanel();
388 this.contentPane.add(this.panelCentre, BorderLayout.CENTER);
389 this.panelCentre.setLayout(new BorderLayout(0, 0));
391 this.textArea = new JTextArea();
392 this.textArea.setText(Messages.MSGNarrowDownProcedure.toString() + this.property.getSongLimit() + Messages.MSGCurrentAlbumType.toString() + this.albumType);
393 this.textArea.setEditable(false);
395 this.scrollPane = new JScrollPane(this.textArea);
396 this.panelCentre.add(this.scrollPane, BorderLayout.CENTER);
397 if(isFirst || !this.property.isCheckLibraryUpdates()) {