package blusunrize.immersiveengineering.client.models.smart;
import blusunrize.immersiveengineering.ImmersiveEngineering;
import blusunrize.immersiveengineering.api.IEProperties;
import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection;
import blusunrize.immersiveengineering.client.ClientUtils;
import blusunrize.immersiveengineering.client.models.IOBJModelCallback;
import com.google.common.collect.ImmutableSet;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nullable;
import java.util.*;
public class ConnModelReal implements IBakedModel
{
TextureAtlasSprite textureAtlasSprite = Minecraft.getMinecraft().getTextureMapBlocks()
.getAtlasSprite(ImmersiveEngineering.MODID.toLowerCase(Locale.ENGLISH) + ":blocks/wire");
public static final HashMap<Pair<Byte, ExtBlockstateAdapter>, IBakedModel> cache = new HashMap<>();
IBakedModel base;
public ConnModelReal(IBakedModel basic)
{
base = basic;
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
if(side==null&&state instanceof IExtendedBlockState)
{
IExtendedBlockState iExtendedBlockState = (IExtendedBlockState) state;
ExtBlockstateAdapter ad = new ExtBlockstateAdapter(iExtendedBlockState, null, ExtBlockstateAdapter.ONLY_OBJ_CALLBACK);
int x = 0, z = 0;
if (iExtendedBlockState.getUnlistedProperties().containsKey(IEProperties.CONNECTIONS))
{
Set<Connection> conns = iExtendedBlockState.getValue(IEProperties.CONNECTIONS);
if (conns!=null&&conns.size()>0)
{
BlockPos tmp = conns.iterator().next().start;
x = (tmp.getX()%16+16)%16;
z = (tmp.getZ()%16+16)%16;
}
}
Pair<Byte, ExtBlockstateAdapter> key = new ImmutablePair<>((byte)((x<<4)|z), ad);
IBakedModel ret = cache.get(key);
if (ret==null)
{
ret = new AssembledBakedModel(iExtendedBlockState, textureAtlasSprite, base, rand);
cache.put(key, ret);
}
return ret.getQuads(state, side, rand);
}
return base.getQuads(state, side, rand);
}
@Override
public boolean isAmbientOcclusion()
{
return false;
}
@Override
public boolean isGui3d()
{
return false;
}
@Override
public boolean isBuiltInRenderer()
{
return false;
}
@Override
public TextureAtlasSprite getParticleTexture()
{
return base.getParticleTexture();
}
@Override
public ItemCameraTransforms getItemCameraTransforms()
{
return ItemCameraTransforms.DEFAULT;
}
@Override
public ItemOverrideList getOverrides()
{
return ItemOverrideList.NONE;
}
public class AssembledBakedModel implements IBakedModel
{
IBakedModel basic;
IExtendedBlockState extendedState;
List<BakedQuad>[] lists;
TextureAtlasSprite texture;
public AssembledBakedModel(IExtendedBlockState iExtendedBlockState, TextureAtlasSprite tex, IBakedModel b, long posRand)
{
basic = b;
extendedState = iExtendedBlockState;
texture = tex;
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
BlockRenderLayer layer = MinecraftForgeClient.getRenderLayer();
if (layer != BlockRenderLayer.SOLID&&layer!=BlockRenderLayer.TRANSLUCENT)
return basic.getQuads(state, side, rand);
if(lists==null)
lists = ClientUtils.convertConnectionFromBlockstate(extendedState, texture);
List<BakedQuad> l = new ArrayList<>(lists[layer==BlockRenderLayer.SOLID?0:1]);
l.addAll(basic.getQuads(state, side, rand));
return Collections.synchronizedList(l);
}
@Override
public boolean isAmbientOcclusion()
{
return false;
}
@Override
public boolean isGui3d()
{
return false;
}
@Override
public boolean isBuiltInRenderer()
{
return false;
}
@Override
public TextureAtlasSprite getParticleTexture()
{
return base.getParticleTexture();
}
@Override
public ItemCameraTransforms getItemCameraTransforms()
{
return ItemCameraTransforms.DEFAULT;
}
@Override
public ItemOverrideList getOverrides()
{
return ItemOverrideList.NONE;
}
}
public static class ExtBlockstateAdapter
{
public static final Set<Object> ONLY_OBJ_CALLBACK = ImmutableSet.of(IOBJModelCallback.PROPERTY);
public static final Set<Object> CONNS_OBJ_CALLBACK = ImmutableSet.of(IOBJModelCallback.PROPERTY, IEProperties.CONNECTIONS);
final IExtendedBlockState state;
final BlockRenderLayer layer;
final String extraCacheKey;
final Set<Object> ignoredProperties;
Object[] additionalProperties = null;
public ExtBlockstateAdapter(IExtendedBlockState s, BlockRenderLayer l, Set<Object> ignored)
{
state = s;
layer = l;
ignoredProperties = ignored;
if (s.getUnlistedNames().contains(IOBJModelCallback.PROPERTY))
{
IOBJModelCallback callback = s.getValue(IOBJModelCallback.PROPERTY);
if (callback!=null)
extraCacheKey = callback.getClass()+";"+callback.getCacheKey(state);
else
extraCacheKey = null;
}
else
extraCacheKey = null;
}
public ExtBlockstateAdapter(IExtendedBlockState s, BlockRenderLayer l, Set<Object> ignored, Object[] additional)
{
this(s, l, ignored);
additionalProperties = additional;
}
@Override
public boolean equals(Object obj)
{
if (obj == this)
return true;
if (!(obj instanceof ExtBlockstateAdapter))
return false;
ExtBlockstateAdapter o = (ExtBlockstateAdapter) obj;
if (o.layer!=layer)
return false;
if (extraCacheKey==null^o.extraCacheKey==null)
return false;
if (extraCacheKey!=null&&!extraCacheKey.equals(o.extraCacheKey))
return false;
if (!Arrays.equals(additionalProperties, o.additionalProperties))
return false;
for(IProperty<?> i : state.getPropertyNames())
{
if(!o.state.getProperties().containsKey(i))
return false;
if (ignoredProperties.contains(i))
continue;
Object valThis = state.getValue(i);
Object valOther = o.state.getValue(i);
if(valThis==null&&valOther==null)
continue;
else if(valOther == null || !valOther.equals(state.getValue(i)))
return false;
}
for(IUnlistedProperty<?> i : state.getUnlistedNames())
{
if(!o.state.getUnlistedProperties().containsKey(i))
return false;
if (ignoredProperties.contains(i))
continue;
Object valThis = state.getValue(i);
Object valOther = o.state.getValue(i);
if(valThis==null&&valOther==null)
continue;
else if (valOther == null || !valOther.equals(valThis))
return false;
}
return true;
}
@Override
public int hashCode()
{
int val = layer==null?0:layer.ordinal();
final int prime = 31;
if (extraCacheKey!=null)
val = val*prime+extraCacheKey.hashCode();
for (IProperty<?> n : state.getPropertyNames())
if (!ignoredProperties.contains(n))
{
Object o = state.getValue(n);
val = prime * val + (o == null ? 0 : o.hashCode());
}
for (IUnlistedProperty<?> n : state.getUnlistedNames())
if (!ignoredProperties.contains(n))
{
Object o = state.getValue(n);
val = prime * val + (o == null ? 0 : o.hashCode());
}
val = prime*val+Arrays.hashCode(additionalProperties);
return val;
}
}
}