package tc.oc.commons.bukkit.listeners;
import java.util.UUID;
import javax.inject.Singleton;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import tc.oc.commons.bukkit.item.StringItemTag;
import tc.oc.commons.core.plugin.PluginFacet;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Generic service for binding individual {@link ItemStack}s to {@link ButtonListener}s,
* to receive generalized click events.
*
* ItemStacks are connected to listeners through a string ID stored in the item metadata.
* Any number of distinct stacks can be bound to a single listener.
*/
@Singleton
public class ButtonManager implements PluginFacet, Listener {
private static final StringItemTag TAG = new StringItemTag("ButtonId", null);
private final BiMap<String, ButtonListener> listeners = HashBiMap.create();
private String createId() {
return UUID.randomUUID().toString();
}
/**
* Register a {@link ButtonListener} under the given ID. If the listener
* is already registered under the given ID, nothing is changed.
* @return the given ID
* @throws IllegalArgumentException if the given ID is already registered with a different listener,
* or the given listener is already registered with a different ID.
*/
public String registerListener(String id, ButtonListener listener) {
final ButtonListener old = listeners.get(id);
checkArgument(old == null || old.equals(listener)); // Check for dupe ID (BiMap already checks for dupe listener)
listeners.put(id, listener);
return id;
}
/**
* Register the given {@link ButtonListener} with a generated unique ID, and return the ID.
* If the listener is already registered, it's current ID is returned.
*/
public String registerListener(ButtonListener listener) {
final String id = listeners.inverse().get(listener);
return id != null ? id : registerListener(createId(), listener);
}
/**
* Release all resources for the given listener ID
*/
public void unregisterListener(String id) {
listeners.remove(id);
}
/**
* Release all resources for the given listener
*/
public void unregisterListener(ButtonListener listener) {
listeners.inverse().remove(listener);
}
/**
* Create a button that is identical to the given {@link ItemStack},
* that notifies the {@link ButtonListener} registered with the given ID.
*
* The returned stack may or may not be the one given (it usually isn't).
*
* @throws IllegalArgumentException if no listener is registered with the given ID
*/
public ItemStack createButton(String listenerId, ItemStack button) {
checkArgument(listeners.containsKey(listenerId));
button = button.clone();
TAG.set(button, listenerId);
return button;
}
/**
* Create a button that is identical to the given {@link ItemStack},
* that notifies the given {@link ButtonListener}.
*
* The listener is registered if it is not already registered.
*
* The returned stack may or may not be the one given (it usually isn't).
*/
public ItemStack createButton(ButtonListener listener, ItemStack button) {
button = button.clone();
TAG.set(button, registerListener(listener));
return button;
}
private boolean onButtonClick(ItemStack button, Player clicker, ClickType click, Event event) {
if(!TAG.has(button)) return false;
final String id = TAG.get(button);
final ButtonListener listener = listeners.get(id);
if(listener == null) return false;
return listener.buttonClicked(button, clicker, click, event);
}
@EventHandler(priority = EventPriority.LOW)
public void onInteract(PlayerInteractEvent event) {
if(!event.hasItem()) return;
final ClickType click;
switch(event.getAction()) {
case LEFT_CLICK_AIR:
case LEFT_CLICK_BLOCK:
click = ClickType.LEFT;
break;
case RIGHT_CLICK_AIR:
case RIGHT_CLICK_BLOCK:
click = ClickType.RIGHT;
break;
default:
return;
}
if(onButtonClick(event.getItem(), event.getActor(), click, event)) {
event.setUseItemInHand(Event.Result.DENY);
event.setUseInteractedBlock(Event.Result.DENY);
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onClick(InventoryClickEvent event) {
if(event.getCurrentItem() != null && onButtonClick(event.getCurrentItem(), event.getActor(), event.getClick(), event)) {
event.setCancelled(true);
}
}
}