package joshie.harvest.town.tracker;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.HashBiMap;
import joshie.harvest.api.HFApi;
import joshie.harvest.api.calendar.CalendarDate;
import joshie.harvest.core.network.PacketHandler;
import joshie.harvest.town.data.TownData;
import joshie.harvest.town.data.TownDataServer;
import joshie.harvest.town.data.TownSavedData;
import joshie.harvest.town.packet.PacketNewTown;
import joshie.harvest.town.packet.PacketSyncTowns;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map.Entry;
import java.util.UUID;
import static joshie.harvest.api.calendar.Season.SPRING;
import static joshie.harvest.town.BuildingLocations.MINE_ENTRANCE;
public class TownTrackerServer extends TownTracker<TownDataServer> {
private static final CalendarDate FUTURE = new CalendarDate(0, SPRING, 999);
public static final TownDataServer NULL_TOWN = new TownDataServer() {
@Override
public CalendarDate getBirthday() {
return FUTURE;
}
};
private TownSavedData data;
@Override
public TownDataServer getNullTown() {
return NULL_TOWN;
}
public void setWorld(TownSavedData data, World world) {
super.setWorld(world);
this.data = data;
}
public void newDay(CalendarDate yesterday, CalendarDate today) {
Cache<BlockPos, Boolean> isFar = CacheBuilder.newBuilder().build();
for (TownDataServer town: uuidMap.values()) {
town.newDay(getWorld(), isFar, yesterday, today);
}
}
public void syncToPlayer(EntityPlayerMP player) {
PacketHandler.sendToClient(new PacketSyncTowns(uuidMap.values(), townIDs), player);
for (TownDataServer town: uuidMap.values()) {
town.getQuests().sync(player);
}
}
@Override
public BlockPos getCoordinatesForOverworldMine(@Nullable Entity entity, int mineID) {
BlockPos default_ = super.getCoordinatesForOverworldMine(entity, mineID);
UUID uuid = townIDs.inverse().get(mineID);
if (uuid == null) return default_;
TownData data = uuidMap.get(uuid);
if (data == null) return default_;
if (!data.hasBuilding(MINE_ENTRANCE.getBuilding())) return data.getTownCentre();
BlockPos location = data.getCoordinatesFor(MINE_ENTRANCE);
if (location != null) {
Rotation rotation = getMineOrientation(mineID);
if (rotation == Rotation.NONE) {
return location.west(12).down(3);
} else if (rotation == Rotation.CLOCKWISE_90) {
return location.north(12).down(3);
} else if (rotation == Rotation.CLOCKWISE_180) {
return location.east(12).down(3);
} else if (rotation == Rotation.COUNTERCLOCKWISE_90) {
return location.south(12).down(3);
}
}
return data.getTownCentre();
}
@Override
public Rotation getMineOrientation(int mineID) {
UUID uuid = townIDs.inverse().get(mineID);
if (uuid == null) return Rotation.NONE;
TownDataServer data = uuidMap.get(uuid);
if (data == null || !data.hasBuilding(MINE_ENTRANCE.getBuilding())) return Rotation.NONE;
return data.getFacingFor(MINE_ENTRANCE.getBuilding().getResource());
}
@Override
public int getMineIDFromCoordinates(@Nonnull BlockPos pos) {
TownData data = getClosestTownToBlockPos(pos, false);
if (!data.hasBuilding(MINE_ENTRANCE.getBuilding())) return -1;
if (townIDs.containsKey(data.getID())) {
return townIDs.get(data.getID());
} else return matchUUIDWithMineID(data.getID());
}
private int matchUUIDWithMineID(UUID uuid) {
for (int i = 0; i < 32000; i++) { //Add a mineid to uuid entry
if (!townIDs.inverse().containsKey(i)) {
townIDs.put(uuid, i);
markDirty();
return i;
}
}
return 0;
}
@Override
public TownDataServer createNewTown(BlockPos pos) {
TownDataServer data = new TownDataServer(getDimension(), pos, HFApi.calendar.getDate(getWorld()));
uuidMap.put(data.getID(), data);
matchUUIDWithMineID(data.getID());
PacketHandler.sendToEveryone(new PacketNewTown(data, townIDs.get(data.getID()))); //Sync to everyone on this dimension
data.getQuests().sync(null);
markDirty();
return data;
}
/* ############# Saving ################*/
public void markDirty() {
data.markDirty();
}
public void readFromNBT(NBTTagCompound nbt) {
NBTTagList dimensionTowns = nbt.getTagList("Towns", 10);
for (int j = 0; j < dimensionTowns.tagCount(); j++) {
NBTTagCompound tag = dimensionTowns.getCompoundTagAt(j);
TownDataServer theData = new TownDataServer();
theData.readFromNBT(tag);
if (theData.getTownCentre().getY() > 0) {
uuidMap.put(theData.getID(), theData);
}
}
townIDs = HashBiMap.create();
NBTTagList ids = nbt.getTagList("IDs", 10);
for (int j = 0; j < ids.tagCount(); j++) {
NBTTagCompound tag = ids.getCompoundTagAt(j);
int id = tag.getInteger("ID");
UUID uuid = UUID.fromString(tag.getString("UUID"));
townIDs.put(uuid, id);
}
}
public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
NBTTagList town_list = new NBTTagList();
for (TownDataServer data: uuidMap.values()) {
NBTTagCompound townData = new NBTTagCompound();
data.writeToNBT(townData);
town_list.appendTag(townData);
}
nbt.setTag("Towns", town_list);
//Ids
NBTTagList ids = new NBTTagList();
for (Entry<UUID, Integer> entry: townIDs.entrySet()) {
NBTTagCompound tag = new NBTTagCompound();
tag.setInteger("ID", entry.getValue());
tag.setString("UUID", entry.getKey().toString());
ids.appendTag(tag);
}
nbt.setTag("IDs", ids);
return nbt;
}
}