From 7ef3d014c497eac5a6534fa71d3eff1684216e78 Mon Sep 17 00:00:00 2001
From: jez04 <david.jezek@post.cz>
Date: Thu, 24 Apr 2025 00:51:58 +0200
Subject: [PATCH] feat: assignment lab 10

---
 .gitignore                                    |   3 +-
 pom.xml                                       |   4 +-
 scores.csv                                    |   6 +
 src/main/java/lab/data/Level.java             |   9 ++
 src/main/java/lab/gui/App.java                |  26 +++-
 src/main/java/lab/gui/EditController.java     | 112 ++++++++++++++++++
 .../java/lab/gui/MainScreenController.java    |  34 +++++-
 src/main/java/lab/storage/FileStorage.java    |   9 +-
 src/main/java/module-info.java                |   1 +
 src/main/resources/lab/gui/application.css    |  16 +++
 src/main/resources/lab/gui/edit.fxml          |  48 ++++++++
 11 files changed, 255 insertions(+), 13 deletions(-)
 create mode 100644 scores.csv
 create mode 100644 src/main/java/lab/gui/EditController.java
 create mode 100644 src/main/resources/lab/gui/edit.fxml

diff --git a/.gitignore b/.gitignore
index e4a1596..3a516dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,5 @@
 .project
 .classpath
 *.mv.db
-*.trace.db
\ No newline at end of file
+*.trace.db
+.idea/
diff --git a/pom.xml b/pom.xml
index 1d7427f..4fa51e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,10 +3,10 @@
 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>cz.vsb.fei.java2</groupId>
-	<artifactId>java2-lab09-v3</artifactId>
+	<artifactId>java2-lab10-v3</artifactId>
 	<version>0.0.1-SNAPHOST</version>
 	<packaging>jar</packaging>
-	<name>java2-lab09-v3</name>
+	<name>java2-lab10-v3</name>
 	<properties>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<maven.compiler.source>21</maven.compiler.source>
diff --git a/scores.csv b/scores.csv
new file mode 100644
index 0000000..1ed1b37
--- /dev/null
+++ b/scores.csv
@@ -0,0 +1,6 @@
+StackSultan;66;MEDIUM
+SnapMaster;167;MEDIUM
+TikTokTornado;210;EASY
+ScriptSensei;158;EASY
+SnapMasteraaa;167000;null
+ScriptSenseidddd;1580000;null
diff --git a/src/main/java/lab/data/Level.java b/src/main/java/lab/data/Level.java
index 4c8e6bd..46f7b95 100644
--- a/src/main/java/lab/data/Level.java
+++ b/src/main/java/lab/data/Level.java
@@ -2,4 +2,13 @@ package lab.data;
 
 public enum Level {
 	EASY,  MEDIUM, HARD;
+	
+	public static Level from(String s) {
+		for(Level l :values()) {
+			if(l.toString().equals(s)) {
+				return l;
+			}
+		}
+		return null;
+	}
 }
diff --git a/src/main/java/lab/gui/App.java b/src/main/java/lab/gui/App.java
index b2e5857..7d8ea2a 100644
--- a/src/main/java/lab/gui/App.java
+++ b/src/main/java/lab/gui/App.java
@@ -13,9 +13,12 @@ import javafx.scene.Scene;
 import javafx.scene.control.Alert;
 import javafx.scene.control.Alert.AlertType;
 import javafx.scene.control.ButtonType;
+import javafx.stage.Modality;
 import javafx.stage.Stage;
+import javafx.stage.StageStyle;
 import javafx.stage.WindowEvent;
 import lab.Setup;
