GUIAdapter.java

package it.fulminazzo.yagl;

import it.fulminazzo.yagl.contents.GUIContent;
import it.fulminazzo.yagl.guis.GUI;
import it.fulminazzo.yagl.guis.GUIType;
import it.fulminazzo.yagl.guis.TypeGUI;
import it.fulminazzo.yagl.items.BukkitItem;
import it.fulminazzo.yagl.utils.MessageUtils;
import it.fulminazzo.yagl.viewers.PlayerOfflineException;
import it.fulminazzo.yagl.viewers.Viewer;
import it.fulminazzo.fulmicollection.objects.Refl;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.UUID;
import java.util.function.Consumer;

/**
 * A collection of utilities for handling with {@link GUI}s and Bukkit.
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class GUIAdapter {

    /**
     * Opens the given {@link GUI} for the specified {@link Viewer}.
     *
     * @param gui    the gui
     * @param viewer the viewer
     */
    public static void openGUI(final @NotNull GUI gui, @NotNull Viewer viewer) {
        openGUI(gui, viewer, null, null);
    }


    /**
     * Opens the given {@link GUI} for the specified {@link Viewer}.
     * Uses the given {@link ItemMeta} function to {@link BukkitItem#create(Class, Consumer)} the contents.
     *
     * @param gui          the gui
     * @param viewer       the viewer
     * @param metaFunction the meta function
     */
    public static void openGUI(final @NotNull GUI gui, @NotNull Viewer viewer, final @NotNull Consumer<ItemMeta> metaFunction) {
        openGUI(gui, viewer, ItemMeta.class, metaFunction);
    }

    /**
     * Opens the given {@link GUI} for the specified {@link Viewer}.
     * Uses the given {@link ItemMeta} class and function to {@link BukkitItem#create(Class, Consumer)} the contents.
     *
     * @param <M>           the type parameter
     * @param gui           the gui
     * @param viewer        the viewer
     * @param itemMetaClass the ItemMeta class
     * @param metaFunction  the meta function
     */
    public static <M extends ItemMeta> void openGUI(final @NotNull GUI gui, final @NotNull Viewer viewer,
                                                    final @Nullable Class<M> itemMetaClass, final @Nullable Consumer<M> metaFunction) {
        Consumer<Viewer> runnable = v -> {
            final UUID uuid = v.getUniqueId();
            final Player player = Bukkit.getPlayer(uuid);
            if (player == null) throw new PlayerOfflineException(v.getName());
            final Refl<Viewer> reflViewer = new Refl<>(v);
            // Add to GUIManager if not present
            v = GUIManager.getViewer(player);
            // Save previous GUI, if present
            GUIManager.getOpenGUIViewer(uuid).ifPresent((vi, g) -> {
                reflViewer.setFieldObject("previousGUI", g).setFieldObject("openGUI", null);
                g.changeGUIAction().ifPresent(a -> a.execute(vi, g, gui));
            });
            // Set global variables
            for (final @NotNull BukkitVariable variable : BukkitVariable.DEFAULT_VARIABLES)
                gui.setVariable(variable.getName(), variable.getValue(player));
            // Open inventory
            Inventory inventory = guiToInventory(gui.apply(gui));
            for (int i = 0; i < gui.size(); i++) {
                GUIContent content = gui.getContent(v, i);
                if (content != null) {
                    ItemStack o = content.copy().copyFrom(gui, false)
                            .render()
                            .copy(BukkitItem.class)
                            .create(itemMetaClass, metaFunction);
                    inventory.setItem(i, o);
                }
            }
            player.openInventory(inventory);
            // Set new GUI
            reflViewer.setFieldObject("openGUI", gui);
            // Execute action if present
            gui.openGUIAction().ifPresent(a -> a.execute(reflViewer.getObject(), gui));
        };
        // Check if context is Async and synchronize
        if (Bukkit.isPrimaryThread()) runnable.accept(viewer);
        else Bukkit.getScheduler().runTask(JavaPlugin.getProvidingPlugin(GUIAdapter.class), () -> runnable.accept(viewer));
    }

    /**
     * Closes the currently open {@link GUI} for the specified {@link Viewer}, if present.
     *
     * @param viewer the viewer
     */
    public static void closeGUI(final @NotNull Viewer viewer) {
        final UUID uuid = viewer.getUniqueId();
        final Player player = Bukkit.getPlayer(uuid);
        if (player == null) throw new PlayerOfflineException(viewer.getName());
        final Refl<Viewer> reflViewer = new Refl<>(viewer);
        // Save previous GUI, if present
        GUIManager.getOpenGUIViewer(uuid).ifPresent((v, g) -> {
            reflViewer.setFieldObject("previousGUI", g).setFieldObject("openGUI", null);
            g.closeGUIAction().ifPresent(a -> a.execute(v, g));
            player.closeInventory();
        });
    }

    /**
     * Converts the given {@link GUI} to a {@link Inventory}.
     *
     * @param gui the gui
     * @return the inventory
     */
    public static Inventory guiToInventory(final @NotNull GUI gui) {
        String title = MessageUtils.color(gui.getTitle());
        final Inventory inventory;
        if (title == null) {
            if (gui instanceof TypeGUI) {
                InventoryType type = guiToInventoryType(((TypeGUI) gui).getInventoryType());
                inventory = Bukkit.createInventory(null, type);
            } else inventory = Bukkit.createInventory(null, gui.size());
        } else {
            if (gui instanceof TypeGUI) {
                InventoryType type = guiToInventoryType(((TypeGUI) gui).getInventoryType());
                inventory = Bukkit.createInventory(null, type, title);
            } else inventory = Bukkit.createInventory(null, gui.size(), title);
        }
        return inventory;
    }

    /**
     * Converts the given {@link GUIType} to a {@link InventoryType}.
     *
     * @param guiType the gui type
     * @return the inventory type
     */
    public static InventoryType guiToInventoryType(final @NotNull GUIType guiType) {
        return InventoryType.valueOf(guiType.name());
    }
}