package exnihiloadscensio.tiles;

import exnihiloadscensio.blocks.BlockInfestedLeaves;
import exnihiloadscensio.config.Config;
import exnihiloadscensio.texturing.Color;
import exnihiloadscensio.util.Util;
import lombok.Getter;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SPacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ITickable;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.biome.BiomeColorHelper;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

public class TileInfestedLeaves extends TileEntity implements ITickable
{
    private static int tileId = 0;
    
    @Getter
    private float progress = 0;
    @Getter
    private boolean hasNearbyLeaves = true;
    @Getter
    private IBlockState leafBlock = Blocks.LEAVES.getDefaultState();
    
    // Stop ALL infested leaves from updating on the same tick always - this way they're evenly spread out and not causing a spike in tick time every time they update
    // Let's hope no one gets 2 billion in their server
    private int updateIndex = tileId++ % Config.leavesUpdateFrequency;
    
    @Override
    public void update()
    {
        if (progress < 1.0F)
        {
            progress += 1.0 / Config.infestedLeavesTicks;
            markDirty();
            
            if (progress > 1.0F)
            {
                progress = 1.0F;
                
                world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3);
            }
        }
        
        // Don't update unless there's leaves nearby, or we haven't checked for leavesUpdateFrequency ticks. And only update on the server
        if (!world.isRemote && hasNearbyLeaves || world.getTotalWorldTime() % Config.leavesUpdateFrequency == updateIndex)
        {
            hasNearbyLeaves = false;
            
            for (int x = -1; x <= 1; x++)
            {
                for (int y = -1; y <= 1; y++)
                {
                    for (int z = -1; z <= 1; z++)
                    {
                        BlockPos newPos = new BlockPos(pos.add(x, y, z));
                        IBlockState state = world.getBlockState(newPos);
                        
                        if (state != null && state.getBlock() != null && (state.getBlock() == Blocks.LEAVES || state.getBlock() == Blocks.LEAVES2))
                        {
                            hasNearbyLeaves = true;
                            
                            if (world.rand.nextFloat() < Config.leavesSpreadChance)
                            {
                                BlockInfestedLeaves.infestLeafBlock(world, newPos);
                            }
                        }
                    }
                }
            }
        }
    }
    
    @Override
    @SideOnly(Side.CLIENT)
    public double getMaxRenderDistanceSquared()
    {
        return 128 * 128;
    }
    
    @SideOnly(Side.CLIENT)
    public int getColor()
    {
        if (world == null || pos == null)
        {
            return Util.whiteColor.toInt();
        }
        else
        {
            Color green = new Color(BiomeColorHelper.getFoliageColorAtPos(world, pos));
            return Color.average(green, Util.whiteColor, (float) Math.pow(progress, 2)).toInt();
        }
    }
    
    public void setProgress(float newProgress)
    {
        progress = newProgress;
        markDirty();
    }
    
    public void setLeafBlock(IBlockState block)
    {
        leafBlock = block;
        markDirty();
    }
    
    @Override
    public NBTTagCompound writeToNBT(NBTTagCompound tag)
    {
        tag.setFloat("progress", progress);
        tag.setString("leafBlock", leafBlock.getBlock().getRegistryName().toString());
        tag.setInteger("leafBlockMeta", leafBlock.getBlock().getMetaFromState(leafBlock));
        return super.writeToNBT(tag);
    }
    
    @SuppressWarnings("deprecation")
    @Override
    public void readFromNBT(NBTTagCompound tag)
    {
        progress = tag.getFloat("progress");
        
        if (tag.hasKey("leafBlock") && tag.hasKey("leafBlockMeta"))
        {
            leafBlock = Block.getBlockFromName(tag.getString("leafBlock")).getStateFromMeta(tag.getInteger("leafBlockMeta"));
        }
        else
        {
            leafBlock = Blocks.LEAVES.getDefaultState();
        }
        
        super.readFromNBT(tag);
    }
    
    @Override
    public SPacketUpdateTileEntity getUpdatePacket()
    {
        return new SPacketUpdateTileEntity(this.pos, this.getBlockMetadata(), getUpdateTag());
    }
    
    @Override
    public void onDataPacket(NetworkManager networkManager, SPacketUpdateTileEntity packet)
    {
        readFromNBT(packet.getNbtCompound());
    }
    
    @Override
    public NBTTagCompound getUpdateTag()
    {
        return writeToNBT(new NBTTagCompound());
    }
}