All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.sanctum.labyrinth.gui.unity.simple.MemoryDocket Maven / Gradle / Ivy

The newest version!
package com.github.sanctum.labyrinth.gui.unity.simple;

import com.github.sanctum.labyrinth.LabyrinthProvider;
import com.github.sanctum.labyrinth.data.service.PlayerSearch;
import com.github.sanctum.labyrinth.formatting.string.FormattedString;
import com.github.sanctum.labyrinth.gui.unity.construct.Menu;
import com.github.sanctum.labyrinth.gui.unity.construct.MenuRegistration;
import com.github.sanctum.labyrinth.gui.unity.construct.PaginatedMenu;
import com.github.sanctum.labyrinth.gui.unity.construct.PrintableMenu;
import com.github.sanctum.labyrinth.gui.unity.construct.SingularMenu;
import com.github.sanctum.labyrinth.gui.unity.impl.BorderElement;
import com.github.sanctum.labyrinth.gui.unity.impl.FillerElement;
import com.github.sanctum.labyrinth.gui.unity.impl.InventoryElement;
import com.github.sanctum.labyrinth.gui.unity.impl.ItemElement;
import com.github.sanctum.labyrinth.gui.unity.impl.ListElement;
import com.github.sanctum.labyrinth.gui.unity.impl.MenuType;
import com.github.sanctum.labyrinth.library.Mailer;
import com.github.sanctum.panther.annotation.Comment;
import com.github.sanctum.panther.annotation.Note;
import com.github.sanctum.panther.annotation.Voluntary;
import com.github.sanctum.panther.container.PantherCollection;
import com.github.sanctum.panther.container.PantherList;
import com.github.sanctum.panther.file.MemorySpace;
import com.github.sanctum.panther.file.Node;
import com.github.sanctum.panther.util.Check;
import com.github.sanctum.panther.util.PantherLogger;
import com.github.sanctum.skulls.CustomHead;
import com.github.sanctum.skulls.SkullType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;

/**
 * A type of unity docket that conforms written gui structure into a usable menu instance.
 *
 * @param  The type of docket.
 */
public class MemoryDocket implements Docket, UniqueHolder {

	protected final PantherCollection> items = new PantherList<>();
	protected Plugin plugin = LabyrinthProvider.getInstance().getPluginInstance();
	protected MemoryItem pagination, next, previous, exit, filler, border;
	protected BiFunction dataConverter = (s, t) -> s;
	protected BiFunction uniqueDataConverter = (s, t) -> s;
	protected final MemorySpace memory;
	protected Supplier> supplier;
	protected Comparator comparator;
	protected Predicate predicate;
	protected Object uniqueData;
	protected boolean shared;
	protected String title;
	protected String key;
	protected String nameHolder;
	protected Menu.Type type;
	protected Menu.Rows rows;
	protected Menu instance;

	public MemoryDocket(MemorySpace memorySpace) {
		this.memory = memorySpace;
	}

	@Voluntary("Used only in tandem with pagination.")
	public MemoryDocket setList(@NotNull Supplier> supplier) {
		this.supplier = supplier;
		return this;
	}

	@Voluntary("Used only in tandem with pagination.")
	public MemoryDocket setComparator(Comparator comparator) {
		this.comparator = comparator;
		return this;
	}

	@Voluntary("Used only in tandem with pagination.")
	public MemoryDocket setFilter(Predicate predicate) {
		this.predicate = predicate;
		return this;
	}

	@Override
	@Note("This method is used for translating player names for skull items, it is expected to be the placeholder for returning a player username and is used in tandem with a Unique Data Converter")
	public MemoryDocket setNamePlaceholder(@NotNull String placeholder) {
		this.nameHolder = placeholder;
		return this;
	}

	@Voluntary("This method allows you to setup custom placeholders, used only in tandem with pagination.")
	public MemoryDocket setDataConverter(@NotNull BiFunction function) {
		this.dataConverter = function;
		return this;
	}

	@Override
	@Note("This method is used for setting up unique translations. Example; a singular parent object being attached for extra placeholders.")
	public  MemoryDocket setUniqueDataConverter(@NotNull V t, @NotNull BiFunction function) {
		this.uniqueData = t;
		this.uniqueDataConverter = (BiFunction) function;
		return this;
	}