+import lab.data.Score;
 import lab.storage.FileStorage;
 
 /**
@@ -28,7 +31,7 @@ public class App extends Application {
 
 	private static Logger log = LogManager.getLogger(App.class);
 	private GameController gameController;
-
+	private MainScreenController menuController;
 	private boolean viewMode;
 
 	private Stage primaryStage;
@@ -74,13 +77,32 @@ public class App extends Application {
 		// Construct a main window with a canvas.
 		FXMLLoader menuLoader = new FXMLLoader(getClass().getResource("/lab/gui/mainScreen.fxml"));
 		Parent root = menuLoader.load();
-		MainScreenController menuController = menuLoader.getController();
+		menuController = menuLoader.getController();
 		menuController.setApp(this);
 		Scene scene = new Scene(root);
 		URL cssUrl = getClass().getResource("application.css");
 		scene.getStylesheets().add(cssUrl.toString());
 		primaryStage.setScene(scene);
 	}
+	
+	public Stage createDialogStage(Score score) throws IOException {
+		Stage dialog = new Stage();
+		dialog.initStyle(StageStyle.UTILITY);
+		FXMLLoader editLoader = new FXMLLoader(getClass().getResource("/lab/gui/edit.fxml"));
+		Parent root = editLoader.load();
+		EditController editController = editLoader.getController();
+		editController.setStage(dialog);
+		editController.setObjectToEdit(score);
+		editController.setMainScreenController(menuController);
+		Scene scene = new Scene(root);
+		URL cssUrl = getClass().getResource("application.css");
+		scene.getStylesheets().add(cssUrl.toString());
+		dialog.setScene(scene);
+		dialog.initModality(Modality.APPLICATION_MODAL);
+		dialog.initOwner(primaryStage);
+		return dialog;
+
+	}
 
 	@Override
 	public void stop() throws Exception {
diff --git a/src/main/java/lab/gui/EditController.java b/src/main/java/lab/gui/EditController.java
new file mode 100644
index 0000000..e5ad2fe
--- /dev/null
+++ b/src/main/java/lab/gui/EditController.java
@@ -0,0 +1,112 @@
+package lab.gui;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import javafx.collections.ListChangeListener;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.geometry.HPos;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.SelectionMode;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableRow;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextField;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.GridPane;
+import javafx.scene.text.Text;
+import javafx.stage.Modality;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import lab.Setup;
+import lab.data.Score;
+import lab.game.Difficult;
+import lombok.Setter;
+
+/**
+ * 
+ */
+public class EditController {
+
+	private static Logger log = LogManager.getLogger(EditController.class);
+
+	@FXML
+	private Button btnOk;
+
+	@FXML
+	private Button btnCancel;
+
+	@FXML
+	private Label txtTitle;
+
+	@FXML
+	private GridPane content;
+
+	private Object data;
+
+	private App app;
+
+	private Map<String, TextField> nameToTextField = new HashMap<>();
+
+	@Setter
+	private Stage stage;
+
+	@Setter
+	private MainScreenController mainScreenController;
+
+	@FXML
+	void btnOkAction(ActionEvent event) {
+		//TODO: add code to read values from text fields
+		
+		mainScreenController.updateData((Score)data);
+		stage.hide();
+	}
+
+	@FXML
+	void btnCancelAction(ActionEvent event) {
+		stage.hide();
+	}
+
+	@FXML
+	void initialize() {
+		log.info("Screen initialized.");
+	}
+
+	public void setObjectToEdit(Object data) {
+		this.data = data;
+		log.info("data set {}", data);
+		content.getChildren().clear();
+		
+		//TODO: code to detect properties and add rows to dialog  
+	}
+
+	private void addDialogRow(int rowNumber, String name, String descriptionName, String stringValue,
+			boolean editable) {
+		Label label = new Label(descriptionName);
+		TextField textField = new TextField(stringValue);
+		textField.setEditable(editable);
+		nameToTextField.put(name, textField);
+		content.addRow(rowNumber, label, textField);
+		GridPane.setHalignment(label, HPos.RIGHT);
+	}
+
+}
diff --git a/src/main/java/lab/gui/MainScreenController.java b/src/main/java/lab/gui/MainScreenController.java
index 07d483b..1c96913 100644
--- a/src/main/java/lab/gui/MainScreenController.java
+++ b/src/main/java/lab/gui/MainScreenController.java
@@ -10,14 +10,23 @@ import org.apache.logging.log4j.Logger;
 import javafx.collections.ListChangeListener;
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
+import javafx.scene.Group;
+import javafx.scene.Scene;
 import javafx.scene.control.Button;
 import javafx.scene.control.SelectionMode;
 import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableRow;
 import javafx.scene.control.TableView;
 import javafx.scene.control.TextField;
 import javafx.scene.control.ToggleButton;
 import javafx.scene.control.ToggleGroup;
 import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.text.Text;
