package bartworks.API;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockAdder;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockAnyMeta;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlocksTiered;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.minecraft.block.Block;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Table;
import com.gtnewhorizon.structurelib.structure.IStructureElement;
import bartworks.common.loaders.ItemRegistry;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.LoaderState;
/**
* API for bartworks borosilicate glass.
*
* You might have noticed this API does not expose any Block instance, but only IStructureElements. This is in case we
* add more glass blocks later, and we run out of meta id for only one block.
*
* IStructureElements returned from this class SHOULD NOT have its methods called before post init, or else you
* might end up with wrong autoplace hints.
*/
public class BorosilicateGlass {
private static List> representatives;
private static SetMultimap> allLevels;
private static final Table allLevelsReverse = HashBasedTable.create();
private static boolean isValidTier(int tier) {
return tier > 0 && tier <= Byte.MAX_VALUE;
}
public static Block getGlassBlock() {
return ItemRegistry.bw_realglas;
}
public static Block getGlassBlock2() {
return ItemRegistry.bw_realglas2;
}
private static void doRegister(byte level, Block block, int meta,
SetMultimap> allLevels) {
allLevels.put(level, Pair.of(block, meta));
allLevelsReverse.put(block, meta, level);
}
private static SetMultimap> getAllLevels() {
if (allLevels == null) {
SetMultimap> ret = LinkedHashMultimap.create();
Block block = getGlassBlock();
doRegister((byte) 3, block, 0, ret);
doRegister((byte) 4, block, 1, ret);
doRegister((byte) 5, block, 12, ret);
doRegister((byte) 5, block, 2, ret);
doRegister((byte) 6, block, 3, ret);
doRegister((byte) 7, block, 4, ret);
doRegister((byte) 8, block, 5, ret);
for (int i = 6; i < 12; i++) {
doRegister((byte) 3, block, i, ret);
}
doRegister((byte) 9, block, 13, ret);
doRegister((byte) 10, block, 14, ret);
doRegister((byte) 11, block, 15, ret);
block = getGlassBlock2();
doRegister((byte) 12, block, 0, ret);
allLevels = ret;
}
return allLevels;
}
private static List> getRepresentatives() {
if (representatives == null) {
SetMultimap> allLevels = getAllLevels();
ArrayList> ret = new ArrayList<>();
for (Byte level : new PriorityQueue<>(allLevels.keySet())) {
ret.add(
allLevels.get(level)
.iterator()
.next());
}
representatives = ret;
}
return representatives;
}
private static Byte checkWithinBound(byte val, byte lo, byte hi) {
return val > hi || val < lo ? null : val;
}
/**
* ONLY registers borosilicate glass. Without this, {@link #getTier} won't work properly in environments that don't
* have the coremod.
*/
public static void registerBorosilicateGlass() {
getAllLevels();
}
/**
* Register a new block as valid borosilicate glass with given tier (even if it doesn't contain boron at all)
*
* Does not support matching by more complex stuff like tile entity!
*
* Can only be called at INIT stage.
*/
public static void registerGlass(Block block, int meta, byte tier) {
if (Loader.instance()
.hasReachedState(LoaderState.POSTINITIALIZATION)) throw new IllegalStateException("register too late!");
if (!Loader.instance()
.hasReachedState(LoaderState.INITIALIZATION)) throw new IllegalStateException("register too early!");
if (!isValidTier(tier)) throw new IllegalArgumentException("not a valid tier: " + tier);
doRegister(tier, block, meta, getAllLevels());
}
/**
* Check if there is at least one type of boroglass in that tier.
*/
public static boolean hasGlassInTier(int tier) {
return getAllLevels().containsKey((byte) tier);
}
/**
* Get a structure element for a certain tier of borosilicate glass. DOES NOT accept other glass like
* reinforced glass, magic mirror, vanilla glass, etc. unless these glass are explicitly registered as a
* borosilicate glass.
*
* Use this if you just want boroglass here and doesn't care what tier it is.
*/
public static IStructureElement ofBoroGlass(int tier) {
if (!hasGlassInTier(tier)) throw new IllegalArgumentException();
return lazy(t -> {
Pair pair = getRepresentatives().get(tier - 3);
return ofBlockAdder((t1, block1, meta) -> getTier(block1, meta) == tier, pair.getKey(), pair.getValue());
});
}
/**
* Get a structure element for any kind of borosilicate glass. DOES NOT accept other glass like reinforced
* glass, magic mirror, vanilla glass, etc. unless these glass are explicitly registered as a borosilicate glass.
*
* Use this if you just want boroglass here and doesn't care what tier it is.
*/
public static IStructureElement ofBoroGlassAnyTier() {
return lazy(t -> ofBlockAnyMeta(getGlassBlock()));
}
/**
* Get a structure element for borosilicate glass. DOES NOT accept other glass like reinforced glass, magic
* mirror, vanilla glass, etc. unless these glass are explicitly registered as a borosilicate glass.
*
* This assumes you want all glass used to be of the same tier.
*
* NOTE: This will accept the basic boron glass (HV tier) as well. You might not want this. Use the other overload
* to filter this out.
*
* @param initialValue the value set before structure check started
*/
public static IStructureElement ofBoroGlass(byte initialValue, BiConsumer setter,
Function getter) {
return lazy(
t -> ofBlocksTiered(BorosilicateGlass::getTier, getRepresentatives(), initialValue, setter, getter));
}
/**
* Get a structure element for borosilicate glass. DOES NOT accept other glass like reinforced glass, magic
* mirror, vanilla glass, etc. unless these glass are explicitly registered as a borosilicate glass.
*
* @param initialValue the value set before structure check started
* @param minTier minimal accepted tier. inclusive. must be greater than 0.
* @param maxTier maximal accepted tier. inclusive.
*/
public static IStructureElement ofBoroGlass(byte initialValue, byte minTier, byte maxTier,
BiConsumer setter, Function getter) {
if (minTier > maxTier || minTier < 0) throw new IllegalArgumentException();
return lazy(
t -> ofBlocksTiered(
(block1, meta) -> checkWithinBound(getTier(block1, meta), minTier, maxTier),
getRepresentatives().stream()
.skip(Math.max(minTier - 3, 0))
.limit(maxTier - minTier + 1)
.collect(Collectors.toList()),
initialValue,
setter,
getter));
}
/**
* Get the tier of this borosilicate glass. DOES NOT consider other glass like reinforced glass, magic
* mirror, vanilla glass, etc. unless these glass are explicitly registered as a borosilicate glass.
*
* @return glass tier, or -1 if is not a borosilicate glass
*/
public static byte getTier(Block block, int meta) {
Byte ret = allLevelsReverse.get(block, meta);
return ret == null ? -1 : ret;
}
}