SearchedItemContainer.java

package org.troy.capstone.ui_components.items.searched;

import java.util.ArrayList;
import java.util.List;

import org.troy.capstone.constants.UISizeControl;
import org.troy.capstone.constants.TestFXId;
import org.troy.capstone.entities.Item;
import org.troy.capstone.interfaces.SearchedItemPanelInteractor;
import org.troy.capstone.ui_components.items.SearchedItemPanel;
import org.troy.capstone.utils.UIUtils;

import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;

/**
 * The {@code SearchedItemContainer} class represents a UI component that contains and displays the search results as a list of {@code SearchedItemPanel} instances. It is a scrollable container that allows users to view all search results, and it provides methods to add new search result panels to the container.
 */
public class SearchedItemContainer extends ScrollPane {
    /** The container for all searched item panels */
    private final VBox itemContainer;

    /** List of interactors to handle interactions with the item panels in the search results. */
    private final List<SearchedItemPanelInteractor> interactors = new ArrayList<>();

    /**
     * Creates a {@code SearchedItemContainer} with a vertical box layout for displaying search result panels.
     */
    private SearchedItemContainer() {
        super();

        itemContainer = new VBox(UISizeControl.SEARCHED_ITEM_PANEL_SPACING.getValue());
        itemContainer.setAlignment(Pos.TOP_CENTER);
        setContent(itemContainer);
        setFitToWidth(true);

        //Optimize scroll performance
        setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
        setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        setPannable(false);
        
        //Cache nodes to improve scroll performance
        itemContainer.setCache(true);
        itemContainer.setCacheHint(javafx.scene.CacheHint.SPEED);

        itemContainer.setId(TestFXId.SEARCHED_ITEM_CONTAINER_CONTAINER.getId());

    }

    /** Factory method to create a {@code SearchedItemContainer} with the given list of items.
     * @param items The list of items to display in the container.
     * @return A new instance of {@code SearchedItemContainer} populated with the given items.
     */
    public static SearchedItemContainer create(List<Item> items) {
        SearchedItemContainer container = new SearchedItemContainer();
        UIUtils.setSize(container, UISizeControl.SEARCHED_ITEM_CONTAINER_WIDTH.getValue(), UISizeControl.SEARCHED_ITEM_CONTAINER_HEIGHT.getValue());
        UIUtils.setLineBorder(container, 5, 1);
        container.updateItems(items);
        return container;
    }

    /**
     * Adds a new {@code SearchedItemPanel} to the container.
     * 
     * @pre itemPanel is not null and is properly initialized with the data to display for a search result.
     * 
     * @post If itemPanel is not null, it is added to the itemContainer and becomes visible in the UI.
     * @param itemPanel The {@code SearchedItemPanel} to add.
     */ 
    private void addItemPanel(SearchedItemPanel itemPanel) {
        if( itemPanel != null ){
            itemContainer.getChildren().add(itemPanel);
            interactors.forEach(interactor -> itemPanel.addSearchedItemPanelInteractor(interactor));
        }
    }

    /** Adds a {@code SearchedItemPanelInteractor} to the {@code SearchedItemContainer} to allow for interaction with the item panels in the search results.
     * @pre interactor should be properly implemented to handle interactions with the item panels, and the {@code SearchedItemContainer} should be properly initialized to allow for adding interactors.
     * @post The provided interactor is added to the {@code SearchedItemContainer}, allowing it to receive interaction events from the item panels in the search results.
     * @param interactor The {@code SearchedItemPanelInteractor} to add to the {@code SearchedItemContainer} for handling interactions with the item panels in the search results.
     */
    public void addSearchedItemPanelInteractor(SearchedItemPanelInteractor interactor) {
        interactors.add(interactor);
        //Set interactors for initial panels
        itemContainer.getChildren().stream().filter(node -> node instanceof SearchedItemPanel)
            .forEach(node -> ((SearchedItemPanel) node).addSearchedItemPanelInteractor(interactor));
    }

    /** Updates the items displayed in the container with a new list of items.
     * @pre items should be a valid list of {@code Item} objects to display in the container. The {@code SearchedItemContainer} should be properly initialized to allow for updating the displayed items.
     * @post The {@code itemContainer} is cleared and repopulated with new {@code SearchedItemPanel} instances corresponding to the provided list of items. If the list is null or empty, a message indicating that no items were found is displayed instead.
     * @param items The new list of {@code Item} objects to display in the container.
     */
    public final void updateItems(List<Item> items) {
        if( items == null ){
            System.out.println("Warning: updateItems called with null list. Doing nothing.");
        }else if (items.isEmpty()) {
            stopAllImagesLoading();
            itemContainer.getChildren().clear();
            itemContainer.getChildren().add(new Label("No items found."));
        }else{
            stopAllImagesLoading();
            itemContainer.getChildren().clear();
            items.forEach(item -> {
                if (item != null)
                    addItemPanel(SearchedItemPanel.create(item));
            });
        }
    }

    /**
     * Stops all image loading tasks from running
     * 
     * @post All asynchronous image loading tasks for the {@code SearchedItemPanel} instances currently displayed in the container are stopped, preventing any further loading of images that are not being displayed.
     */
    public void stopAllImagesLoading() {
        itemContainer.getChildren().stream().filter(node -> node instanceof SearchedItemPanel)
            .forEach(node -> ((SearchedItemPanel) node).stopLoadingImage());
    }

}