Coverage Summary for Class: TotemFXMLController (it.polimi.ingsw.gc14.View.GUI)

Class Class, % Method, % Branch, % Line, %
TotemFXMLController 0% (0/1) 0% (0/16) 0% (0/24) 0% (0/90)


 package it.polimi.ingsw.gc14.View.GUI;
 
 import it.polimi.ingsw.gc14.Controller.ClientController;
 import it.polimi.ingsw.gc14.Model.Totems;
 import javafx.animation.*;
 import javafx.fxml.FXML;
 import javafx.geometry.Pos;
 import javafx.geometry.Rectangle2D;
 import javafx.scene.Cursor;
 import javafx.scene.control.Button;
 import javafx.scene.control.Label;
 import javafx.scene.image.Image;
 import javafx.scene.image.ImageView;
 import javafx.scene.layout.*;
 import javafx.stage.Screen;
 import javafx.util.Duration;
 
 import java.util.Locale;
 import java.util.Objects;
 
 /**
  * FXML controller for the totem selection scene.
  *
  * <p>Displays the available totems and lets the current player choose one;
  * other players see a waiting banner.
  */
 public class TotemFXMLController {
     /** Background image displayed behind the totem selection grid. */
     @FXML private ImageView backgroundImage;
     /** HBox containing the totem card widgets. */
     @FXML private HBox mainHBox;
     /** Banner label showing whose turn it is to choose a totem. */
     @FXML private Label turnBanner;
     /** Button that submits the selected totem to the server. */
     @FXML private Button confirmButton;
 
     /** Client controller for sending the totem choice. */
     private ClientController controller;
     /** Index of the currently selected totem, or {@code -1} if none selected. */
     private int selectedIndex = -1;
     /** The {@link StackPane} frame of the currently selected totem card, or {@code null}. */
     private StackPane selectedFrame = null;
 
     /**
      * Injects the client controller into this FXML controller.
      *
      * @param controller the client controller to use.
      */
     public void setController(ClientController controller) {
         this.controller = controller;
     }
 
     /** Initializes the scene: sets the background image to fill the screen. */
     @FXML
     public void initialize() {
         Image img = new Image(Objects.requireNonNull(getClass().getResourceAsStream("/GUIImages/Background.png")));
         backgroundImage.setImage(img);
         Rectangle2D screenBounds = Screen.getPrimary().getBounds();
         backgroundImage.setFitWidth(screenBounds.getWidth());
         backgroundImage.setFitHeight(screenBounds.getHeight());
     }
 
     /**
      * Renders the totem selection cards with entry animations.
      * Disables interaction for players who are not the current chooser.
      */
     public void render() {
         mainHBox.getChildren().clear();
         selectedIndex = -1;
         selectedFrame = null;
 
         if (controller.getMiniModel().currentState.getCurrentPlayer() == null) return;
         String chooser = controller.getMiniModel().currentState.getCurrentPlayer().getUserName();
         boolean myTurn = chooser.equals(controller.getMyUsername());
 
         updateBanner(chooser, myTurn);
         updateConfirmButton(false);
 
         for (int i = 0; i < controller.getMiniModel().availableTotems.size(); i++) {
             Totems totem = controller.getMiniModel().availableTotems.get(i);
             VBox card = buildCard(totem, i, myTurn);
             mainHBox.getChildren().add(card);
 
             card.setOpacity(0);
             card.setTranslateY(14);
             PauseTransition delay = new PauseTransition(Duration.millis(80 + i * 65));
             delay.setOnFinished(e -> {
                 FadeTransition ft = new FadeTransition(Duration.millis(320), card);
                 ft.setToValue(1);
                 TranslateTransition tt = new TranslateTransition(Duration.millis(320), card);
                 tt.setToY(0);
                 new ParallelTransition(ft, tt).play();
             });
             delay.play();
         }
     }
 
     /** Updates the turn banner text and style based on whether it is the local player's turn. */
     private void updateBanner(String chooser, boolean myTurn) {
         if (myTurn) {
             turnBanner.setText("✦   It's your turn to choose   ✦");
             turnBanner.setStyle(
                     "-fx-font-family: 'Cinzel'; -fx-font-size: 13; -fx-font-weight: 'bold'; -fx-letter-spacing: 3;" +
                             "-fx-text-fill: #000000;" +
                             "-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
                             "-fx-border-color: #000000; -fx-border-width: 1;" +
                             "-fx-border-radius: 3; -fx-background-radius: 3;" +
                             "-fx-padding: 10 28 10 28;" +
                             "-fx-effect: dropshadow(gaussian, rgba(244,192,90,0.3), 18, 0, 0, 0);"
             );
         } else {
             turnBanner.setText("Waiting for " + chooser + " to choose his totem…");
             turnBanner.setStyle(
                     "-fx-font-family: 'Cinzel'; -fx-font-size: 11; -fx-letter-spacing: 2;" +
                             "-fx-text-fill:  #000000;" +
                             "-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
                             "-fx-border-color:  #000000; -fx-border-width: 1;" +
                             "-fx-border-radius: 3; -fx-background-radius: 3;" +
                             "-fx-padding: 10 28 10 28;"
             );
         }
     }
 
     /** Builds a totem card widget; if {@code interactive}, wires click/hover handlers and selection logic. */
     private VBox buildCard(Totems totem, int idx, boolean interactive) {
         ImageView img = new ImageView(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/GUIImages/Totems/totem_" + String.valueOf(totem).toLowerCase(Locale.ROOT) + ".png"))));
         img.setFitHeight(150);
         img.setPreserveRatio(true);
 
         StackPane frame = new StackPane(img);
         frame.setPrefSize(100, 145);
         frame.setBackground(Background.EMPTY);
         img.setStyle(frameStyle(false));
 
         Region base = new Region();
         base.setPrefSize(100, 10);
 
         Label name = new Label(capitalize(totem));
         name.setStyle(nameStyle(false));
 
         VBox card = new VBox(4, frame, base, name);
         card.setAlignment(Pos.CENTER);
 
         if (!interactive) {
             card.setOpacity(0.35);
             card.setCursor(Cursor.DEFAULT);
             return card;
         }
 
         card.setCursor(Cursor.HAND);
 
         card.setOnMouseEntered(e -> {
             if (frame != selectedFrame) {
                 ScaleTransition st = new ScaleTransition(Duration.millis(140), card);
                 st.setToX(1.06); st.setToY(1.06); st.play();
             }
         });
         card.setOnMouseExited(e -> {
             if (frame != selectedFrame) {
                 ScaleTransition st = new ScaleTransition(Duration.millis(140), card);
                 st.setToX(1.0); st.setToY(1.0); st.play();
             }
         });
 
         card.setOnMouseClicked(e -> {
             mainHBox.getChildren().forEach(n -> {
                 if (n instanceof VBox v) {
                     StackPane f = (StackPane) v.getChildren().get(0);
                     Label l = (Label) v.getChildren().get(2);
                     f.setStyle(frameStyle(false));
                     l.setStyle(nameStyle(false));
                     ScaleTransition st = new ScaleTransition(Duration.millis(130), v);
                     st.setToX(1.0); st.setToY(1.0); st.play();
                 }
             });
 
             frame.setStyle(frameStyle(true));
             name.setStyle(nameStyle(true));
             selectedFrame = frame;
             selectedIndex = idx;
 
             ScaleTransition pop = new ScaleTransition(Duration.millis(160), card);
             pop.setToX(1.08); pop.setToY(1.08);
             pop.setAutoReverse(true); pop.setCycleCount(2); pop.play();
 
             updateConfirmButton(true);
         });
 
         return card;
     }
 
     /** Enables or disables the confirm button and updates its visual opacity to reflect the state. */
     private void updateConfirmButton(boolean enabled) {
         confirmButton.setDisable(!enabled);
         confirmButton.setStyle(
                 "-fx-font-family: 'Cinzel'; -fx-font-size: 12; -fx-font-weight: bold;" +
                         "-fx-letter-spacing: 4; -fx-text-fill: #0c0601;" +
                         "-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
                         "-fx-padding: 12 48 12 48; -fx-background-radius: 2;" +
                         "-fx-opacity: " + (enabled ? "1.0" : "0.3") + ";" +
                         "-fx-cursor: " + (enabled ? "hand" : "default") + ";"
         );
     }
 
     /** Returns the CSS style string for the totem card frame, highlighting it when {@code selected}. */
     private String frameStyle(boolean selected) {
         return selected ? "-fx-effect: dropshadow(gaussian, #ffffff, 20, 0.33, 0, 0);" : "";
     }
 
     /** Returns the CSS style string for the totem name label, brightening it when {@code selected}. */
     private String nameStyle(boolean selected) {
         return "-fx-font-family: 'Cinzel'; -fx-font-size: 10; -fx-letter-spacing: 2;" +
                 "-fx-text-fill: " + (selected ? "#FFD700" : "#ffffff") + ";";
     }
 
     /** Returns the totem name with only the first letter capitalized. */
     private String capitalize(Totems t) {
         String s = t.name().toLowerCase(Locale.ROOT);
         return Character.toUpperCase(s.charAt(0)) + s.substring(1);
     }
 
 
     /** Submits the selected totem index to the controller when the confirm button is clicked. */
     @FXML
     private void onConfirm() {
         if (selectedIndex >= 0) {
             controller.totemChoice(selectedIndex);
         }
     }
 }