// 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;
     * @param loc  Location of the new view
     * @return The newly-created view
     * @throws SMSException if the given location is not a suitable location for the new view
     */
    public SMSView addPublicHoloViewToMenu(String viewName, SMSMenu menu, Location loc, CommandSender owner) {
        SMSView view = new SMSPublicHoloView(viewName, menu, loc);
        initialiseView(view, owner);
        return view;
    }

    /**
     * Convenience method.  Create and register a new SMSPrivateHoloView object, and attach it to
     * the given menu.
     *
     * @param viewName Name for the view; if null a name will be auto-generated
     * @param menu The menu to attach the new view to
     * @return The newly-created view
     * @throws SMSException if the given location is not a suitable location for the new view
     */
    public SMSView addPrivateHoloToView(String viewName, SMSMenu menu, CommandSender owner) {
        SMSView view = new SMSPrivateHoloView(viewName, menu);
        initialiseView(view, owner);
        return view;
    }

    private void initialiseView(SMSView view, CommandSender owner) {
        // common setup tasks for a new view
        registerView(view);
        view.setAttribute(SMSView.OWNER, view.makeOwnerName(owner));
        view.setOwnerId(getUniqueId(owner));
// 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;

     * @param loc  Location of the new view
     * @return The newly-created view
     * @throws SMSException if the given location is not a suitable location for the new view
     */
    public SMSView addPublicHoloViewToMenu(String viewName, SMSMenu menu, Location loc, CommandSender owner) {
        SMSView view = new SMSPublicHoloView(viewName, menu, loc);
        initialiseView(view, owner);
        return view;
    }

    /**
     * Convenience method.  Create and register a new SMSPrivateHoloView object, and attach it to
     * the given menu.
     *
     * @param viewName Name for the view; if null a name will be auto-generated
     * @param menu The menu to attach the new view to
     * @return The newly-created view
     * @throws SMSException if the given location is not a suitable location for the new view
     */
    public SMSView addPrivateHoloToView(String viewName, SMSMenu menu, CommandSender owner) {
        SMSView view = new SMSPrivateHoloView(viewName, menu);
        initialiseView(view, owner);
        return view;
    }

    private void initialiseView(SMSView view, CommandSender owner) {
        // common setup tasks for a new view
        registerView(view);
        view.setAttribute(SMSView.OWNER, view.makeOwnerName(owner));
        view.setOwnerId(getUniqueId(owner));
        view.update(view.getNativeMenu(), new RepaintAction());