package com.github.jamesnorris.ablockalypse.aspect;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.SkullType;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.CommandBlock;
import org.bukkit.block.ContainerBlock;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.block.Jukebox;
import org.bukkit.block.NoteBlock;
import org.bukkit.block.Sign;
import org.bukkit.block.Skull;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import com.github.jamesnorris.ablockalypse.Ablockalypse;
import com.github.jamesnorris.ablockalypse.DataContainer;
import com.github.jamesnorris.ablockalypse.External;
import com.github.jamesnorris.ablockalypse.behavior.Blinkable;
import com.github.jamesnorris.ablockalypse.behavior.MapDatable;
import com.github.jamesnorris.ablockalypse.utility.Cuboid;
import com.github.jamesnorris.ablockalypse.utility.Region;
import com.github.jamesnorris.ablockalypse.utility.SerialLocation;
@SuppressWarnings("deprecation") public class MapData {
private static DataContainer data = Ablockalypse.getData();
public static MapData getFromGame(Game game) {
return getFromGame(game.getName());
}
/* used for unloaded games */
public static MapData getFromGame(String gameName) {
return new MapData(gameName);
}
private Game game;
private File mapFile, gameFile;
public MapData(Game game) {
this.game = game;
mapFile = Ablockalypse.getExternal().getMapDataFile(game.getName(), true);
gameFile = Ablockalypse.getExternal().getMapGameObjectDataFile(game.getName(), true);
}
public MapData(String gameName) {
this(data.getGame(gameName, true));
}
@SuppressWarnings("unused") public boolean load(Location anchor) {
try {
List<Map<String, Object>> serialized = External.load(mapFile);
String gameName = "UNKNOWN";
Location originalAnchor = null;
Location originalOpposite = null;
for (Map<String, Object> thisSerialBlock : serialized) {
if (thisSerialBlock.containsKey("gameName")) {
gameName = (String) thisSerialBlock.get("gameName");
originalAnchor = SerialLocation.returnLocation((SerialLocation) thisSerialBlock.get("anchor"));
originalOpposite = SerialLocation.returnLocation((SerialLocation) thisSerialBlock.get("opposite"));
continue;
}
int id = (Integer) thisSerialBlock.get("id");
byte data = (Byte) thisSerialBlock.get("data");
World world = Bukkit.getWorld((String) thisSerialBlock.get("worldName"));
double x = anchor.getX() + (Double) thisSerialBlock.get("xDif");
double y = anchor.getY() + (Double) thisSerialBlock.get("yDif");
double z = anchor.getZ() + (Double) thisSerialBlock.get("zDif");
float yaw = (Float) thisSerialBlock.get("yaw");
float pitch = (Float) thisSerialBlock.get("pitch");
Location loc = new Location(world, x, y, z, yaw, pitch);
Block block = loc.getBlock();
block.setTypeId(id);
block.setData(data);
updateSpecificData(block, thisSerialBlock);
}
@SuppressWarnings("unchecked") Map<String, Object> save = (Map<String, Object>) External.load(gameFile);
Region oldRegion = new Region(originalAnchor, originalOpposite);
for (MapDatable mdble : game.getObjectsOfType(MapDatable.class)) {
Location original = mdble.getPointClosestToOrigin();
double xDif = original.getX() - originalAnchor.getX();
double yDif = original.getY() - originalAnchor.getY();
double zDif = original.getZ() - originalAnchor.getZ();
if (!oldRegion.contains(original)) {
mdble.remove();
continue;
}
mdble.paste(anchor.clone().add(xDif, yDif, zDif));
}
return true;
} catch (Exception ex) {
Ablockalypse.getTracker().error("MapData could not be loaded from " + mapFile.getName() + ".", 35, ex);
return false;
}
}
public boolean save(Cuboid cuboid) {
try {
for (Blinkable blink : game.getObjectsOfType(Blinkable.class)) {
blink.getBlinkerTask().revertBlocks();
blink.getBlinkerTask().pause(true);
}
Location anchor = cuboid.getCorner(false, false, false);
List<Map<String, Object>> serialized = new ArrayList<Map<String, Object>>();
// game id tag
Map<String, Object> serialGameID = new HashMap<String, Object>();
serialGameID.put("gameName", game.getName());
serialGameID.put("anchor", new SerialLocation(anchor));
serialGameID.put("opposite", new SerialLocation(cuboid.getCorner(true, true, true)));
serialized.add(serialGameID);
for (Location loc : cuboid.getLocations()) {
Map<String, Object> thisSerialBlock = new HashMap<String, Object>();
Block block = loc.getBlock();
BlockState state = block.getState();
thisSerialBlock.put("id", block.getTypeId());
thisSerialBlock.put("data", block.getData());
thisSerialBlock.put("worldName", loc.getWorld().getName());
thisSerialBlock.put("xDif", loc.getX() - anchor.getX());
thisSerialBlock.put("yDif", loc.getY() - anchor.getY());
thisSerialBlock.put("zDif", loc.getZ() - anchor.getZ());
thisSerialBlock.put("yaw", loc.getYaw());
thisSerialBlock.put("pitch", loc.getPitch());
if (state == null) {
continue;
}
thisSerialBlock.putAll(getSpecificData(state));
serialized.add(thisSerialBlock);
}
External.save(serialized, mapFile);
External.save(game.getSave(), gameFile);
for (Blinkable blink : game.getObjectsOfType(Blinkable.class)) {
blink.getBlinkerTask().pause(false);
}
return true;
} catch (Exception ex) {
Ablockalypse.getTracker().error("MapData could not be saved to " + mapFile.getName() + ".", 35, ex);
return false;
}
}
private ItemStack[] getItemsFromSerialization(Map<String, Object> thisSerialBlock) {
List<ItemStack> itemStacks = new ArrayList<ItemStack>();
Map<Integer, Map<String, Object>> serialStacks = new HashMap<Integer, Map<String, Object>>();
for (String name : thisSerialBlock.keySet()) {
Object obj = thisSerialBlock.get(name);
if (name.startsWith("MAPDATA_ITEM=")) {
name.replace("MAPDATA_ITEM=", "");
int index = name.indexOf("=");
int itemNum = Integer.parseInt(name.substring(0, index - 1));
name = name.substring(index);
Map<String, Object> serialItem = new HashMap<String, Object>();
serialItem.put(name, obj);
serialStacks.put(itemNum, serialItem);
}
}
for (Integer stackNum : serialStacks.keySet()) {
Map<String, Object> serialStack = serialStacks.get(stackNum);
itemStacks.add(ItemStack.deserialize(serialStack));
}
return itemStacks.toArray(new ItemStack[itemStacks.size()]);
}
private Map<String, Object> getSpecificData(BlockState state) {
Map<String, Object> thisSerialBlock = new HashMap<String, Object>();
if (state instanceof InventoryHolder && ((InventoryHolder) state).getInventory() != null) {
thisSerialBlock.put("INVENTORY_HOLDER", "NULL");
int i = 1;
for (ItemStack stack : ((InventoryHolder) state).getInventory().getContents()) {
if (stack == null) {
continue;
}
Map<String, Object> serialStack = stack.serialize();
for (String name : serialStack.keySet()) {
Object obj = serialStack.get(name);
serialStack.remove(name);
serialStack.put("MAPDATA_ITEM=" + i++ + "=" + name, obj);
}
thisSerialBlock.putAll(serialStack);
}
} else if (state instanceof ContainerBlock && ((ContainerBlock) state).getInventory() != null) {
thisSerialBlock.put("CONTAINER_BLOCK", "NULL");
int i = 1;
for (ItemStack stack : ((ContainerBlock) state).getInventory().getContents()) {
if (stack == null) {
continue;
}
Map<String, Object> serialStack = stack.serialize();
for (String name : serialStack.keySet()) {
Object obj = serialStack.get(name);
serialStack.remove(name);
serialStack.put("MAPDATA_ITEM=" + i++ + "=" + name, obj);
}
thisSerialBlock.putAll(serialStack);
}
}
if (state instanceof CommandBlock && ((CommandBlock) state).getCommand() != null) {
thisSerialBlock.put("command", ((CommandBlock) state).getCommand());
}
if (state instanceof CreatureSpawner && ((CreatureSpawner) state).getSpawnedType() != null) {
CreatureSpawner spawner = (CreatureSpawner) state;
thisSerialBlock.put("spawnType", spawner.getSpawnedType().toString());
thisSerialBlock.put("spawnDelay", spawner.getDelay());
}
if (state instanceof Jukebox && ((Jukebox) state).getPlaying() != null) {
thisSerialBlock.put("playingRecord", ((Jukebox) state).getPlaying().toString());
}
if (state instanceof NoteBlock) {
thisSerialBlock.put("rawNote", ((NoteBlock) state).getRawNote());
}
if (state instanceof Sign) {
for (int i = 0; i < 4; i++) {
String line = ((Sign) state).getLine(i);
thisSerialBlock.put("signLine" + i, line);
}
}
if (state instanceof Skull && ((Skull) state).getOwner() != null && ((Skull) state).getRotation() != null && ((Skull) state).getSkullType() != null) {
Skull skull = (Skull) state;
thisSerialBlock.put("skullOwner", skull.getOwner());
thisSerialBlock.put("skullRotation", skull.getRotation().toString());
thisSerialBlock.put("skullType", skull.getSkullType().toString());
}
return thisSerialBlock;
}
private void updateSpecificData(Block block, Map<String, Object> thisSerialBlock) {
BlockState state = block.getState();
if (thisSerialBlock.containsKey("INVENTORY_HOLDER")) {
((InventoryHolder) state).getInventory().setContents(getItemsFromSerialization(thisSerialBlock));
} else if (thisSerialBlock.containsKey("CONTAINER_BLOCK")) {
((ContainerBlock) state).getInventory().setContents(getItemsFromSerialization(thisSerialBlock));
}
if (thisSerialBlock.containsKey("command")) {
((CommandBlock) state).setCommand((String) thisSerialBlock.get("command"));
}
if (thisSerialBlock.containsKey("spawnType")) {
CreatureSpawner spawner = (CreatureSpawner) state;
spawner.setSpawnedType(EntityType.valueOf((String) thisSerialBlock.get("spawnType")));
spawner.setDelay((Integer) thisSerialBlock.get("spawnDelay"));
}
if (thisSerialBlock.containsKey("playingRecord")) {
((Jukebox) state).setPlaying(Material.valueOf((String) thisSerialBlock.get("playingRecord")));
}
if (thisSerialBlock.containsKey("rawNote")) {
((NoteBlock) state).setRawNote((Byte) thisSerialBlock.get("rawNote"));
}
if (thisSerialBlock.containsKey("signLine0")) {
for (int i = 0; i < 4; i++) {
((Sign) state).setLine(i, (String) thisSerialBlock.get("signLine" + i));
}
}
if (thisSerialBlock.containsKey("skullOwner")) {
Skull skull = (Skull) state;
skull.setOwner((String) thisSerialBlock.get("skullOwner"));
skull.setRotation(BlockFace.valueOf((String) thisSerialBlock.get("skullRotation")));
skull.setSkullType(SkullType.valueOf((String) thisSerialBlock.get("skullType")));
}
state.update(true, false);
}
}