	@Override
	public @NotNull MemoryDocket load() {
		this.title = Check.forNull(memory.getNode("title").toPrimitive().getString(), "Configured menus cannot have null titles please correct under path '" + memory.getPath() + "'");
		if (this.uniqueData != null) {
			this.title = uniqueDataConverter.apply(title, uniqueData);
		}
		this.rows = Check.forNull(Menu.Rows.valueOf(memory.getNode("rows").toPrimitive().getString()), "Configured menus need a row size please correct under path '" + memory.getPath() + "'");
		this.type = Check.forNull(Menu.Type.valueOf(memory.getNode("type").toPrimitive().getString()), "Configured menus need a valid type please correct under path '" + memory.getPath() + "'");
		this.shared = memory.getNode("shared").toPrimitive().getBoolean();
		if (memory.getNode("id").toPrimitive().isString()) {
			if (this.uniqueData != null) {
				this.key = uniqueDataConverter.apply(memory.getNode("id").toPrimitive().getString(), uniqueData);
			} else {
				this.key = memory.getNode("id").toPrimitive().getString();
			}
		}
		if (memory.isNode("filler")) {
			this.filler = new MemoryItem(memory.getNode("filler"));
		}
		if (memory.isNode("border")) {
			this.border = new MemoryItem(memory.getNode("border"));
		}
		if (memory.isNode("pagination")) {
			pagination = new MemoryItem(memory.getNode("pagination"));
			if (memory.getNode("pagination").isNode("navigation")) {
				Node parent = memory.getNode("pagination").getNode("navigation");
				next = new MemoryItem(parent.getNode("next"));
				previous = new MemoryItem(parent.getNode("previous"));
				if (parent.isNode("exit")) {
					exit = new MemoryItem(parent.getNode("exit"));
				}
			}
		}
		if (memory.isNode("items")) {
			for (String item : memory.getNode("items").getKeys(false)) {
				MemoryItem i = new MemoryItem(memory.getNode("items").getNode(item));
				ItemStack result = i.toItem();
				ItemElement element = new ItemElement<>();
				element.setElement(result);
				handlePlayerHeadLookup(true, result, element);
				if (i.getSlot() > -1) {
					element.setSlot(i.getSlot());
				}
				handleClickEvent(i, element);
				if (element.getElement().hasItemMeta() && element.getElement().getItemMeta().hasLore()) {
					List lore = new ArrayList<>();
					for (String s : element.getElement().getItemMeta().getLore()) {
						String res = s;
						if (uniqueData != null) {
							res = uniqueDataConverter.apply(res, uniqueData);
						}
						lore.add(res);
					}
					element.setElement(edit -> edit.setLore(lore).build());
				}
				String res = element.getName();
				if (uniqueData != null ) {
					res = uniqueDataConverter.apply(res, uniqueData);
				}
				String finalRes = res;
				element.setElement(edit -> edit.setTitle(finalRes).build());
				items.add(element);
			}
		}
		switch (type) {
			case PAGINATED:
				Menu.Builder paginated = MenuType.PAGINATED.build().setHost(plugin).setTitle(title).setSize(rows);
				if (key != null) paginated.setKey(key).setProperty(Menu.Property.CACHEABLE);
				if (shared) paginated.setProperty(Menu.Property.SHAREABLE);
				paginated.setStock(i -> {
					items.forEach(i::addItem);
					ListElement element = new ListElement<>(supplier);
					if (this.comparator != null) {
						element.setComparator((o1, o2) -> comparator.compare(o1.getData().orElse(null), o2.getData().orElse(null)));
					}
					if (this.predicate != null) {
						element.setFilter(tItemElement -> predicate.test(tItemElement.getData().orElse(null)));
					}
					if (this.border != null) {
						BorderElement border = new BorderElement<>(i);
						for (Menu.Panel p : Menu.Panel.values()) {
							if (p == Menu.Panel.MIDDLE) continue;
							border.add(p, ed -> {
								ItemStack built = this.border.toItem();
								ed.setElement(built);
								handleClickEvent(this.border, ed);
								ed.setType(ItemElement.ControlType.ITEM_BORDER);
							});
						}
						i.addItem(border);
					}
					if (this.filler != null) {
						FillerElement filler = new FillerElement<>(i);
						filler.add(ed -> {
							ItemStack built = this.filler.toItem();
							ed.setElement(built);
							handleClickEvent(this.filler, ed);
							ed.setType(ItemElement.ControlType.ITEM_FILLER);
						});
						i.addItem(filler);
					}

					final ItemStack built = pagination.toItem();
					element.setLimit(pagination.getLimit());
					element.setPopulate((value, item) -> {
						item.setElement(built);
						handlePlayerHeadLookup(false, built, item, value);
						String title = item.getName();
						if (pagination != null) {
							if (pagination.isNotRemovable()) {
								item.setClick(click -> {
									click.setCancelled(true);
									if (pagination.isExitOnClick()) click.getParent().getParent().getParent().close(click.getElement());
									if (pagination.getMessage() != null) {
										String res = handlePaginationReplacements(pagination, pagination.getMessage(), value);
										if (dataConverter != null) {
											res = dataConverter.apply(res, value);
										}
										Mailer.empty(click.getElement()).chat(res).deploy();
									}
									if (pagination.getOpenOnClick() != null) {
										String open = pagination.getOpenOnClick();
										String r = handlePaginationReplacements(pagination, open, value);
										if (dataConverter != null) {
											r = dataConverter.apply(r, value);
										}
										MenuRegistration registration = MenuRegistration.getInstance();
										Menu registered = registration.get(r).deploy().get();
										if (registered != null) {
											registered.open(click.getElement());
										} else {
											if (pagination.getOpenOnClick().startsWith("/")) {
												String command = pagination.getOpenOnClick().replace("/", "");
												String res = handlePaginationReplacements(pagination, command, value);
												if (dataConverter != null) {
													res = dataConverter.apply(res, value);
												}
												click.getElement().performCommand(res);
											}
										}
									}
								});
							}
							if (pagination.getReplacements() != null) {
								if (item.getElement().hasItemMeta() && item.getElement().getItemMeta().hasLore()) {
									List lore = new ArrayList<>();
									for (String s : item.getElement().getItemMeta().getLore()) {
										String res = handlePaginationReplacements(pagination, s, value);
										if (dataConverter != null) {
											res = dataConverter.apply(res, value);
										}
										lore.add(res);
									}
									item.setElement(edit -> edit.setLore(lore).build());
								}
								String res = handlePaginationReplacements(pagination, title, value);
								if (dataConverter != null ) {
									res = dataConverter.apply(res, value);
								}
								String finalRes = res;
								item.setElement(edit -> edit.setTitle(finalRes).build());
							}
						}
					});
					i.addItem(element);
					if (!Check.isNull(next, previous)) {
						i.addItem(b -> {
									b.setElement(it -> it.setItem(next.toItem()).build()).setType(ItemElement.ControlType.BUTTON_NEXT).setSlot(next.getSlot());
									handleClickEvent(next, b);
								})
								.addItem(b -> {
									b.setElement(it -> it.setItem(previous.toItem()).build()).setType(ItemElement.ControlType.BUTTON_BACK).setSlot(previous.getSlot());
									handleClickEvent(previous, b);
								});
					}
					if (exit != null) {
						i.addItem(b -> {
							b.setElement(it -> it.setItem(exit.toItem()).build()).setType(ItemElement.ControlType.BUTTON_EXIT).setSlot(exit.getSlot());
							handleClickEvent(exit, b);
						});
					}
				});
				this.instance = paginated.join();
				break;
			case PRINTABLE:
				Menu.Builder printable = MenuType.PRINTABLE.build().setHost(plugin).setTitle(title).setSize(rows);
				if (key != null) printable.setKey(key).setProperty(Menu.Property.CACHEABLE);
				if (shared) printable.setProperty(Menu.Property.SHAREABLE);
				printable.setStock(i -> items.forEach(i::addItem));
				this.instance = printable.join();
				break;
			case SINGULAR:
				Menu.Builder singular = MenuType.SINGULAR.build().setHost(plugin).setTitle(title).setSize(rows);
				if (key != null) singular.setKey(key).setProperty(Menu.Property.CACHEABLE);
				if (shared) singular.setProperty(Menu.Property.SHAREABLE);
				singular.setProperty(Menu.Property.RECURSIVE);
				singular.setStock(i -> {
					items.forEach(i::addItem);
					if (this.border != null) {
						BorderElement border = new BorderElement<>(i);
						for (Menu.Panel p : Menu.Panel.values()) {
							if (p == Menu.Panel.MIDDLE) continue;
							border.add(p, ed -> {
								ItemStack built = this.border.toItem();
								ed.setElement(built);
								if (this.border != null) {
									handleClickEvent(this.border, ed);
								}
								ed.setType(ItemElement.ControlType.ITEM_BORDER);
							});
						}
						i.addItem(border);
					}
					if (this.filler != null) {
						FillerElement filler = new FillerElement<>(i);
						filler.add(ed -> {
							ItemStack built = this.filler.toItem();
							ed.setElement(built);
							if (this.filler != null) {
								handleClickEvent(this.filler, ed);
							}
							ed.setType(ItemElement.ControlType.ITEM_FILLER);
						});
						i.addItem(filler);
					}
				});
				this.instance = singular.join();
				break;
		}
		return this;
	}

