package me.desht.scrollingmenusign.views;
import com.dsh105.holoapi.HoloAPI;
import com.dsh105.holoapi.api.Hologram;
import com.dsh105.holoapi.api.HologramFactory;
import com.dsh105.holoapi.api.touch.Action;
import com.dsh105.holoapi.api.touch.TouchAction;
import me.desht.dhutils.ConfigurationManager;
import me.desht.dhutils.Debugger;
import me.desht.dhutils.MiscUtil;
import me.desht.scrollingmenusign.SMSException;
import me.desht.scrollingmenusign.SMSMenu;
import me.desht.scrollingmenusign.ScrollingMenuSign;
import me.desht.scrollingmenusign.enums.SMSUserAction;
import me.desht.scrollingmenusign.views.hologram.HoloUtil;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockPhysicsEvent;
import java.util.LinkedHashMap;
import java.util.Observable;
public class SMSPublicHoloView extends SMSGlobalScrollableView {
private static final String LINES = "lines";
private static final String DIRECTION = "direction";
private static final String OFFSET = "offset";
private static final String REDSTONE = "redstone";
private Hologram hologram;
private boolean powered;
public SMSPublicHoloView(String name, SMSMenu menu) {
super(name, menu);
registerAttributes();
}
public SMSPublicHoloView(String name, SMSMenu menu, Location loc) {
super(name, menu);
if (!ScrollingMenuSign.getInstance().isHoloAPIEnabled()) {
throw new SMSException("Public hologram view cannot be created - server does not have HoloAPI enabled");
}
registerAttributes();
addLocation(loc);
}
@Override
public void addLocation(Location loc) {
super.addLocation(loc);
powered = loc.getBlock().isBlockIndirectlyPowered();
}
private void registerAttributes() {
registerAttribute(LINES, 4, "Number of lines visible in the hologram (including title)");
registerAttribute(DIRECTION, BlockFace.UP, "Positioning of hologram relative to anchor block");
registerAttribute(OFFSET, 1.0, "Offset distance from anchor block");
registerAttribute(REDSTONE, RedstoneBehaviour.IGNORE, "Whether to require a redstone signal to show the hologram");
}
private Hologram buildHologram(String[] text) {
if (hologram != null) {
HoloAPI.getManager().stopTracking(hologram);
}
Debugger.getInstance().debug("creating new public hologram for " + getName());
Hologram h = new HologramFactory(ScrollingMenuSign.getInstance())
.withLocation(getHologramPosition())
.withText(text)
.withSimplicity(true)
.build();
h.addTouchAction(new SMSHoloTouchAction(this));
h.setTouchEnabled(true);
return h;
}
private Location getHologramPosition() {
Location loc0 = getLocationsArray()[0].clone();
BlockFace face = (BlockFace) getAttribute(DIRECTION);
double offset = (Double) getAttribute(OFFSET);
loc0.add(0.5 + face.getModX() * offset, 0.5 + face.getModY() * offset, 0.5 + face.getModZ() * offset);
return loc0;
}
@Override
public String getType() {
return "public-holo";
}
@Override
public void update(Observable menu, Object arg1) {
super.update(menu, arg1);
repaintAll();
}
@Override
public void onDeleted(boolean permanent) {
super.onDeleted(permanent);
if (permanent) {
if (hologram != null) {
popdown();
}
}
}
private void repaintAll() {
if (isActive()) {
String[] text = HoloUtil.buildText(this, null, (Integer) getAttribute(LINES));
if (hologram != null && hologram.getLines().length != text.length) {
popdown(); // force a new hologram to be created with the right size
}
if (hologram == null) {
hologram = buildHologram(text);
HoloAPI.getManager().track(hologram, ScrollingMenuSign.getInstance());
} else {
HoloAPI.getManager().setLineContent(hologram, text);
// hologram.updateLines(text);
}
}
}
@Override
public String toString() {
Location[] locs = getLocationsArray();
return "public-holo @ " + (locs.length == 0 ? "NONE" : MiscUtil.formatLocation(getLocationsArray()[0]));
}
@Override
protected int getLineLength() {
return 50; // estimate
}
@Override
protected int getHardMaxTitleLines() {
return 2;
}
@Override
public void onConfigurationChanged(ConfigurationManager configurationManager, String attribute, Object oldVal, Object newVal) {
super.onConfigurationChanged(configurationManager, attribute, oldVal, newVal);
if ((attribute.equals(OFFSET) || attribute.equals(DIRECTION)) && hologram != null) {
hologram.move(getHologramPosition());
} else if (attribute.equals(REDSTONE)) {
checkVisibility(powered, powered, (RedstoneBehaviour) oldVal, (RedstoneBehaviour) newVal);
}
}
@Override
public boolean isClickable() {
// the anchor block isn't used for scrolling or executing - click the hologram itself for that
return false;
}
/**
* Check if this view is active; if the hologram for the view is being displayed.
* This is dependent on the anchor block's current redstone power level and the
* value of the REDSTONE attribute.
*
* @return true if the view is active, false otherwise
*/
public boolean isActive() {
return isActive(powered, getRedstoneBehaviour());
}
/**
* Get the current redstone behaviour.
*
* @return the current redstone behaviour
*/
public RedstoneBehaviour getRedstoneBehaviour() {
return (RedstoneBehaviour) getAttribute(REDSTONE);
}
@Override
public void processEvent(ScrollingMenuSign plugin, BlockPhysicsEvent event) {
super.processEvent(plugin, event);
boolean newPower = getLocationsArray()[0].getBlock().isBlockIndirectlyPowered();
if (newPower != powered) {
Debugger.getInstance().debug("holo anchor for " + getName() + " got power change! " + powered + " => " + newPower);
checkVisibility(powered, newPower, getRedstoneBehaviour(), getRedstoneBehaviour());
powered = newPower;
}
}
private void checkVisibility(boolean oldPower, boolean newPower, RedstoneBehaviour oldRedstone, RedstoneBehaviour newRedstone) {
if (!isActive(oldPower, oldRedstone) && isActive(newPower, newRedstone)) {
if (hologram == null) {
hologram = buildHologram(HoloUtil.buildText(this, null, (Integer) getAttribute(LINES)));
HoloAPI.getManager().track(hologram, ScrollingMenuSign.getInstance());
}
} else if (isActive(oldPower, oldRedstone) && !isActive(newPower, newRedstone) && hologram != null) {
popdown();
}
}
private boolean isActive(boolean level, RedstoneBehaviour rsb) {
switch (rsb) {
case IGNORE: return true;
case HIGH: return level;
case LOW: return !level;
default: throw new IllegalArgumentException("invalid value: " + rsb);
}
}
private void popdown() {
HoloAPI.getManager().stopTracking(hologram);
hologram = null;
}
public enum RedstoneBehaviour {
IGNORE, HIGH, LOW
}
private class SMSHoloTouchAction implements TouchAction {
private final SMSPublicHoloView view;
private SMSHoloTouchAction(SMSPublicHoloView view) {
this.view = view;
}
@Override
public void onTouch(Player player, Action action) {
Debugger.getInstance().debug("Hologram action: player=" + player.getName() + " action=" + action + " view = " + view.getName());
SMSUserAction ua = getAction(player, action);
if (ua != null) {
ua.execute(player, view);
}
}
@Override
public String getSaveKey() {
return null;
}
@Override
public LinkedHashMap<String, Object> getDataToSave() {
return null;
}
private SMSUserAction getAction(Player player, Action action) {
StringBuilder key = new StringBuilder();
switch (action) {
case RIGHT_CLICK:
key = new StringBuilder("sms.actions.rightclick.");
break;
case LEFT_CLICK:
key = new StringBuilder("sms.actions.leftclick.");
break;
}
key.append(player.isSneaking() ? "sneak" : "normal");
String s = ScrollingMenuSign.getInstance().getConfig().getString(key.toString(), "none");
return SMSUserAction.valueOf(s.toUpperCase());
}
}
}