public class CachePrisoner {
    private Jail jail;
    private Prisoner p;

    public CachePrisoner(Jail jail, Prisoner prisoner) {
        this.jail = jail;
        this.p = prisoner;
    }

    /**
     * Gets the {@link Jail} this cache is in.
     * 
     * @return the {@link Jail} instance
     */
    public Jail getJail() {
        return this.jail;
    }

    /**
     * Gets the {@link Prisoner} in this cache.
     * 
     * @return the {@link Prisoner} instance
     */
    public Prisoner getPrisoner() {
        return this.p;
    }
} public class Cell implements ICell {
    private int databaseid;
    private String name;
    private Prisoner p;
    private HashSet<SimpleLocation> signs;
    private SimpleLocation teleport, chest;
    private boolean changed;

    /** Creates a new Cell with the given name
     *
     * @param name The name of the cell.
     */
    public Cell(String name) {
        this.databaseid = -1;
        this.name = name;
        this.signs = new HashSet<SimpleLocation>();
        this.changed = false;
    }
    
    /**
     * Creates a new Cell with the id in the database and the name.
     * 
     * @param id The id of the cell in the database.
     * @param name The name of the cell.
     */
    public Cell(int id, String name) {
        this.databaseid = id;
        this.name = name;
        this.signs = new HashSet<SimpleLocation>();
        this.changed = false;
    }
    
    public int getDatabaseID() {
        return this.databaseid;
    }

    public String getName() {
        return this.name;
    }

    public void setPrisoner(Prisoner prisoner) {
        this.p = prisoner;
        this.changed = true;
    }

    public Prisoner getPrisoner() {
        return this.p;
    }

    public void removePrisoner() {
        this.p = null;
        this.changed = true;
    }

    public boolean hasPrisoner() {
        return this.p != null; //Return true if prison is not null, as when it isn't null we have a prisoner in this cell
    }

    public void addAllSigns(HashSet<SimpleLocation> signs) {
        this.signs.addAll(signs);
        this.changed = true;
    }

    public void addSign(SimpleLocation sign) {
        this.signs.add(sign);
        this.changed = true;
    }

    public HashSet<SimpleLocation> getSigns() {
        return this.signs;
    }

    public boolean hasSigns() {
        return !this.signs.isEmpty();
    }

    public String getSignString() {
        String r = "";

        for(SimpleLocation s : signs) {
            if(r.isEmpty()) {
                r = s.toString();
            }else {
                r += ";" + s.toString();
            }
        }

        return r;
    }
    
    public List<String> getInvalidSigns() {
        List<String> invalid = new ArrayList<String>();

        for(SimpleLocation s : new HashSet<SimpleLocation>(signs))
            if (s.getLocation().getBlock().getState() instanceof Sign)
                continue;
           else
                invalid.add(s.toString());

        return invalid;
    }

    public List<String> cleanSigns() {
        List<String> cleaned = new ArrayList<String>();

        for(SimpleLocation s : new HashSet<SimpleLocation>(signs)) {
            if (s.getLocation().getBlock().getState() instanceof Sign) {
                continue;
            }else {
                changed = true;
                signs.remove(s);
                cleaned.add(s.toString());
            }
        }

        return cleaned;
    }

    public HashMap<String, List<String>> updateSigns() {
        List<String> removed = new ArrayList<String>();
        List<String> updated = new ArrayList<String>();

        for(SimpleLocation s : new HashSet<SimpleLocation>(signs)) {
            BlockState bs = s.getLocation().getBlock().getState();

            if (bs instanceof Sign) {
                Sign sign = (Sign) bs;

                if(hasPrisoner()) {
                    String[] lines = Util.replaceAllVariables(p, Util.getSignLines());

                    sign.setLine(0, lines[0]);
                    sign.setLine(1, lines[1]);
                    sign.setLine(2, lines[2]);
                    sign.setLine(3, lines[3]);
                    sign.update(true, false);
                }else {
                    sign.setLine(0, "");
                    sign.setLine(1, Lang.CELLEMPTYSIGN.get());
                    sign.setLine(2, "");
                    sign.setLine(3, "");
                    sign.update(true, false);
                }

                updated.add(s.toString());
            }else {
                changed = true;
                signs.remove(s);
                removed.add(s.toString());
            }
        }

        HashMap<String, List<String>> results = new HashMap<String, List<String>>();
        results.put("removed", removed);
        results.put("updated", updated);

        return results;
    }

    public void setTeleport(SimpleLocation location) {
        this.teleport = location;
        this.changed = true;
    }

    public Location getTeleport() {
        return this.teleport.getLocation();
    }

    public void setChestLocation(SimpleLocation simpleLocation) {
        this.chest = simpleLocation;
        this.changed = true;
    }

    public Location getChestLocation() {
        return this.chest == null ? null : this.chest.getLocation();
    }

    public Chest getChest() {
        if(this.chest == null) return null;
        if(this.chest.getLocation().getBlock() == null
                || (this.chest.getLocation().getBlock().getType() != Material.CHEST
                && this.chest.getLocation().getBlock().getType() != Material.TRAPPED_CHEST)) return null;

        return (Chest) this.chest.getLocation().getBlock().getState();
    }

    public boolean hasChest() {
        Chest c = getChest();
        if(c != null) {
            if(c.getInventory().getSize() >= 40)
                return true;
            else {
                Bukkit.getLogger().severe("The cell " + this.name + " has chest that isn't a double chest, please fix.");
                return false;
            }
        }else
            return false;
    }
    
    public boolean useChest() {
        return this.chest != null;
    }

    public boolean setChanged(boolean changed) {
        return this.changed = changed;
    }

    public boolean hasChanged() {
        return this.changed;
    }
} public class ConfirmPlayer {
    private String name;
    private String[] args;
    private Confirmation confirm;
    private Long expires;

    public ConfirmPlayer(String name, String[] args, Confirmation confirm) {
        this.name = name;
        this.args = args;
        this.confirm = confirm;
        this.expires = System.currentTimeMillis() + 10000L;
    }

    /**
     * Returns the name of the thing needing to confirm.
     * 
     * @return the name of what is being confirmed
     */
    public String getName() {
        return this.name;
    }

    /**
     * Returns the initial arguments they sent with their command.
     * 
     * @return the initial arguments of the original command
     */
    public String[] getArguments() {
        return this.args;
    }

    /**
     * Returns what they are {@link Confirmation confirming}.
     * 
     * @return the {@link Confirmation} of what they're confirming
     */
    public Confirmation getConfirming() {
        return this.confirm;
    }

    /**
     * Returns the time in milliseconds their confirmation time frame expires.
     * 
     * @return the time their confirming expires
     */
    public Long getExpiryTime() {
        return this.expires;
    }
} public class CreationPlayer {
    private String jailName, cellName;
    private int step;
    private int x1, y1, z1, x2, y2, z2;
    private String inWorld, freeWorld;
    private double inX, inY, inZ, freeX, freeY, freeZ;
    private float inPitch, inYaw, freePitch, freeYaw;
    private HashSet<SimpleLocation> signs;
    private SimpleLocation chest;

    /**
     * Create a new instance of a CreationPlayer, given the name of the jail.
     * 
     * @param jailName The name of the jail.
     */
    public CreationPlayer(String jailName) {
        this.jailName = jailName;
        this.step = 1; //Set the default to 1 when creating this.
    }

    /**
     * Creates a new instance of a CreationPlayer, give the name of the jail and cell.
     * 
     * @param jailName The name of the jail.
     * @param cellName The name of the cell.
     */
    public CreationPlayer(String jailName, String cellName) {
        this.jailName = jailName;
        this.cellName = cellName;
        this.signs = new HashSet<SimpleLocation>();
        this.step = 1;
    }

    /**
     * Gets the name of the jail.
     * 
     * @return the name of the jail the player is creating
     */
    public String getJailName() {
        return this.jailName;
    }

    /**
     * Gets the name of the cell.
     * 
     * @return the name of the cell the player is creating
     */
    public String getCellName() {
        return this.cellName;
    }

    /**
     * Returns the step the creation is in.
     * 
     * <p>
     * 
     * If it is a <strong>Jail</strong>, then when these numbers are returned it means the following:
     * <ol>
     * 	<li>Creating the first block of the Jail region.</li>
     * 	<li>Creating the second block of the Jail region.</li>
     * 	<li>Creating the teleport in location.</li>
     * 	<li>Creating the teleport out location.</li>
     * </ol>
     * 
     * If it is a <strong>Cell</strong>, then when these numbers are returned it means the following:
     * <ol>
     * 	<li>Setting the teleport in location.</li>
     * 	<li>Setting all the signs.</li>
     * 	<li>Setting the double chest.</li>
     * </ol>
     * 
     * @return The step of the Jail/Cell Creation, as an integer.
     */
    public int getStep() {
        return this.step;
    }

    /**
     * Sets the step of the creation.
     * 
     * @param step The state of the creation, see {@link #getStep() getStep} for more information.
     */
    public void setStep(int step) {
        this.step = step;
    }

    /**
     * Increments the current step up one.
     * 
     * <p>
     * 
     * <em>Notice:</em> Using this method can cause the step to go above four (three for cell),
     * which might cause errors later on. Only use when you know that it won't
     * be used again or you know for a fact that the next step is not above four (three for cell).
     * 
     */
    public void nextStep() {
        this.step++;
    }

    /**
     * Sets the first corner with the given location.
     * 
     * @param loc the location of the first corner
     */
    public void setCornerOne(Location loc) {
        this.x1 = loc.getBlockX();
        this.y1 = loc.getBlockY();
        this.z1 = loc.getBlockZ();
    }

    /**
     * Sets the first corner with the given x, y, and z.
     * 
     * @param x coordinate of the first corner's x
     * @param y coordinate of the first corner's x
     * @param z coordinate of the first corner's x
     */
    public void setCornerOne(int x, int y, int z) {
        this.x1 = x;
        this.y1 = y;
        this.z1 = z;
    }

    /**
     * Returns the <strong>first corner</strong> coords an array of int. <strong>0 = x</strong>, <strong>1 = y</strong>, <strong>2 = z</strong>
     * 
     * @return the first corner coords
     */
    public int[] getCornerOne() {
        return new int[] {x1, y1, z1};
    }

    /**
     * Sets the second corner with the given location.
     * 
     * @param loc The second corner's location
     */
    public void setCornerTwo(Location loc) {
        this.x2 = loc.getBlockX();
        this.y2 = loc.getBlockY();
        this.z2 = loc.getBlockZ();
    }

    /** Sets the second corner with the given x, y, and z.
     * 
     * @param x coordinate of the second corner's x
     * @param y coordinate of the second corner's x
     * @param z coordinate of the second corner's x
     */ 
    public void setCornerTwo(int x, int y, int z) {
        this.x2 = x;
        this.y2 = y;
        this.z2 = z;
    }

    /** Returns the <strong>second corner</strong> coords an array of int. <strong>0 = x</strong>, <strong>1 = y</strong>, <strong>2 = z</strong>
     * 
     * @return the first corner coords
     */
    public int[] getCornerTwo() {
        return new int[] {x2, y2, z2};
    }

    /**
     * Sets the teleport in coords from the given location.
     * 
     * @param location the in location
     */
    public void setTeleportIn(Location location) {
        this.inWorld = location.getWorld().getName();
        this.inX = location.getX();
        this.inY = location.getY();
        this.inZ = location.getZ();
        this.inYaw = location.getYaw();
        this.inPitch = location.getPitch();
    }

    /**
     * Sets the teleport in coords from the given params.
     * 
     * @param world the name of the world
     * @param x the coordinate for x
     * @param y the coordinate for y
     * @param z the coordinate for z
     * @param yaw the yaw of the teleport in
     * @param pitch the pitch of the teleport in
     */
    public void setTeleportIn(String world, double x, double y, double z, float yaw, float pitch) {
        this.inWorld = world;
        this.inX = x;
        this.inY = y;
        this.inZ = z;
        this.inYaw = yaw;
        this.inPitch = pitch;
    }

    /**
     * Gets the teleport in location in a {@link Location}.
     * 
     * @return the teleport in {@link Location}
     */
    public Location getTeleportIn() {
        return new Location(Bukkit.getWorld(inWorld), inX, inY, inZ, inYaw, inPitch);
    }

    /**
     * Gets the teleport in location in a {@link SimpleLocation}.
     * 
     * @return the {@link SimpleLocation} for the teleportin
     */
    public SimpleLocation getTeleportInSL() {
        return new SimpleLocation(inWorld, inX, inY, inZ, inYaw, inPitch);
    }

    /**
     * Sets the teleport free coords from the given location.
     * 
     * @param location Sets the teleport free {@link Location}
     */
    public void setTeleportFree(Location location) {
        this.freeWorld = location.getWorld().getName();
        this.freeX = location.getX();
        this.freeY = location.getY();
        this.freeZ = location.getZ();
        this.freeYaw = location.getYaw();
        this.freePitch = location.getPitch();
    }

    /**
     * Sets the teleport free coords from the given params.
     * 
     * @param world the name of the world
     * @param x the coordinate for x
     * @param y the coordinate for y
     * @param z the coordinate for z
     * @param yaw the yaw of the teleport free
     * @param pitch the pitch of the teleport free
     */
    public void setTeleportFree(String world, double x, double y, double z, float yaw, float pitch) {
        this.freeWorld = world;
        this.freeX = x;
        this.freeY = y;
        this.freeZ = z;
        this.freeYaw = yaw;
        this.freePitch = pitch;
    }

    /**
     * Gets the teleport free location in a {@link Location}.
     * 
     * @return the teleport free {@link Location}
     */
    public Location getTeleportFree() {
        return new Location(Bukkit.getWorld(freeWorld), freeX, freeY, freeZ, freeYaw, freePitch);
    }

    /**
     * Gets the teleport free location in a {@link SimpleLocation}.
     * 
     * @return the teleport free {@link SimpleLocation}
     */
    public SimpleLocation getTeleportFreeSL() {
        return new SimpleLocation(freeWorld, freeX, freeY, freeZ, freeYaw, freePitch);
    }

    /**
     * Adds a sign to this cell.
     * 
     * @param sign the {@link SimpleLocation} of the sign to add
     */
    public void addSign(SimpleLocation sign) {
        this.signs.add(sign);
    }

    /**
     * Returns all the signs, null if none (usually null when a jail is being created).
     * 
     * @return all of the signs, null if none.
     */
    public HashSet<SimpleLocation> getSigns() {
        return this.signs == null ? null : new HashSet<SimpleLocation>(this.signs);
    }

    /**
     * Sets the chest's location, used mainly for cells.
     * 
     * @param loc the {@link SimpleLocation} of the cheset
     */
    public void setChestLocation(SimpleLocation loc) {
        this.chest = loc;
    }

    /**
     * Gets the chest's location.
     * 
     * @return the {@link SimpleLocation} of the cheset
     */
    public SimpleLocation getChestLocation() {
        return this.chest;
    }
} public class Jail {
    private JailMain plugin;
    private boolean enabled;
    private HashMap<String, Cell> cells;
    private HashMap<UUID, Prisoner> nocellPrisoners;//prisoners who aren't in a cell
    private String name = "", world = "";
    private int minX, minY, minZ, maxX, maxY, maxZ;
    private Location in, free;

    public Jail(JailMain plugin, String name) {
        this.plugin = plugin;
        this.enabled = true;
        this.name = name;
        cells = new HashMap<String, Cell>();
        nocellPrisoners = new HashMap<UUID, Prisoner>();
    }

    /**
     * Gets the instance of the plugin's main class.
     * 
     * @return the {@link JailMain} instance
     */
    public JailMain getPlugin() {
        return this.plugin;
    }

    /**
     * Sets whether this jail can be used or not.
     * 
     * @param enabled Whether to set if this Jail is enabled or not
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    /**
     * Gets whether this jail is enabled or not.
     * 
     * @return Whether the Jail is enabled or not
     */
    public boolean isEnabled() {
        return this.enabled;
    }

    /**
     * Sets the name of the jail.
     * 
     * @param name the name of the jail to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Gets the name of the jail.
     * 
     * @return the name of the jail
     */
    public String getName() {
        return this.name;
    }

    /**
     * Sets the location of the <b>minimum</b> point to the given location's coordinates.
     * 
     * @param location the {@link Location} of the lowest point
     */
    public void setMinPoint(Location location) {
        if(this.world.isEmpty()) this.world = location.getWorld().getName();

        this.minX = location.getBlockX();
        this.minY = location.getBlockY();
        this.minZ = location.getBlockZ();
    }

    /**
     * Accepts an array of ints as the coord, where <strong>0 = x</strong>, <strong>1 = y</strong>, <strong>2 = z</strong>.
     * 
     * @param coords the coordinates of the minimum point
     */
    public void setMinPoint(int[] coords) {
        if(coords.length != 3) return;

        this.minX = coords[0];
        this.minY = coords[1];
        this.minZ = coords[2];
    }

    /**
     * Gets the minimum point as a Bukkit Location class.
     * 
     * @return the {@link Location} of the minimum point.
     */
    public Location getMinPoint() {
        return new Location(plugin.getServer().getWorld(world), minX, minY, minZ);
    }

    /**
     * Sets the location of the <b>maximum</b> point to the given location's coordinates.
     * 
     * @param location the {@link Location} of the maximum point
     */
    public void setMaxPoint(Location location) {
        if(this.world.isEmpty()) this.world = location.getWorld().getName();

        this.maxX = location.getBlockX();
        this.maxY = location.getBlockY();
        this.maxZ = location.getBlockZ();
    }

    /**
     * Gets the minimum point as a Bukkit Location class.
     * 
     * @return the {@link Location} of the maximum point
     */
    public Location getMaxPoint() {
        return new Location(plugin.getServer().getWorld(world), maxX, maxY, maxZ);
    }

    /** Accepts an array of ints as the coord, where <strong>0 = x</strong>, <strong>1 = y</strong>, <strong>2 = z</strong>. */
    public void setMaxPoint(int[] coords) {
        if(coords.length != 3) return;

        this.maxX = coords[0];
        this.maxY = coords[1];
        this.maxZ = coords[2];
    }

    /** Sets the name of the world this Jail is in. */
    public void setWorld(String name) {
        this.world = name;
    }

    /** Gets the name of the world this Jail is in. */
    public String getWorldName() {
        return this.world;
    }

    /** Gets the instance of the {@link World world} this Jail is in. */
    public World getWorld() {
        return plugin.getServer().getWorld(world);
    }

    /** Sets the {@link Location location} of the teleport <strong>in</strong>. */
    public void setTeleportIn(Location location) {
        if(this.world.isEmpty()) this.world = location.getWorld().getName();

        this.in = location;
    }

    /** Gets the {@link Location location} of the teleport in. */
    public Location getTeleportIn() {
        return this.in;
    }

    /** Sets the {@link Location location} of the teleport for the <strong>free</strong> spot. */
    public void setTeleportFree(Location location) {
        this.free = location;
    }

    /** Gets the {@link Location location} of the teleport free spot.*/
    public Location getTeleportFree() {
        return this.free;
    }

    /** Add a prisoner to this jail. */
    public void addPrisoner(Prisoner p) {
        this.nocellPrisoners.put(p.getUUID(), p);
    }

    /** Removes a prisoner from this jail, doesn't remove it from the cell. */
    public void removePrisoner(Prisoner p) {
        this.nocellPrisoners.remove(p.getUUID());
    }
    
    /** Returns if there are any cells or not. */
    public boolean hasCells() {
        return !this.cells.isEmpty();
    }

    /** Adds a cell to the Jail. */
    public boolean addCell(Cell cell, boolean save) {
        if(save) plugin.getJailIO().saveCell(this, cell, false);
        
        //Check if it already exists or not
        if(this.cells.containsKey(cell.getName())) return false;
        else this.cells.put(cell.getName(), cell);
        return true;
    }

    /** Gets the cell with the given name. */
    public Cell getCell(String name) {
        return this.cells.get(name);
    }

    /** Checks if the given name is a valid cell. */
    public boolean isValidCell(String name) {
        return this.cells.get(name) != null;
    }

    /** Removes the cell from the jail. */
    public void removeCell(String name) {
        Cell c = this.cells.get(name);
        //If we have a chest, clear the inventory
        if(c.hasChest()) {
            c.getChest().getInventory().clear();
        }

        //For each sign, clear the lines on the sign
        for(SimpleLocation s : c.getSigns()) {
            if(s.getLocation().getBlock() instanceof Sign) {
                Sign sign = (Sign) s.getLocation().getBlock();
                for(int i = 0; i < 4; i++) {
                    sign.setLine(i, "");
                }
            }
        }

        //remove the information from the storage first as it requires an instance
        plugin.getJailIO().removeCell(this, c);
        //now remove it from the local storage
        this.cells.remove(name);
    }

    /** Returns the cell which the given player name is jailed in, null if not. */
    public Cell getCellPrisonerIsIn(UUID uuid) {
        for(Cell c : cells.values())
            if(c.hasPrisoner())
                if(c.getPrisoner().getUUID().equals(uuid))
                    return c;

        return null;
    }

    /** Returns the first empty cell, returns null if there aren't any cells or any free cells. */
    public Cell getFirstEmptyCell() {
        for(Cell c : getCells())
            if(c.hasPrisoner())
                continue;
            else
                return c;

        return null;
    }
    
    /** Checks if there is an empty cell in this Jail. */
    public boolean hasEmptyCell() {
        for(Cell c : getCells())
            if(!c.hasPrisoner())
                return true;
        
        return false;
    }

    /** Gets the amount of cells the jail. */
    public int getCellCount() {
        return this.cells.size();
    }

    /** Gets all the cells in the jail. */
    public HashSet<Cell> getCells() {
        return new HashSet<Cell>(this.cells.values());
    }

    /** Gets the closest cell to the provided location, via the teleport in location of the cells. */
    public Cell getNearestCell(Location loc) {
        Cell cell = null;
        double distance = -1;

        for(Cell c : getCells()) {
            //Check if the worlds are the same, if not we can't calculate anything
            if(c.getTeleport().getWorld().getName().equalsIgnoreCase(loc.getWorld().getName())) {
                //They are in the same world
                double dist = c.getTeleport().distance(loc);
                if (dist < distance || distance < 0) {
                    cell = c;
                    distance = dist;
                }
            }else {
                //If they aren't, return the first cell found.
                return c;
            }
        }

        return cell;
    }

    /** Clears all the prisoners from this Jail. */
    public void clearPrisoners() {
        //Remove the prisoners from all the cells
        for(Cell c : getCells()) {
            c.removePrisoner();
        }

        //Replace all the current no cell prisoners with
        //a new hashset of prisoners.
        this.nocellPrisoners = new HashMap<UUID, Prisoner>();
    }

    /** Gets a HashMap of <b>all</b> the prisoners, the ones in cells and ones who aren't. */
    public HashMap<UUID, Prisoner> getAllPrisoners() {
        HashMap<UUID, Prisoner> all = new HashMap<UUID, Prisoner>(nocellPrisoners); //initalize the temp one to return with the prisoners not in any cells

        for(Cell c : cells.values())
            if(c.hasPrisoner())
                all.put(c.getPrisoner().getUUID(), c.getPrisoner());

        return all;
    }

    /** Gets a HashSet of the prisoners <b>in cells</b>. */
    public HashSet<Prisoner> getPrisonersInCells() {
        HashSet<Prisoner> all = new HashSet<Prisoner>();

        for(Cell c : getCells())
            if(c.hasPrisoner())
                all.add(c.getPrisoner());

        return all;
    }

    /** Gets a HashSet of the prisoners <b>not</b> in cells.*/
    public HashMap<UUID, Prisoner> getPrisonersNotInCells() {
        return this.nocellPrisoners;
    }

    /**
     * Returns whether the player is a prisoner in the system, whether in a cell or no cell.
     * 
     * @param player The {@link Player player instance} of the person we're checking.
     * @return true if is jailed, false if not.
     */
    public boolean isPlayerJailed(Player player) {
        return this.isPlayerAPrisoner(player.getUniqueId());
    }

    /**
     * Returns whether the uuid of a player is a prisoner in the system, whether in a cell or no cell.
     * 
     * @param uuid The uuid of the person we're checking.
     * @return true if is jailed, false if not.
     */
    public boolean isPlayerJailed(UUID uuid) {
        return this.isPlayerAPrisoner(uuid);
    }

    /**
     * Returns whether the uuid of a player is a prisoner in this jail, no matter if they're in a cell or not.
     * 
     * @param uuid The name of the person we're checking.
     * @return true if is a prisoner, false if not.
     */
    private boolean isPlayerAPrisoner(UUID uuid) {
        return this.getAllPrisoners().containsKey(uuid);
    }

    /**
     * Checks if the given uuid of a player is a prisoner in a cell.
     * 
     * @param uuid of the prisoner to check.
     * @return true if is jailed in a cell, false if not.
     */
    public boolean isJailedInACell(UUID uuid) {
        if(this.nocellPrisoners.containsKey(uuid)) return false;

        for(Cell c : cells.values())
            if(c.getPrisoner() != null)
                if(c.getPrisoner().getUUID().equals(uuid))
                    return true;

        return false;
    }

    /**
     * Gets the {@link Prisoner prisoner} instance for the given name.
     * 
     * @param name The name of the prisoner to get.
     * @return the prisoner instance, can be null
     */
    public Prisoner getPrisonerByLastKnownName(String name) {
        for(Prisoner p : this.getAllPrisoners().values())
            if(p.getLastKnownName().equalsIgnoreCase(name))
                return p;

        return null;
    }

    /**
     * Gets the {@link Prisoner prisoner} instance for the given uuid.
     * 
     * @param uuid The uuid of the prisoner to get.
     * @return the prisoner instance, can be null
     */
    public Prisoner getPrisoner(UUID uuid) {
        if(this.nocellPrisoners.containsKey(uuid)) return this.nocellPrisoners.get(uuid);

        for(Cell c : cells.values())
            if(c.hasPrisoner())
                if(c.getPrisoner().getUUID().equals(uuid))
                    return c.getPrisoner();

        return null;
    }

    /**
     * Returns the squared distance between teleport location of this jail
     * and specified location in blocks. If locations are not in same world,
     * distance cannot be calculated and it will return Integer.MAX_VALUE.
     * 
     * @param loc The location to check
     * @return Distance between the location provided and the teleport in location.
     */
    public double getDistance(Location loc) {
        if (!loc.getWorld().getName().equalsIgnoreCase(getTeleportIn().getWorld().getName())) return Integer.MAX_VALUE;
        else return loc.distance(getTeleportIn());
    }

    /**
     * Returns whether the given location is inside this Jail.
     * 
     * @param loc to check whether is inside this jail
     * @return True if the location is in the jail, false if it isn't
     */
    public boolean isInside(Location loc) {
        if(loc.getWorld().getName().equalsIgnoreCase(world)) {
            return Util.isInsideAB(loc.toVector(), new Vector(minX, minY, minZ), new Vector(maxX, maxY, maxZ));
        }else {
            return false;
        }
    }
} public class Prisoner {
    private String uuid, name, jailer, reason, inventory, armor;
    private boolean muted = true, offlinePending = false, teleporting = false, toBeTransferred = false, changed = false;
    private long time = -1L, afk = 0L;
    private Location previousPosition;
    private GameMode previousGameMode;

    /**
     * Creates the prisoner instance with the lot of data provided.
     *
     * @param uuid The uuid of the prisoner
     * @param name The name of the prisoner
     * @param muted Whether the prisoner is muted or not
     * @param time The amount of remaining time the prisoner has
     * @param jailer The name of the person who jailed this prisoner
     * @param reason The reason why this prisoner is in jail
     */
    public Prisoner(String uuid, String name, boolean muted, long time, String jailer, String reason) {
        this.uuid = uuid;
        this.name = name;
        this.muted = muted;
        this.time = time;
        this.jailer = jailer;
        this.reason = reason;
        finishSetup();
    }

    /**
     * Creates the prisoner instance with the lot of data provided.
     *
     * @param uuid The uuid of the prisoner
     * @param name The name of the prisoner
     * @param time The amount of remaining time the prisoner has
     * @param jailer The name of the person who jailed this prisoner
     * @param reason The reason why this prisoner is in jail
     */
    public Prisoner(String uuid, String name, long time, String jailer, String reason) {
        this.uuid = uuid;
        this.name = name;
        this.time = time;
        this.jailer = jailer;
        this.reason = reason;
        finishSetup();
    }

    /**
     * Creates the prisoner instance with the lot of data provided.
     *
     * @param uuid The uuid of the prisoner
     * @param name The name of the prisoner
     * @param time The amount of remaining time the prisoner has
     * @param reason The reason why this prisoner is in jail
     */
    public Prisoner(String uuid, String name, long time, String reason) {
        this.uuid = uuid;
        this.name = name;
        this.time = time;
        this.reason = reason;
        finishSetup();
    }

    /**
     * Creates the prisoner instance with the lot of data provided.
     *
     * @param uuid The uuid of the prisoner
     * @param name The name of the prisoner
     * @param time The amount of remaining time the prisoner has
     */
    public Prisoner(String uuid, String name, long time) {
        this.uuid = uuid;
        this.name = name;
        this.time = time;
        finishSetup();
    }

    /**
     * Creates the prisoner instance with the data provided.
     *
     * @param player The instance of the player who is to be jailed
     * @param muted Whether the prisoner is muted or not
     * @param time The amount of remaining time the prisoner has
     * @param jailer The jailer who jailed the prisoner
     * @param reason The reason why this prisoner is in jail
     */
    public Prisoner(Player player, boolean muted, long time, String jailer, String reason) {
        this.uuid = player.getUniqueId().toString();
        this.name = player.getName();
        this.muted = muted;
        this.time = time;
        this.jailer = jailer;
        this.reason = reason;
        finishSetup();
    }

    /**
     * Creates the prisoner instance with the data provided.
     *
     * @param player The instance of the player who is to be jailed
     * @param time The amount of remaining time the prisoner has
     * @param jailer The jailer who jailed the prisoner
     * @param reason The reason why this prisoner is in jail
     */
    public Prisoner(Player player, long time, String jailer, String reason) {
        this.uuid = player.getUniqueId().toString();
        this.name = player.getName();
        this.time = time;
        this.jailer = jailer;
        this.reason = reason;
        finishSetup();
    }

    /**
     * The most basic prisoner instance creation via providing the player, time, and reason.
     *
     * @param player The instance of the player who is to be jailed
     * @param time The amount of remaining time the prisoner has
     * @param reason The reason why this prisoner is in jail
     */
    public Prisoner(Player player, long time, String reason) {
        this.uuid = player.getUniqueId().toString();
        this.name = player.getName();
        this.time = time;
        this.reason = reason;
        finishSetup();
    }

    /**
     * The most basic prisoner instance creation via providing the player and time.
     *
     * @param player The instance of the player who is to be jailed
     * @param time The amount of remaining time the prisoner has
     */
    public Prisoner(Player player, long time) {
        this.uuid = player.getUniqueId().toString();
        this.name = player.getName();
        this.time = time;
        finishSetup();
    }

    /** Finishes the setup of the prisoner data, set to defaults. */
    private void finishSetup() {
        if(jailer == null)
            jailer = Lang.DEFAULTJAILER.get();
        if(reason == null)
            Lang.DEFAULTJAILEDREASON.get();
        if(inventory == null)
            inventory = "";
        if(armor == null)
            armor = "";
        if(previousGameMode == null)
            previousGameMode = GameMode.SURVIVAL;
        previousPosition = null;
    }

    /** Returns the UUID of the prisoner. */
    public UUID getUUID() {
        return UUID.fromString(this.uuid);
    }

    /** Gets the name of this prisoner. */
    public String getLastKnownName() {
        return this.name;
    }

    /** Sets the name of this prisoner. */
    public String setLastKnownName(String username) {
        this.name = username;
        this.changed = true;
        return this.name;
    }

    /** Gets the reason this player was jailed for. */
    public String getReason() {
        return this.reason;
    }

    /**
     * Sets the reason this player was jailed for.
     *
     * @param reason the player was jailed.
     * @return the reason the player was jailed, what we have stored about them.
     */
    public String setReason(String reason) {
        this.reason = reason;
        this.changed = true;
        return this.reason;
    }

    /** Gets the person who jailed this prisoner. */
    public String getJailer() {
        return this.jailer;
    }

    /** Sets the person who jailed this prisoner. */
    public void setJailer(String jailer) {
        this.jailer = jailer;
        this.changed = true;
    }

    /** Gets whether the prisoner is muted or not. */
    public boolean isMuted() {
        return this.muted;
    }

    /** Sets whether the prisoner is muted or not. */
    public void setMuted(boolean muted) {
        this.muted = muted;
        this.changed = true;
    }
    
    /** Gets whether the prisoner is jailed forever or not. */
    public boolean isJailedForever() {
        return this.time == -1;
    }

    /** Gets the remaining time the prisoner has. */
    public long getRemainingTime() {
        return this.time;
    }

    /** Gets the remaining time the prisoner has in minutes. */
    public long getRemainingTimeInMinutes() {
        return TimeUnit.MINUTES.convert(time, TimeUnit.MILLISECONDS);
    }

    /** Gets the remaining time the prison has in minutes except only in int format. */
    public int getRemainingTimeInMinutesInt() {
        return (int) this.getRemainingTimeInMinutes();
    }

    /**
     * Sets the remaining time the prisoner has left.
     *
     * @param time The amount of time left, in milliseconds.
     */
    public void setRemainingTime(long time) {
        this.time = time;
        this.changed = true;
    }

    /**
     * Adds the given time to the remaining time the prisoner has left, unless they're jailed forever.
     *
     * @param time to add to the prisoner's remaining time.
     * @return the new remaining time the prisoner has
     */
    public long addTime(long time) {
        if(this.time != -1L) {
            this.time += time;
            this.changed = true;
        }

        return this.time;
    }

    /**
     * Subtracts the given time from the remaining time the prisoner has left, unless they're jailed forever.
     *
     * @param time to subtract from the prisoner's remaining time.
     * @return the new remaining time the prisoner has
     */
    public long subtractTime(long time) {
        if(this.time != -1L) {
            this.time -= time;
            this.changed = true;
        }

        return this.time;
    }

    /** Gets whether the player is offline or not. */
    public boolean isOfflinePending() {
        return this.offlinePending;
    }

    /** Sets whether the player is offline or not. */
    public void setOfflinePending(boolean offline) {
        this.offlinePending = offline;
        this.changed = true;
    }

    /** Gets whether the player is being teleported or not. */
    public boolean isTeleporting() {
        return this.teleporting;
    }

    /** Sets whether the player is being teleported or not. */
    public void setTeleporting(boolean teleport) {
        this.teleporting = teleport;
    }

    /** Gets whether the prisoner is going to be transferred or not, mainly for teleporting on join purposes. */
    public boolean isToBeTransferred() {
        return this.toBeTransferred;
    }

    /** Sets whether the prisoner is going to be transferred or not, mainly for teleporting on join purposes. */
    public void setToBeTransferred(boolean transferred) {
        this.toBeTransferred = transferred;
        this.changed = true;
    }

    /** Gets the previous location of this player, can be null. */
    public Location getPreviousLocation() {
        return this.previousPosition;
    }

    /** Gets the previous location of this player, separated by a comma. */
    public String getPreviousLocationString() {
        if(previousPosition == null) return "";
        else if(previousPosition.getWorld() == null) return "";
        else return previousPosition.getWorld().getName() + "," +
        previousPosition.getX() + "," +
        previousPosition.getY() + "," +
        previousPosition.getZ() + "," +
        previousPosition.getYaw() + "," +
        previousPosition.getPitch();
    }

    /** Sets the previous location of this player. */
    public void setPreviousPosition(Location location) {
        this.previousPosition = location;
    }

    /** Sets the previous location of this player from a comma separated string. */
    public void setPreviousPosition(String location) {
        if(location == null) return;
        if(location.isEmpty()) return;

        this.changed = true;
        String[] s = location.split(",");
        this.previousPosition = new Location(Bukkit.getWorld(s[0]),
                Double.valueOf(s[1]),
                Double.valueOf(s[2]),
                Double.valueOf(s[3]),
                Float.valueOf(s[4]),
                Float.valueOf(s[5]));
    }

    /** Gets the previous gamemode of this player. */
    public GameMode getPreviousGameMode() {
        return this.previousGameMode;
    }

    /** Sets the previous gamemode of this player. */
    public void setPreviousGameMode(GameMode previous) {
        this.previousGameMode = previous;
        this.changed = true;
    }

    /** Sets the previous gamemode of this player based upon the provided string. */
    public void setPreviousGameMode(String previous) {
        if(previous == null) return;
        else if(previous.isEmpty()) return;
        else this.previousGameMode = GameMode.valueOf(previous);
        this.changed = true;
    }

    /** Gets the inventory string for this player, it is encoded in Base64 string. */
    public String getInventory() {
        return this.inventory;
    }

    /** Sets the inventory Base64 string. */
    public void setInventory(String inventory) {
        this.inventory = inventory;
        this.changed = true;
    }

    /** Gets the armor content, encoded in Base64 string. */
    public String getArmor() {
        return this.armor;
    }

    /** Sets the armor inventory Base64 string. */
    public void setArmor(String armor) {
        this.armor = armor;
        this.changed = true;
    }

    /** Gets the time, in milliseconds, this prisoner has been afk. */
    public long getAFKTime() {
        return this.afk;
    }

    /** Sets the time, in milliseconds, this prisoner has been afk. */
    public void setAFKTime(long time) {
        this.afk = time;
    }

    /** Checks if the prisoner was changed or not. */
    public boolean wasChanged() {
        return this.changed;
    }

    /** Sets whether the prisoner was changed or not. */
    public boolean setChanged(boolean change) {
        this.changed = change;
        return this.changed;
    }
} public enum Confirmation {
    /** When they are clearing a jail from all their prisoner's with releasing properly. */
    CLEAR,
    /** When they are clearing a jail from all their prisoner's by force. */
    CLEARFORCE,
    /** When they are deleting a cell from a jail. */
    DELETECELL,
    /** When they are deleting all a jail's cells. */
    DELETECELLS,
    /** When they are deleting a jail. */
    DELETE;
} public enum Lang {
    // actions section
    /** Section for when they break a block. */
    BLOCKBREAKING("actions"),
    /** Section for when they place a block. */
    BLOCKPLACING("actions"),
    /** Section for when they try to do a command that isn't whitelisted. */
    COMMAND("actions"),
    /** Section for when a player tramples a crop and protection is enabled. */
    CROPTRAMPLING("actions"),
    /** Section for when a player interacts with a block that is blacklisted. */
    INTERACTIONBLOCKS("actions"),
    /** Section for when a player interacts with an item that is blacklisted. */
    INTERACTIONITEMS("actions"),
    /** Section for when a player moves outside of the jail. */
    MOVING("actions"),

    // Jailing section
    /** The message displayed when players are kicked for being afk. */
    AFKKICKMESSAGE("jailing"),
    /** The message sent when jailing someone that is already jailed. */
    ALREADYJAILED("jailing"),
    /** The message sent when we broadcast/log the message for time below -1. */
    BROADCASTMESSAGEFOREVER("jailing"),
    /** The message sent when we broadcast/log the message for any time above -1. */
    BROADCASTMESSAGEFORMINUTES("jailing"),
    /** The message sent to the broadcast/log the unjailing of someone. */
    BROADCASTUNJAILING("jailing"),
    /** The message sent to the sender when trying to jail someone and a plugin cancels it but doesn't leave a message why. */
    CANCELLEDBYANOTHERPLUGIN("jailing"),
    /** The message sent when trying to jail someone who can't be jailed by permission. */
    CANTBEJAILED("jailing"),
    /** The message sent to the sender when they are trying to jail into a cell which is not empty. */
    CELLNOTEMPTY("jailing"),
    /** The jailer set whenever a jailer is not provided. */
    DEFAULTJAILER("jailing"),
    /** The message sent when someone is jailed without a reason. */
    DEFAULTJAILEDREASON("jailing"),
    /** The message sent when someone is unjailed yet they never came online and so they were forcefully unjailed. */
    FORCEUNJAILED("jailing"),
    /** The message sent when players are jailed without a reason. */
    JAILED("jailing"),
    /** The message sent when players are jailed with a reason. */
    JAILEDWITHREASON("jailing"),
    /** The message sent when players are jailed and they try to talk. */
    MUTED("jailing"),
    /** The message sent when the sender tries to jail someone in a cell and there aren't any cells to suggest. */
    NOEMPTYCELLS("jailing"),
    /** The message sent to the sender when they list all the prisoners in a jail which has no prisoners. */
    NOPRISONERS("jailing"),
    /** The message sent when a player is not jailed and the sender is trying to see/do something about it. */
    NOTJAILED("jailing"),
    /** The message sent to the sender when they mute a prisoner. */
    NOWMUTED("jailing"),
    /** The message sent to the sender when they unmute a prisoner. */
    NOWUNMUTED("jailing"),
    /** The message sent to the jailer when they jail someone offline. */
    OFFLINEJAIL("jailing"),
    /** The message sent to the jailer when they jail someone who is online. */
    ONLINEJAIL("jailing"),
    /** The message sent to the jailer when they jail someone who has never played before. */
    PLAYERHASNEVERPLAYEDBEFORE("jailing"),
    /** The message sent when finding out how much time a prisoner has. */
    PRISONERSTIME("jailing"),
    /** The message sent to the prisoner when they try to do something but it is protected. */
    PROTECTIONMESSAGE("jailing"),
    /** The message sent to the prisoner when they try to do something and it is protected, but no penalty. */
    PROTECTIONMESSAGENOPENALTY("jailing"),
    /** The message sent to the sender when they need to provide a player. */
    PROVIDEAPLAYER("jailing"),
    /** The message sent to the sender when they need to provide a jail. */
    PROVIDEAJAIL("jailing"),
    /** The message sent to someone trying to jail someone else with a jail stick that requires health below a certain amount. */
    RESISTEDARRESTJAILER("jailing"),
    /** The message sent to the person someone else is trying to jail that requires their health below a certain amount. */
    RESISTEDARRESTPLAYER("jailing"),
    /** The message sent when to a prisoner about their status in jail. */
    STATUS("jailing"),
    /** The message sent to the sender of a command when suggesting a cell. */
    SUGGESTEDCELL("jailing"),
    /** The message sent to the sender when they teleport someone to a jail's teleport in location. */
    TELEIN("jailing"),
    /** The message sent to the sender when they teleport someone to a jail's teleport out location. */
    TELEOUT("jailing"),
    /** The message sent to the sender when they transfer all a jail's prisoners to another jail. */
    TRANSFERALLCOMPLETE("jailing"),
    /** The message sent when another plugin cancels the transferring but doesn't provide a reason why. */
    TRANSFERCANCELLEDBYANOTHERPLUGIN("jailing"),
    /** The message sent to the sender when they transfer someone to a jail and a cell. */
    TRANSFERCOMPLETECELL("jailing"),
    /** The message sent to the sender when they transfer someone to a jail. */
    TRANSFERCOMPLETENOCELL("jailing"),
    /** The message sent to the player when they get transferred to a new jail. */
    TRANSFERRED("jailing"),
    /** The message sent when players are released from jail. */
    UNJAILED("jailing"),
    /** The message sent to the person who released a prisoner from jail. */
    UNJAILSUCCESS("jailing"),
    /** The message sent when you jailing offline players is not allowed. */
    UNALLOWEDTOJAILOFFLINE("jailing"),
    /** The message went when an offline player is unjailed. */
    WILLBEUNJAILED("jailing"),
    /** The message sent when trying to jail a player in an unloaded world. */
    WORLDUNLOADED("jailing"),
    /** The message sent when a player joins and is jailed in a world that is unloaded. */
    WORLDUNLOADEDKICK("jailing"),
    /** The message sent to the sender when they check their jail status and they aren't jailed. */
    YOUARENOTJAILED("jailing"),

    // Handcuffing section
    /** The message sent to the sender when trying to handcuff someone who can't be. */
    CANTBEHANDCUFFED("handcuffing"),
    /** The message sent to the sender whenever they try to handcuff someone who is in jail. */
    CURRENTLYJAILEDHANDCUFF("handcuffing", "currentlyjailed"),
    /** The message sent to the sender when the player doesn't have any handcuffs. */
    NOTHANDCUFFED("handcuffing"),
    /** The message sent to the handcuff on a successful handcuffing. */
    HANDCUFFSON("handcuffing"),
    /** The message sent when players are handcuffed. */
    HANDCUFFED("handcuffing"),
    /** The message sent to the player who has release handcuffs. */
    HANDCUFFSRELEASED("handcuffing"),
    /** The message sent when the player has his/her handcuffs removed. */
    UNHANDCUFFED("handcuffing"),

    // General section, used by different parts
    /** Part message of any messages which require 'all the jails' or such. */
    ALLJAILS("general"),
    /** The one line on signs when the cell is empty. */
    CELLEMPTYSIGN("general"),
    /** The message sent to the sender whenever they try to remove a cell but was unsuccessful due to a prisoner. */
    CELLREMOVALUNSUCCESSFUL("general"),
    /** The message sent whenever a cell is successfully removed. */
    CELLREMOVED("general"),
    /** The message sent when cleaning our cell signs. */
    CLEANEDSIGNS("general"),
    /** The message sent when seeing what signs are invalid. */
    INVALIDSIGNS("general"),
    /** The line on a cell's sign when the prisoner is jailed forever. */
    JAILEDFOREVERSIGN("general"),
    /** The simple word jailing to be put in other parts. */
    JAILING("general"),
    /** The message sent to the sender when they try to remove a jail but there are still prisoners in there. */
    JAILREMOVALUNSUCCESSFUL("general"),
    /** The message sent whenever a jail is successfully removed. */
    JAILREMOVED("general"),
    /** The message sent whenever a player toggles using jail stick to disabled. */
    JAILSTICKDISABLED("general"),
    /** The message sent whenever a player toggles using jail stick to enabled. */
    JAILSTICKENABLED("general"),
    /** The message sent whenever a player tries to toggle using jail stick but the config has it disabled. */
    JAILSTICKUSAGEDISABLED("general"),
    /** Message sent when doing something that requires a cell but the given name of a cell doesn't exist. */
    NOCELL("general"),
    /** Message sent when needing a cell or something and there are no cells. */
    NOCELLS("general"),
    /** Message sent when no invalid signs were found. */
    NOINVALIDSIGNS("general"),
    /** The message sent whenever the sender does something which the jail does not found. */
    NOJAIL("general"),
    /** The message sent whenever the sender does something and there are no jails. */
    NOJAILS("general"),
    /** The message sent whenever the sender/player doesn't have permission. */
    NOPERMISSION("general"),
    /** The message sent whenever the sender/player supplies a number format that is incorrect. */
    NUMBERFORMATINCORRECT("general"),
    /** The message sent whenever something is done that needs a player but doesn't have it. */
    PLAYERCONTEXTREQUIRED("general"),
    /** The message sent whenever an online player is required but not found. */
    PLAYERNOTONLINE("general"),
    /** The message sent to the sender when the plugin data has been reloaded. */
    PLUGINRELOADED("general"),
    /** The message sent to the sender of a command when the plugin didn't start correct. */
    PLUGINNOTLOADED("general"),
    /** The message sent whenever the prisoners are cleared from jail(s). */
    PRISONERSCLEARED("general"),
    /** The format we should use when entering a record into flatfile or showing it. */
    RECORDENTRY("general"),
    /** The message format sent saying how many times a user has been jailed. */
    RECORDTIMESJAILED("general"),
    /** The simple word: sign. */
    SIGN("general"),
    /** The message sent when the signs are refreshed. */
    SIGNSREFRESHED("general"),
    /** The format of the time entry we should use for the record entries. */
    TIMEFORMAT("general"),
    /** The simple word transferring to be put in other parts. */
    TRANSFERRING("general"),
    /** The message sent whenever someone does a command we don't know. */
    UNKNOWNCOMMAND("general"),

    // Jail pay
    /** The message sent when the jail pay portion is not enabled. */
    PAYNOTENABLED("jailpay", "notenabled"),
    /** The message sent when finding out how much it costs. */
    PAYCOST("jailpay", "cost"),
    /** The message sent when finding out how much it costs and they are jailed forever. */
    PAYCOSTINFINITE("jailpay", "costinfinite"),
    /** The message sent when someone tries to pay a negative amount. */
    PAYNONEGATIVEAMOUNTS("jailpay", "nonegativeamounts"),
    /** The message sent when someone is jailed and tries to pay for someone else. */
    PAYCANTPAYWHILEJAILED("jailpay", "cantpayforotherswhilejailed"),
    /** The message sent whenever someone tries to pay an amount they don't have. */
    PAYNOTENOUGHMONEY("jailpay", "notenoughmoney"),
    /** The message sent when they try to pay an amount but it isn't enough for the jailing sentence. */
    PAYNOTENOUGHMONEYPROVIDED("jailpay", "notenoughmoneyprovided"),
    /** The message sent when they pay and get released. */
    PAYPAIDRELEASED("jailpay", "paidreleased"),
    /** The message sent when they pay for someone else and release them. */
    PAYPAIDRELEASEDELSE("jailpay", "paidreleasedelse"),
    /** The message sent when they pay and lower their time. */
    PAYPAIDLOWEREDTIME("jailpay", "paidloweredtime"),
    /** The message sent when they pay and lower someone else's time. */
    PAYPAIDLOWEREDTIMEELSE("jailpay", "paidloweredtimeelse"),

    // Jail vote
    /** The header sent when broadcasting a new jail vote. */
    VOTEBROADCASTHEADER("jailvote.broadcast", "header"),
    /** The footer sent when broadcasting a new jail vote. */
    VOTEBROADCASTFOOTER("jailvote.broadcast", "footer"),
    /** Line1 of the broadcast message sent when a new jail vote is happening. */
    VOTEBROADCASTLINE1("jailvote.broadcast", "line1"),
    /** Line2 of the broadcast message sent when a new jail vote is happening. */
    VOTEBROADCASTLINE2("jailvote.broadcast", "line2"),
    /** Line3 of the broadcast message sent when a new jail vote is happening. */
    VOTEBROADCASTLINE3("jailvote.broadcast", "line3"),
    /** Line4 of the broadcast message sent when a new jail vote is happening. */
    VOTEBROADCASTLINE4("jailvote.broadcast", "line4"),
    /** The message sent when someone tries to vote for a player when a vote isn't running. */
    VOTENOVOTEFORTHATPLAYER("jailvote", "novotegoingforthatplayer"),
    /** The message sent to a player who tries to start a vote to jail someone and doesn't have permission. */
    VOTENOPERMISSIONTOSTART("jailvote", "nopermissiontostartvote"),
    /** The message sent when jail vote is not enabled. */
    VOTENOTENABLED("jailvote", "notenabled"),
    /** The message sent whenever someone's vote is not successful. */
    VOTEUNSUCCESSFUL("jailvote", "voteunsuccessful"),
    /** The message sent whenever a player successfully votes no. */
    VOTENOSUCCESS("jailvote", "votenosuccessful"),
    /** The message sent whenever a player successfully votes yes. */
    VOTEYESSUCCESS("jailvote", "voteyessuccessful"),
    /** The message broadcasted whenever a vote is tied. */
    VOTESTIED("jailvote", "votestied"),
    /** The message broadcasted whenever there are more no votes. */
    VOTESSAIDNO("jailvote", "morenovotes"),
    /** The message broadcasted whenever there aren't the minimum yes votes. */
    VOTESNOTENOUGHYES("jailvote", "notenoughyes"),
    /** The message broadcasted whenever the player the vote is for is no longer online. */
    VOTEPLAYERNOLONGERONLINE("jailvote", "playernolongeronline"),
    /** The message sent when a player tries to vote again for someone. */
    VOTEALREADYVOTEDFOR("jailvote", "alreadyvotedfor"),

    // Confirming action messages.
    /** The message sent when the sender is already confirming something. */
    ALREADY("confirm"),
    /** The message sent when their confirmation has expired. */
    EXPIRED("confirm"),
    /** The message sent to the sender when they tried to confirm something but don't have anything to confirm. */
    NOTHING("confirm"),
    /** The message sent to the sender when they type something and need to confirm it. */
    START("confirm");

    private String                   section, name, path;
    private static YamlConfiguration lang;

    Lang(String section) {
        this.section = section;
        this.name = toString().toLowerCase();
        this.path = "language." + this.section + "." + this.name;
    }

    Lang(String section, String name) {
        this.section = section;
        this.name = name;
        this.path = "language." + this.section + "." + this.name;
    }

    /**
     * Sets the {@link YamlConfiguration} instance to use.
     *
     * @param file
     *            of the language to use.
     */
    public static void setFile(YamlConfiguration file) {
        lang = file;
    }

    /** Gets the {@link YamlConfiguration} instance. */
    public static YamlConfiguration getFile() {
        return lang;
    }

    /** Writes any new language settings to the language file in storage. */
    public static boolean writeNewLanguage(YamlConfiguration newLang) {
        boolean anything = false;

        for(Lang l : values()) {
            if(!lang.contains(l.path)) {
                lang.set(l.path, newLang.getString(l.path));
                anything = true;
            }
        }

        return anything;
    }

    /** Returns the message in the language, no variables are replaced. */
    public String get() {
        return get(new String[] {});
    }

    /** Returns the message in the language, no variables are replaced. */
    public String get(Lang langString) {
        return get(new String[] { langString.get() });
    }

    /**
     * Returns the message in the language, with the provided variables being replaced.
     *
     * @param variables
     *            All the variables to replace, in order from 0 to however many.
     * @return The message as a colorful message or an empty message if that
     *         isn't defined in the language file.
     */
    public String get(String... variables) {
        String message = lang.getString(path);

        if (message == null) return "";

        for (int i = 0; i < variables.length; i++) {
            message = message.replaceAll("%" + i + "%", variables[i]);
        }

        return ChatColor.translateAlternateColorCodes('&', message);
    }
} public class CellCreationSteps {
    /** Sends the Cell Creation message for starting out. */
    public void startStepping(Player player){
        player.sendMessage(ChatColor.AQUA + "---------- Jail Cell Creation (tp) ----------");
        player.sendMessage(ChatColor.GREEN + "First, you must select a teleport point for the cell! Move to the teleport point and then click anywhere with your clay brick to set it.");
        player.sendMessage(ChatColor.AQUA + "----------------------------------------");

        ItemStack wand = Util.getWand();
        if(player.getInventory().contains(wand)) {
            int i = player.getInventory().first(wand);
            if(i != -1) {
                player.getInventory().setItem(i, player.getInventory().getItemInMainHand());
                player.getInventory().setItemInMainHand(wand);
            }
        }else {
            player.getInventory().addItem(wand);
        }
    }

    /**
     * Applies the next step in the Cell Creation process that involves a location, null if no location is needed.
     * 
     * @param jm The {@link JailManager} instance.
     * @param player The player who is doing the creating.
     * @param cp The {@link CreationPlayer} instance
     * @param location The location, null if none, being set.
     */
    public void step(JailManager jm, Player player, CreationPlayer cp, Location location) {
        jm.getPlugin().debug("Stepping into step #" + cp.getStep());
        switch(cp.getStep()) {
            case 1:
                firstStep(jm, cp, player);
                break;
            case 2:
                secondStep(cp, player, location.getBlock());
                break;
            case 3:
                thirdStep(jm, cp, player, location.getBlock());
                break;
            default:
                player.sendMessage(ChatColor.RED + "Something went wrong with the creation of the Jail, please start over");
                jm.removeJailCreationPlayer(player.getName());
                break;
        }
    }

    /** Applies the first step, which is setting the teleport in location. */
    private void firstStep(JailManager jm, CreationPlayer cp, Player player) {
        Vector v1 = jm.getJail(cp.getJailName()).getMinPoint().toVector().clone();
        Vector v2 = jm.getJail(cp.getJailName()).getMaxPoint().toVector().clone();
        Vector point = player.getLocation().toVector().clone();

        if(Util.isInsideAB(point, v1, v2)) {
            player.sendMessage(ChatColor.AQUA + "---------- Jail Cell Creation (signs) ----------");
            player.sendMessage(ChatColor.GREEN + "Teleport point selected. Now select signs associated with this cell. You may select multiple signs. After you are done with the sign selection, right click on any non-sign block.");
            player.sendMessage(ChatColor.AQUA + "----------------------------------------");

            cp.setTeleportIn(player.getLocation());
            cp.nextStep();
        }else {
            player.sendMessage(ChatColor.RED + "---------- Jail Cell Creation (tp) ----------");
            player.sendMessage(ChatColor.RED + "Teleport point NOT selected. Please make sure that you are setting the teleport point inside the Jail's corners.");
            player.sendMessage(ChatColor.RED + "----------------------------------------");
        }
    }

    /** Applies the second step, which is adding signs to the cell. */
    private void secondStep(CreationPlayer cp, Player player, Block block) {
        if (block.getType() == Material.SIGN || block.getType() == Material.WALL_SIGN) {
            cp.addSign(new SimpleLocation(block.getLocation()));

            //Set the sign's first text
            Sign sign = (Sign) block.getState();
            sign.setLine(0, "");
            sign.setLine(1, Lang.CELLEMPTYSIGN.get());
            sign.setLine(2, "");
            sign.setLine(3, "");
            sign.update();

            player.sendMessage(ChatColor.GREEN + "Sign added, if you want to select another go ahead otherwise right click on any non-sign block.");
        }else {
            player.sendMessage(ChatColor.AQUA + "---------- Jail Cell Creation (chest) ----------");
            player.sendMessage(ChatColor.GREEN + (cp.getSigns().size() != 0 ? "Sign selection completed. " : "") + "Now select a double chest associated with this cell. If there is no chest click on any non-chest block. (Please note that having no chest may result in players items being lost.)");
            player.sendMessage(ChatColor.AQUA + "----------------------------------------");
            cp.nextStep();
        }
    }

    /** Applies the third step, which is adding a chest or select not to have a chest. */
    private void thirdStep(JailManager jm, CreationPlayer cp, Player player, Block block) {
        if (block.getType() == Material.CHEST || block.getType() == Material.TRAPPED_CHEST) {
            if(((Chest) block.getState()).getInventory().getSize() >= 40) {
                cp.setChestLocation(new SimpleLocation(block.getLocation()));
                player.sendMessage(ChatColor.AQUA + "---------- Jail Cell Creation ----------");
                player.sendMessage(ChatColor.GREEN + "Chest selected.");
                player.sendMessage(ChatColor.AQUA + "----------------------------------------");
            }else {
                player.sendMessage(ChatColor.RED + "---------- Jail Cell Creation (chest) ----------");
                player.sendMessage(ChatColor.RED + "Chest must be a double chest, chest not selected");
                player.sendMessage(ChatColor.RED + "----------------------------------------");
                return;
            }
        }else {
            player.sendMessage(ChatColor.AQUA + "---------- Jail Cell Creation ----------");
            player.sendMessage(ChatColor.RED + "No chest selected.");
            player.sendMessage(ChatColor.AQUA + "----------------------------------------");
        }

        finalStep(jm, cp, player);
    }

    private void finalStep(JailManager jm, CreationPlayer cp, Player player) {
        Jail j = jm.getJail(cp.getJailName());
        Cell c = new Cell(cp.getCellName());

        c.addAllSigns(cp.getSigns());
        c.setTeleport(cp.getTeleportInSL());

        if(cp.getChestLocation() != null)
            c.setChestLocation(cp.getChestLocation());

        j.addCell(c, true);

        jm.removeCellCreationPlayer(player.getName());
        jm.addCreatingCell(player.getName(), j.getName(), "cell_n" + (j.getCellCount() + 1));

        player.sendMessage(ChatColor.AQUA + "---------- Jail Cell Creation ----------");
        player.sendMessage(ChatColor.GREEN + "Cell created. Now select the teleport point of the next cell, which is going to be named '" + jm.getCellCreationPlayer(player.getName()).getCellName() + "'. If you wish to stop creating cells, type /jail stop.");
        player.sendMessage(ChatColor.AQUA + "----------------------------------------");
    }
} public class JailCreationSteps {

    /** Sends the Jail Creation message for starting out. */
    public void startStepping(Player player) {
        player.sendMessage(ChatColor.AQUA + "----------Jail Zone Creation----------");
        player.sendMessage(ChatColor.GREEN + "First, you must select jail cuboid. Select the first point of the cuboid by right clicking on the block with your clay brick. DO NOT FORGET TO MARK THE FLOOR AND CEILING TOO!");
        player.sendMessage(ChatColor.AQUA + "--------------------------------------");

        ItemStack wand = Util.getWand();
        if(player.getInventory().contains(wand)) {
            int i = player.getInventory().first(wand);
            if(i != -1) {
                player.getInventory().setItem(i, player.getInventory().getItemInMainHand());
                player.getInventory().setItemInMainHand(wand);
            }
        }else {
            player.getInventory().addItem(wand);
        }
    }

    /**
     * Applies the next step in the Jail Creation process that involves a location, null if no location is needed.
     * 
     * @param jm The {@link JailManager} instance.
     * @param player The player who is doing the creating.
     * @param cp The {@link CreationPlayer} instance
     * @param location The location, null if none, being set.
     */
    public void step(JailManager jm, Player player, CreationPlayer cp, Location location) {
        jm.getPlugin().debug("Stepping into step #" + cp.getStep());
        switch(cp.getStep()) {
            case 1:
                firstStep(cp, player, location);
                break;
            case 2:
                secondStep(cp, player, location);
                break;
            case 3:
                thirdStep(cp, player);
                break;
            case 4:
                fourthStep(jm, cp, player);
                break;
            default:
                player.sendMessage(ChatColor.RED + "Something went wrong with the creation of the Jail, please start over");
                jm.removeJailCreationPlayer(player.getName());
                break;
        }
    }

    /** Applies the first step, which is setting the first corner. */
    private void firstStep(CreationPlayer cp, Player p, Location location) {
        p.sendMessage(ChatColor.AQUA + "---------- Jail Zone Creation----------");
        p.sendMessage(ChatColor.GREEN + "First point selected. Now select the second point.");
        p.sendMessage(ChatColor.AQUA + "---------------------------------------");

        cp.setCornerOne(location);
        cp.nextStep();
    }

    /** Applies the second step, which is setting the second corner. */
    private void secondStep(CreationPlayer cp, Player p, Location location) {
        p.sendMessage(ChatColor.AQUA + "---------- Jail Zone Creation ----------");
        p.sendMessage(ChatColor.GREEN + "Second point selected. Now go inside the jail and right click anywhere to select your current position as the teleport location for the jail.");
        p.sendMessage(ChatColor.AQUA + "----------------------------------------");

        cp.setCornerTwo(location);
        cp.nextStep();
    }

    /** Applies the third step, which is setting the teleport in location. */
    private void thirdStep(CreationPlayer cp, Player p) {

        int[] p1 = cp.getCornerOne();
        int[] p2 = cp.getCornerTwo();
        Vector v1 = new Vector(p1[0], p1[1], p1[2]);
        Vector v2 = new Vector(p2[0], p2[1], p2[2]);
        Vector point = p.getLocation().toVector().clone();

        if(Util.isInsideAB(point, v1, v2)) {
            p.sendMessage(ChatColor.AQUA + "---------- Jail Zone Creation ----------");
            p.sendMessage(ChatColor.GREEN + "Teleport point selected. Now go outside of the jail and right click anywhere to select your current position as the location where people will be teleported after they are released from this jail.");
            p.sendMessage(ChatColor.AQUA + "----------------------------------------");

            cp.setTeleportIn(p.getLocation());
            cp.nextStep();
        } else {
            p.sendMessage(ChatColor.RED + "---------- Jail Zone Creation ----------");
            p.sendMessage(ChatColor.RED + "Teleport point NOT selected. Now go inside the jail and right click anywhere to select your current position as the teleport location for the jail. Point must be INSIDE the points selected for the Jail.");
            p.sendMessage(ChatColor.RED + "----------------------------------------");
        }
    }

    private void fourthStep(JailManager jm, CreationPlayer cp, Player p) {
        int[] p1 = cp.getCornerOne();
        int[] p2 = cp.getCornerTwo();
        Vector v1 = new Vector(p1[0], p1[1], p1[2]);
        Vector v2 = new Vector(p2[0], p2[1], p2[2]);
        Vector point = p.getLocation().toVector().clone();

        if(Util.isInsideAB(point, v1, v2)) {
            p.sendMessage(ChatColor.RED + "---------- Jail Zone Creation ----------");
            p.sendMessage(ChatColor.RED + "Teleport out point NOT selected. Go outside of the jail and right click anywhere to select your current position as the location where people will be teleported after they are released from this jail.");
            p.sendMessage(ChatColor.RED + "----------------------------------------");
        }else {
            cp.setTeleportFree(p.getLocation());

            finalStep(jm, cp, p);
        }
    }

    private void finalStep(JailManager jm, CreationPlayer cp, Player p) {
        Jail jail = new Jail(jm.getPlugin(), cp.getJailName());

        jail.setMinPoint(cp.getCornerOne());
        jail.setMaxPoint(cp.getCornerTwo());
        jail.setTeleportIn(cp.getTeleportInSL().getLocation());
        jail.setTeleportFree(cp.getTeleportFreeSL().getLocation());

        jm.addJail(jail, true);

        p.sendMessage(ChatColor.GREEN + "Jail (" + jail.getName() +  ") created successfully!");

        jm.removeJailCreationPlayer(p.getName());
    }
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import com.graywolf336.jail.beans.CachePrisoner;
import com.graywolf336.jail.beans.Cell;
import com.graywolf336.jail.beans.ConfirmPlayer;
import com.graywolf336.jail.beans.CreationPlayer;
import com.graywolf336.jail.beans.Jail;
import com.graywolf336.jail.beans.Prisoner;
import com.graywolf336.jail.enums.Confirmation;
import com.graywolf336.jail.enums.Lang;
import com.graywolf336.jail.steps.CellCreationSteps;
import com.graywolf336.jail.steps.JailCreationSteps;
package com.graywolf336.jail;




/**
 * Handles all things related to jails.
 * 
 * <p>
 * 
 * Stores the following:
 * <ul>
 * 	<li>The {@link Jail jails}, which contains the prisoners and cells.</li>
 * 	<li>Players creating jails, see {@link CreationPlayer}.</li>
 * 	<li>Players creating jail cells, see {@link CreationPlayer}.</li>
 * 	<li>An instance of {@link JailCreationSteps} for stepping players through the Jail creation process.</li>
 * </ul>
 * 
 * @author graywolf336
 * @since 3.0.0
 * @version 1.1.0
 */
public class JailManager {
    private JailMain plugin;
    private HashMap<String, Jail> jails;
    private HashMap<String, CreationPlayer> jailCreators;
    private HashMap<String, CreationPlayer> cellCreators;
    private HashMap<String, ConfirmPlayer> confirms;
    private HashMap<UUID, CachePrisoner> cache;