// Path: src/main/java/me/desht/scrollingmenusign/views/SMSMapView.java
// public class SMSMapRenderer extends MapRenderer {
//     private final SMSMapView view;
// 
//     public SMSMapRenderer(SMSMapView view) {
//         super(true);
//         this.view = view;
//     }
// 
//     public SMSMapView getView() {
//         return view;
//     }
// 
//     @Override
//     public void render(MapView map, MapCanvas canvas, Player player) {
//         if (isDirty(player)) {
//             BufferedImage img = renderImage(player);
//             canvas.drawImage(0, 0, img);
//             setDirty(player, false);
//             player.sendMap(map);
//         }
//     }
// }
// 
// Path: src/main/java/me/desht/scrollingmenusign/views/SMSView.java
// public class MenuStack {
//     final Deque<WeakReference<SMSMenu>> stack;
// 
//     public MenuStack() {
//         stack = new ArrayDeque<WeakReference<SMSMenu>>();
//     }
// 
//     public void pushMenu(SMSMenu menu) {
//         stack.push(new WeakReference<SMSMenu>(menu));
//     }
// 
//     public SMSMenu popMenu() {
//         return stack.pop().get();
//     }
// 
//     public SMSMenu peek() {
//         return stack.peek().get();
//     }
// 
//     public boolean isEmpty() {
//         return stack.isEmpty();
//     }
// }
// 
// Path: src/main/java/me/desht/scrollingmenusign/views/action/RepaintAction.java
// public class RepaintAction extends ViewUpdateAction {
//     public RepaintAction() {
//         super();
//     }
// 
//     public RepaintAction(CommandSender sender) {
//         super(sender);
//     }
// }


import me.desht.dhutils.Debugger;
import me.desht.dhutils.LogUtils;
import me.desht.dhutils.block.BlockUtil;
import me.desht.scrollingmenusign.*;
import me.desht.scrollingmenusign.views.SMSMapView.SMSMapRenderer;
import me.desht.scrollingmenusign.views.SMSView.MenuStack;
import me.desht.scrollingmenusign.views.action.RepaintAction;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.map.MapRenderer;
import org.bukkit.map.MapView;
import org.bukkit.util.Vector;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.Map.Entry;

    private void loadError(String viewName, Throwable e) {
        LogUtils.warning("Caught " + e.getClass().getName() + " while loading view " + viewName);
        LogUtils.warning("  Exception message: " + e.getMessage());
    }

    /**
     * Register this view in the global view list and get it saved to disk.
     */
    public void registerView(SMSView view) {
        if (allViewNames.containsKey(view.getName())) {
            throw new SMSException("A view named '" + view.getName() + "' already exists.");
        }
        allViewNames.put(view.getName(), view);
        for (Location l : view.getLocations()) {
            plugin.getLocationManager().registerLocation(l, view);
        }
        view.getNativeMenu().addObserver(view);
        view.autosave();
    }

    /**
     * Unregister a view: remove it as an observer from its menu (and any active submenus),
     * and remove its name & location(s) from the manager.
     *
     * @param view the view to unregister
     */
    private void unregisterView(SMSView view) {
        view.getNativeMenu().deleteObserver(view);
        for (UUID playerId : view.getSubmenuPlayers()) {
// Path: src/main/java/me/desht/scrollingmenusign/views/SMSMapView.java
// public class SMSMapRenderer extends MapRenderer {
//     private final SMSMapView view;
// 
//     public SMSMapRenderer(SMSMapView view) {
//         super(true);
//         this.view = view;
//     }
// 
//     public SMSMapView getView() {
//         return view;
//     }
// 
//     @Override
//     public void render(MapView map, MapCanvas canvas, Player player) {
//         if (isDirty(player)) {
//             BufferedImage img = renderImage(player);
//             canvas.drawImage(0, 0, img);
//             setDirty(player, false);
//             player.sendMap(map);
//         }
//     }
// }
// 
// Path: src/main/java/me/desht/scrollingmenusign/views/SMSView.java
// public class MenuStack {
//     final Deque<WeakReference<SMSMenu>> stack;
// 
//     public MenuStack() {
//         stack = new ArrayDeque<WeakReference<SMSMenu>>();
//     }
// 
//     public void pushMenu(SMSMenu menu) {
//         stack.push(new WeakReference<SMSMenu>(menu));
//     }
// 
//     public SMSMenu popMenu() {
//         return stack.pop().get();
//     }
// 
//     public SMSMenu peek() {
//         return stack.peek().get();
//     }
// 
//     public boolean isEmpty() {
//         return stack.isEmpty();
//     }
// }
// 
// Path: src/main/java/me/desht/scrollingmenusign/views/action/RepaintAction.java
// public class RepaintAction extends ViewUpdateAction {
//     public RepaintAction() {
//         super();
//     }
// 
//     public RepaintAction(CommandSender sender) {
//         super(sender);
//     }
// }


// Path: src/main/java/me/desht/scrollingmenusign/views/ViewManager.java
import me.desht.dhutils.Debugger;
import me.desht.dhutils.LogUtils;
import me.desht.dhutils.block.BlockUtil;
import me.desht.scrollingmenusign.*;
import me.desht.scrollingmenusign.views.SMSMapView.SMSMapRenderer;
import me.desht.scrollingmenusign.views.SMSView.MenuStack;
import me.desht.scrollingmenusign.views.action.RepaintAction;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.map.MapRenderer;
import org.bukkit.map.MapView;
import org.bukkit.util.Vector;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.Map.Entry;


    private void loadError(String viewName, Throwable e) {
        LogUtils.warning("Caught " + e.getClass().getName() + " while loading view " + viewName);
        LogUtils.warning("  Exception message: " + e.getMessage());
    }

    /**
     * Register this view in the global view list and get it saved to disk.
     */
    public void registerView(SMSView view) {
        if (allViewNames.containsKey(view.getName())) {
            throw new SMSException("A view named '" + view.getName() + "' already exists.");
        }
        allViewNames.put(view.getName(), view);
        for (Location l : view.getLocations()) {
            plugin.getLocationManager().registerLocation(l, view);
        }
        view.getNativeMenu().addObserver(view);
        view.autosave();
    }

    /**
     * Unregister a view: remove it as an observer from its menu (and any active submenus),
     * and remove its name & location(s) from the manager.
     *
     * @param view the view to unregister
     */
    private void unregisterView(SMSView view) {
        view.getNativeMenu().deleteObserver(view);
        for (UUID playerId : view.getSubmenuPlayers()) {
            MenuStack mst = view.getMenuStack(playerId);