package me.desht.scrollingmenusign;
import java.lang.ref.WeakReference;
import me.desht.dhutils.MiscUtil;
import me.desht.scrollingmenusign.views.PoppableView;
import me.desht.scrollingmenusign.views.SMSView;
import org.bukkit.Effect;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
/**
* @author des
* <p/>
* Represents a written book that can be used to trigger poppable views.
*/
public class PopupBook {
private static final int VIEW_TYPE = 2;
private static final int VIEW_NAME = 3;
private static final int MENU_NAME = 4;
private final WeakReference<SMSView> view;
private final WeakReference<Player> player;
/**
* Private constructor (use PopupBook.get()). Create a popup book object from a
* written book item.
*
* @param player the player
* @param bi the book item
*/
private PopupBook(Player player, ItemStack bi) {
BookMeta bm = (BookMeta) bi.getItemMeta();
String viewType = bm.getPage(VIEW_TYPE).split(" ")[1];
String viewName = bm.getPage(VIEW_NAME);
String menuName = bm.getPage(MENU_NAME);
if (!ScrollingMenuSign.getInstance().getHandler().checkMenu(menuName)) {
// the menu's been deleted? the book's of no use anymore
throw new SMSException("Missing menu " + menuName);
}
SMSView wantedView = null;
if (!ScrollingMenuSign.getInstance().getViewManager().checkForView(viewName)) {
// the view could have been deleted - see if the menu has any other views of the same type
for (SMSView view : ScrollingMenuSign.getInstance().getViewManager().listViews()) {
if (view.getNativeMenu().getName().equals(menuName) && view.getType().equals(viewType)) {
wantedView = view;
break;
}
}
if (wantedView != null) {
// update the book to refer to the new view we found
bm.setPage(VIEW_NAME, wantedView.getName());
}
} else {
wantedView = ScrollingMenuSign.getInstance().getViewManager().getView(viewName);
}
SMSValidate.isTrue(wantedView != null && wantedView instanceof PoppableView, "Invalid view: " + viewName);
this.player = new WeakReference<Player>(player);
this.view = new WeakReference<SMSView>(wantedView);
}
/**
* Create a popup book object for the given player and view.
*
* @param player the player object
* @param view the view
*/
public PopupBook(Player player, SMSView view) {
SMSValidate.isTrue(view instanceof PoppableView, "Invalid view: " + view.getName());
this.player = new WeakReference<Player>(player);
this.view = new WeakReference<SMSView>(view);
}
public SMSView getView() {
return view.get();
}
/**
* Toggle the popped state of the view this book refers to.
*/
public void toggle() {
Player p = this.player.get();
SMSView v = getView();
if (p != null && v != null) {
v.ensureAllowedToUse(p);
((PoppableView) v).toggleGUI(p);
}
}
/**
* Get the book item corresponding to this popup book.
*
* @return an ItemStack of 1 written book with the title and pages filled in
*/
public ItemStack toItemStack() {
return toItemStack(1);
}
/**
* Get one or more book item corresponding to this popup book.
*
* @param amount the number of books
* @return an ItemStack of one or mote written books with the title and pages filled in
*/
public ItemStack toItemStack(int amount) {
Player p = this.player.get();
SMSView v = getView();
if (v == null || p == null) {
return null;
}
ItemStack item = new ItemStack(Material.WRITTEN_BOOK, amount);
BookMeta bm = (BookMeta) item.getItemMeta();
bm.setTitle(v.doVariableSubstitutions(null, v.getNativeMenu().getTitle()));
bm.setAuthor(p.getDisplayName());
bm.setPages("Left Click to Use!",
"sms " + v.getType() + " view",
v.getName(),
v.getNativeMenu().getName());
item.setItemMeta(bm);
return item;
}
/**
* Get the popup book that the player is holding, if any.
*
* @param p the player
* @return the book, or null if the player is not holding one
* @throws SMSException if the player is holding a popup book, but it's not valid
*/
public static PopupBook get(Player p) {
if (!holding(p)) {
return null;
}
try {
return new PopupBook(p, p.getItemInHand());
} catch (SMSException e) {
destroy(p);
return null;
}
}
/**
* Check if the player is holding a popup book.
*
* @param p the player
* @return true if the player is holding a popup book
*/
public static boolean holding(Player p) {
if (p.getItemInHand().getType() != Material.WRITTEN_BOOK) {
return false;
}
BookMeta bm = (BookMeta) p.getItemInHand().getItemMeta();
return bm.getPageCount() >= 4 && bm.getPage(VIEW_TYPE).matches("^sms [\\w-]+ view$");
}
/**
* Destroys the item in the player's hand. Doesn't check first to see if it's a
* popup book - would usually be called if PopupBook.get() throws a SMSException.
*
* @param player the player object
*/
private static void destroy(Player player) {
player.setItemInHand(new ItemStack(Material.AIR));
MiscUtil.statusMessage(player, "Your book suddenly vanishes in a puff of smoke!");
player.playEffect(player.getLocation().add(player.getLocation().getDirection()), Effect.SMOKE, BlockFace.UP);
}
}