SceneManager.java

package com.example.project.services;

import com.example.project.controllers.RootLayoutController;
import javafx.scene.Parent;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.example.project.controllers.gameScreens.GameScreenController;

/**
 * Represents the scene manager class.
 */
public class SceneManager
{
    private static RootLayoutController rootController;
    private static Map<GameScenes, Parent> pages = new HashMap<>();
    private static Map<GameScenes, GameScreenController> controllers = new HashMap<>();
    private static SceneManager instance;

    /**
     * @return Gets the programs Scene Manager instance.
     */
    public static SceneManager getInstance()
    {
        if (instance == null){
            instance = new SceneManager();
        }
        return instance;
    }

    private SceneManager() {}

    /**
     * Helper method for tests.
     * @param newInstance new Instance to inject.
     * @param newRootController root layout controller.
     * @param controllersToUse controllers to use.
     * @param pagesToUse pages to use.
     */
    public static void injectForTests(SceneManager newInstance, RootLayoutController newRootController, Map<GameScenes,
            GameScreenController> controllersToUse, Map<GameScenes, Parent> pagesToUse)
    {
        if (!Boolean.getBoolean("testenv")) {
            throw new IllegalStateException("resetForTests() must not be used in production!");
        }

        instance = newInstance;
        rootController = newRootController;
        controllers = controllersToUse;
        pages = pagesToUse;
    }

    /**
     * loads the fxml of all the scenes on launch to switch to via scene manaager.
     * @param theRootController the root Controller for the rootLayout scene.
     * @param loader FXMLLoader
     */
    public void initialise(RootLayoutController theRootController, PageLoader loader)
    {
        if (rootController != null)
        {
            throw new RuntimeException("Scene Manager already initialised in Application.start().");
        }

        rootController = theRootController;
        preloadPage(GameScenes.LOGIN, "/com/example/project/gameScreens/login-view.fxml", loader);
        preloadPage(GameScenes.LEVEL, "/com/example/project/gameScreens/level-view.fxml", loader);
        preloadPage(GameScenes.SHOP, "/com/example/project/gameScreens/shop-view.fxml", loader);
    }

    private void preloadPage(GameScenes type, String fxmlPath, PageLoader loader)
    {
        Parent page;
        try{
            page = loader.load(fxmlPath);
        }
        catch (IOException e){
            throw new RuntimeException(String.format("Exception when loading page: %s. Message: %s, cause: %s", fxmlPath, e.getMessage(), e.getCause()));
        }
        catch (IllegalStateException e){
            throw new RuntimeException(String.format("game scene fxml path for %s not correct: %s, cause: %s", fxmlPath, e.getMessage(), e.getCause()));
        }

        if (page == null)
        {
            throw new IllegalArgumentException("Page not loaded: " + type);
        }

        GameScreenController controller = loader.getController();
        if (controller == null){
            throw new RuntimeException("must have a controller on the game screen that is a gameScreenController");
        }

        controllers.put(type, controller);
        pages.put(type, page);
    }

    /**
     * Switch between scenes specifiy scene type of type from {@link GameScenes}.
     * @param type scene.
     */
    public void switchScene(GameScenes type)
    {
        Parent page = pages.get(type);
        GameScreenController controller = controllers.get(type);
        rootController.setContent(page);
        controller.onSceneChangedToThis();
    }
}