+import javafx.stage.Modality;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
 import lab.Setup;
 import lab.data.Score;
 import lab.game.Difficult;
@@ -41,7 +50,6 @@ public class MainScreenController {
 	@FXML
 	private Button btnDelete;
 
-
 	@FXML
 	private TableView<Score> scores;
 
@@ -97,7 +105,7 @@ public class MainScreenController {
 	void btnLoadFirstTenAction(ActionEvent event) {
 		updateScoreTable(Setup.getInstance().getScoreStorageInterface().getFirstTen());
 	}
-	
+
 	@FXML
 	void btnDeleteAction(ActionEvent event) {
 		List<Score> selectedScores = new ArrayList<>(scores.getSelectionModel().getSelectedItems());
@@ -110,6 +118,11 @@ public class MainScreenController {
 		this.scores.getItems().addAll(scores);
 	}
 
+	public void updateData(Score score) {
+		Setup.getInstance().getScoreStorageInterface().save(score);
+		updateScoreTable(Setup.getInstance().getScoreStorageInterface().getAll());
+	}
+	
 	@FXML
 	void initialize() {
 		assert difficult != null : "fx:id=\"difficult\" was not injected: check your FXML file 'mainScreen.fxml'.";
@@ -123,10 +136,23 @@ public class MainScreenController {
 		difficultColumn.setCellValueFactory(new PropertyValueFactory<>("level"));
 
 		btnDelete.setDisable(true);
+		scores.setRowFactory(tableView -> {
+			TableRow<Score> row = new TableRow<>();
+			row.setOnMouseClicked(event -> {
+				if (event.getClickCount() >= 2 && event.getButton() == MouseButton.PRIMARY && row.getItem() != null) {
+					try {
+						MainScreenController.this.app.createDialogStage(row.getItem()).show();
+					} catch (IOException e) {
+						e.printStackTrace();
+					}
+				}
+			});
+			return row;
+		});
 		scores.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
 		scores.getSelectionModel().getSelectedItems()
-				.addListener((ListChangeListener.Change<? extends Score> change) -> 
-					btnDelete.setDisable(change.getList().isEmpty()));
+				.addListener((ListChangeListener.Change<? extends Score> change) -> btnDelete
+						.setDisable(change.getList().isEmpty()));
 
 		initStorage();
 		log.info("Screeen initialized.");
diff --git a/src/main/java/lab/storage/FileStorage.java b/src/main/java/lab/storage/FileStorage.java
index 828ca06..71f3c3e 100644
--- a/src/main/java/lab/storage/FileStorage.java
+++ b/src/main/java/lab/storage/FileStorage.java
@@ -10,6 +10,7 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.stream.Stream;
 
+import lab.data.Level;
 import lab.data.Score;
 
 public class FileStorage implements ScoreStorageInterface {
@@ -21,7 +22,7 @@ public class FileStorage implements ScoreStorageInterface {
 		if (Files.exists(Paths.get(SCORE_FILE_NAME))) {
 			try (Stream<String> lines = Files.lines(Paths.get(SCORE_FILE_NAME))) {
 				List<Score> result = lines.map(line -> line.split(";"))
-						.map(parts -> new Score(null, parts[0], Integer.parseInt(parts[1]), null)).toList();
+						.map(parts -> new Score(null, parts[0], Integer.parseInt(parts[1]), Level.from(parts[2]))).toList();
 				return new ArrayList<>(result);
 			} catch (IOException e) {
 				e.printStackTrace();
@@ -34,7 +35,7 @@ public class FileStorage implements ScoreStorageInterface {
 	public List<Score> getFirstTen() {
 		List<Score> all = getAll();
 		Collections.sort(all, Comparator.comparing(Score::getPoints).reversed());
-		return all.subList(0, 10);
+		return all.subList(0, Math.min(all.size(), 10));
 	}
 
 	@Override
@@ -51,9 +52,9 @@ public class FileStorage implements ScoreStorageInterface {
 	}
 
 	private void storeAll(List<Score> all) {
-		List<String> lines = all.stream().map(s -> String.format("%s;%d", s.getName(), s.getPoints())).toList();
+		List<String> lines = all.stream().map(s -> String.format("%s;%d;%s", s.getName(), s.getPoints(), s.getLevel())).toList();
 		try {
-			Files.write(Paths.get(SCORE_FILE_NAME), lines, StandardOpenOption.TRUNCATE_EXISTING);
+			Files.write(Paths.get(SCORE_FILE_NAME), lines, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
 		} catch (IOException e) {
 			e.printStackTrace();
 		}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index abec138..4839703 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -8,6 +8,7 @@ module lab05_module {
 	requires com.h2database;
 	requires jakarta.persistence;
 	requires org.hibernate.orm.core;
+	requires java.desktop;
 	
 	opens lab.gui to javafx.fxml;
 	opens lab.data to javafx.base,org.hibernate.orm.core;
diff --git a/src/main/resources/lab/gui/application.css b/src/main/resources/lab/gui/application.css
index 0ef5f16..7f0e980 100644
--- a/src/main/resources/lab/gui/application.css
+++ b/src/main/resources/lab/gui/application.css
@@ -26,6 +26,22 @@ Label {
 	-fx-background-size: stretch;
 }
 
+#content {
+	-fx-grid-lines-visible: true;
+}
+
+#btnOk {
+	-fx-background-color: RGBA(0.0,255.0,0.0,0.5);
+}
+
+#btnCancel {
+	-fx-background-color: RGBA(150.0,150.0,0.0,0.5);
+}
+
+.actionButton:hover {
+	-fx-text-fill: white;
+}
+
 #playButton {
 	-fx-background-color: RGBA(0.0,0.0,255.0,0.5);
 }
diff --git a/src/main/resources/lab/gui/edit.fxml b/src/main/resources/lab/gui/edit.fxml
new file mode 100644
index 0000000..b80cc5b
--- /dev/null
+++ b/src/main/resources/lab/gui/edit.fxml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.TextField?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.ColumnConstraints?>
+<?import javafx.scene.layout.GridPane?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.RowConstraints?>
+<?import javafx.scene.text.Font?>
+
+<BorderPane fx:id="menuPanel" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"  xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="lab.gui.EditController">
+   <bottom>
+      <HBox BorderPane.alignment="CENTER">
+         <children>
+            <Button fx:id="btnCancel" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#btnCancelAction" styleClass="actionButton" text="Cancle" HBox.hgrow="ALWAYS">
+               <font>
+                  <Font name="System Bold" size="51.0" />
+               </font>
+            </Button>
+            <Button fx:id="btnOk" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#btnOkAction" styleClass="actionButton" text="Ok" HBox.hgrow="ALWAYS">
+               <font>
+                  <Font name="System Bold" size="51.0" />
+               </font>
+            </Button>
+         </children>
+      </HBox>
+   </bottom>
+   <center>
+      <GridPane fx:id="content" BorderPane.alignment="CENTER">
+         <columnConstraints>
+            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
+            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
+         </columnConstraints>
+         <rowConstraints>
+            <RowConstraints minHeight="10.0" vgrow="NEVER" />
+         </rowConstraints>
+      </GridPane>
+   </center>
+   <top>
+      <Label fx:id="txtTitle" text="Title" BorderPane.alignment="CENTER">
+         <font>
+            <Font name="System Bold" size="48.0" />
+         </font>
+      </Label>
+   </top>
+</BorderPane>
-- 
GitLab