	@Override
	public @NotNull Menu toMenu() {
		return instance;
	}

	protected void handleClickEvent(MemoryItem item, ItemElement ed) {
		if (item.isNotRemovable()) {
			ed.setClick(click -> {
				click.setCancelled(true);
				if (item.isExitOnClick()) click.getParent().getParent().getParent().close(click.getElement());
				if (item.getMessage() != null) {
					String message = item.getMessage();
					if (uniqueData != null) {
						message = uniqueDataConverter.apply(message, uniqueData);
					}
					Mailer.empty(click.getElement()).chat(message).deploy();
				}
				if (item.getOpenOnClick() != null) {
					String open = item.getOpenOnClick();
					if (uniqueData != null) {
						open = uniqueDataConverter.apply(open, uniqueData);
					}
					MenuRegistration registration = MenuRegistration.getInstance();
					Menu registered = registration.get(open).deploy().complete();
					if (registered != null) {
						registered.open(click.getElement());
					} else {
						if (item.getOpenOnClick().startsWith("/")) {
							String command = item.getOpenOnClick().replace("/", "");
							if (uniqueData != null) {
								command = uniqueDataConverter.apply(command, uniqueData);
							}
							click.getElement().performCommand(command);
						}
					}
				}
			});
		}
	}

	@Comment("Handle player head to user translations, local being a unique object instead of paginated.")
	protected void handlePlayerHeadLookup(boolean local, ItemStack built, ItemElement item, Object... args) {
		boolean pass = local ? !Check.isNull(uniqueData, uniqueDataConverter, nameHolder) : !Check.isNull(dataConverter, nameHolder);
		if (pass && new FormattedString(built.getType().name()).contains("player_head", "skull_item")) {
			String name = local ? uniqueDataConverter.apply(nameHolder, uniqueData) : dataConverter.apply(nameHolder, (T) args[0]);
			PlayerSearch search = PlayerSearch.of(name);
			if (search != null) {
				ItemStack head = CustomHead.Manager.get(search.getPlayer());
				item.setElement(edit -> edit.setItem(head != null ? head : SkullType.PLAYER.get()).build());
				if (built.hasItemMeta()) {
					if (built.getItemMeta().hasDisplayName()) {
						item.setElement(edit -> edit.setTitle(built.getItemMeta().getDisplayName()).build());
					}
					if (built.getItemMeta().hasLore()) {
						item.setElement(edit -> edit.setLore(built.getItemMeta().getLore()).build());
					}
				}
			}
		}
	}

	@Comment("Handle memory item placeholder translation on a string with a provided value")
	protected String handlePaginationReplacements(MemoryItem item, String context, T value) {
		final FormattedString string = new FormattedString(context);
		for (Map.Entry entry : item.getReplacements().entrySet()) {
			try {
				if (entry.getValue().contains(".")) {
					// Here we invoke method-ception, allow the jvm to point to each specified method result.
					String[] steps = entry.getValue().split("\\.");
					Method method = value.getClass().getMethod(steps[0]);
					Object step = method.invoke(value);
					int position = 1;
					do {
						method = step.getClass().getMethod(steps[position]);
						step = method.invoke(step);
						position++;
					} while (position != steps.length);
					String rep = step.toString();
					string.replace(entry.getKey(), rep);
				} else {
					Method m = value.getClass().getMethod(entry.getValue());
					String rep = m.invoke(value).toString();
					string.replace(entry.getKey(), rep);
				}
			} catch (Exception ex) {
				plugin.getLogger().severe("Unable to resolve method name " + '"' + entry.getValue() + '"' + " from class " + value.getClass().getSimpleName() + " in menu " + '"' + this.title + '"');
			}
		}
		return string.get();
	}

	@Override
	public @NotNull Type getType() {
		return Type.MEMORY;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy