// Path: src/main/java/com/nisovin/shopkeepers/compat/NMSManager.java
// public final class NMSManager {
// 
// 	private static NMSCallProvider provider;
// 
// 	public static NMSCallProvider getProvider() {
// 		return NMSManager.provider;
// 	}
// 
// 	public static void load(Plugin plugin) {
// 		final String packageName = plugin.getServer().getClass().getPackage().getName();
// 		String cbversion = packageName.substring(packageName.lastIndexOf('.') + 1);
// 		try {
// 			final Class<?> clazz = Class.forName("com.nisovin.shopkeepers.compat." + cbversion + ".NMSHandler");
// 			if (NMSCallProvider.class.isAssignableFrom(clazz)) {
// 				NMSManager.provider = (NMSCallProvider) clazz.getConstructor().newInstance();
// 			} else {
// 				throw new Exception("Nope");
// 			}
// 		} catch (final Exception e) {
// 			Log.severe("Potentially incompatible server version: " + cbversion);
// 			Log.severe("Shopkeepers is trying to run in 'compatibility mode'.");
// 			Log.info("Check for updates at: " + plugin.getDescription().getWebsite());
// 
// 			try {
// 				NMSManager.provider = new FailedHandler();
// 			} catch (Exception e_u) {
// 				// uncomment for debugging:
// 				// e_u.printStackTrace();
// 			}
// 		}
// 	}
// }
// 
// Path: src/main/java/com/nisovin/shopkeepers/shopobjects/DefaultShopObjectTypes.java
// public class DefaultShopObjectTypes {
// 
// 	private final LivingEntityObjectTypes livingEntityObjectTypes = new LivingEntityObjectTypes();
// 	private final ShopObjectType signShopObjectType = new SignShopObjectType();
// 	private final ShopObjectType citizensShopObjectType = new CitizensShopObjectType();
// 
// 	// TODO maybe change object type permissions to 'shopkeeper.object.<type>'?
// 
// 	public DefaultShopObjectTypes() {
// 	}
// 
// 	public List<ShopObjectType> getAllObjectTypes() {
// 		List<ShopObjectType> shopObjectTypes = new ArrayList<ShopObjectType>();
// 		shopObjectTypes.addAll(livingEntityObjectTypes.getAllObjectTypes());
// 		shopObjectTypes.add(signShopObjectType);
// 		shopObjectTypes.add(citizensShopObjectType);
// 		return shopObjectTypes;
// 	}
// 
// 	public LivingEntityObjectTypes getLivingEntityObjectTypes() {
// 		return livingEntityObjectTypes;
// 	}
// 
// 	public ShopObjectType getSignShopObjectType() {
// 		return signShopObjectType;
// 	}
// 
// 	public ShopObjectType getCitizensShopObjectType() {
// 		return citizensShopObjectType;
// 	}
// 
// 	// STATICS (for convenience):
// 
// 	public static ShopObjectType SIGN() {
// 		return ShopkeepersPlugin.getInstance().getDefaultShopObjectTypes().getSignShopObjectType();
// 	}
// 
// 	public static ShopObjectType CITIZEN() {
// 		return ShopkeepersPlugin.getInstance().getDefaultShopObjectTypes().getCitizensShopObjectType();
// 	}
// }


import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
import com.nisovin.shopkeepers.compat.NMSManager;
import com.nisovin.shopkeepers.shopobjects.DefaultShopObjectTypes;
				}

				// TODO maybe always prevent normal usage, also for cases in which shop creation failed because of some
				// reason, and instead optionally allow normal usage when crouching? Or, if normal usage is not denied,
				// allow shop creation only when crouching?
				// Or handle chest selection and/or shop creation on left clicks only, instead of right clicks?
				// Or with MC 1.9: Normal usage if the item is in the off-hand
			}
		}
	}

	// returns true on success
	private boolean handleShopkeeperCreation(Player player, ShopType<?> shopType, ShopObjectType shopObjType, Block selectedChest, Block clickedBlock, BlockFace clickedBlockFace) {
		assert shopType != null && shopObjType != null; // has been check already

		if (selectedChest == null) {
			// clicked a location without having a chest selected:
			Utils.sendMessage(player, Settings.msgMustSelectChest);
			return false;
		}
		assert Utils.isChest(selectedChest.getType()); // we have checked that above

		// check for selected chest being too far away:
		if (!selectedChest.getWorld().equals(clickedBlock.getWorld())
				|| (int) selectedChest.getLocation().distanceSquared(clickedBlock.getLocation()) > (Settings.maxChestDistance * Settings.maxChestDistance)) {
			Utils.sendMessage(player, Settings.msgChestTooFar);
			return false;
		}

		// TODO move object type specific stuff into the object type instead
// Path: src/main/java/com/nisovin/shopkeepers/compat/NMSManager.java
// public final class NMSManager {
// 
// 	private static NMSCallProvider provider;
// 
// 	public static NMSCallProvider getProvider() {
// 		return NMSManager.provider;
// 	}
// 
// 	public static void load(Plugin plugin) {
// 		final String packageName = plugin.getServer().getClass().getPackage().getName();
// 		String cbversion = packageName.substring(packageName.lastIndexOf('.') + 1);
// 		try {
// 			final Class<?> clazz = Class.forName("com.nisovin.shopkeepers.compat." + cbversion + ".NMSHandler");
// 			if (NMSCallProvider.class.isAssignableFrom(clazz)) {
// 				NMSManager.provider = (NMSCallProvider) clazz.getConstructor().newInstance();
// 			} else {
// 				throw new Exception("Nope");
// 			}
// 		} catch (final Exception e) {
// 			Log.severe("Potentially incompatible server version: " + cbversion);
// 			Log.severe("Shopkeepers is trying to run in 'compatibility mode'.");
// 			Log.info("Check for updates at: " + plugin.getDescription().getWebsite());
// 
// 			try {
// 				NMSManager.provider = new FailedHandler();
// 			} catch (Exception e_u) {
// 				// uncomment for debugging:
// 				// e_u.printStackTrace();
// 			}
// 		}
// 	}
// }
// 
// Path: src/main/java/com/nisovin/shopkeepers/shopobjects/DefaultShopObjectTypes.java
// public class DefaultShopObjectTypes {
// 
// 	private final LivingEntityObjectTypes livingEntityObjectTypes = new LivingEntityObjectTypes();
// 	private final ShopObjectType signShopObjectType = new SignShopObjectType();
// 	private final ShopObjectType citizensShopObjectType = new CitizensShopObjectType();
// 
// 	// TODO maybe change object type permissions to 'shopkeeper.object.<type>'?
// 
// 	public DefaultShopObjectTypes() {
// 	}
// 
// 	public List<ShopObjectType> getAllObjectTypes() {
// 		List<ShopObjectType> shopObjectTypes = new ArrayList<ShopObjectType>();
// 		shopObjectTypes.addAll(livingEntityObjectTypes.getAllObjectTypes());
// 		shopObjectTypes.add(signShopObjectType);
// 		shopObjectTypes.add(citizensShopObjectType);
// 		return shopObjectTypes;
// 	}
// 
// 	public LivingEntityObjectTypes getLivingEntityObjectTypes() {
// 		return livingEntityObjectTypes;
// 	}
// 
// 	public ShopObjectType getSignShopObjectType() {
// 		return signShopObjectType;
// 	}
// 
// 	public ShopObjectType getCitizensShopObjectType() {
// 		return citizensShopObjectType;
// 	}
// 
// 	// STATICS (for convenience):
// 
// 	public static ShopObjectType SIGN() {
// 		return ShopkeepersPlugin.getInstance().getDefaultShopObjectTypes().getSignShopObjectType();
// 	}
// 
// 	public static ShopObjectType CITIZEN() {
// 		return ShopkeepersPlugin.getInstance().getDefaultShopObjectTypes().getCitizensShopObjectType();
// 	}
// }


// Path: src/main/java/com/nisovin/shopkeepers/CreateListener.java
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
import com.nisovin.shopkeepers.compat.NMSManager;
import com.nisovin.shopkeepers.shopobjects.DefaultShopObjectTypes;

				}

				// TODO maybe always prevent normal usage, also for cases in which shop creation failed because of some
				// reason, and instead optionally allow normal usage when crouching? Or, if normal usage is not denied,
				// allow shop creation only when crouching?
				// Or handle chest selection and/or shop creation on left clicks only, instead of right clicks?
				// Or with MC 1.9: Normal usage if the item is in the off-hand
			}
		}
	}

	// returns true on success
	private boolean handleShopkeeperCreation(Player player, ShopType<?> shopType, ShopObjectType shopObjType, Block selectedChest, Block clickedBlock, BlockFace clickedBlockFace) {
		assert shopType != null && shopObjType != null; // has been check already

		if (selectedChest == null) {
			// clicked a location without having a chest selected:
			Utils.sendMessage(player, Settings.msgMustSelectChest);
			return false;
		}
		assert Utils.isChest(selectedChest.getType()); // we have checked that above

		// check for selected chest being too far away:
		if (!selectedChest.getWorld().equals(clickedBlock.getWorld())
				|| (int) selectedChest.getLocation().distanceSquared(clickedBlock.getLocation()) > (Settings.maxChestDistance * Settings.maxChestDistance)) {
			Utils.sendMessage(player, Settings.msgChestTooFar);
			return false;
		}

		// TODO move object type specific stuff into the object type instead
		if (shopObjType == DefaultShopObjectTypes.SIGN() && !Utils.isWallSignFace(clickedBlockFace)) {