<commit_before>package com.creativemd.littletiles.common.util.place;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.function.Predicate;

import com.creativemd.creativecore.common.utils.type.HashMapList;
import com.creativemd.littletiles.LittleTiles;
import com.creativemd.littletiles.LittleTilesConfig.NotAllowedToPlaceException;
import com.creativemd.littletiles.common.action.LittleAction;
import com.creativemd.littletiles.common.action.LittleActionException;
import com.creativemd.littletiles.common.block.BlockTile;
import com.creativemd.littletiles.common.structure.LittleStructure;
import com.creativemd.littletiles.common.tile.LittleTile;
import com.creativemd.littletiles.common.tile.math.vec.LittleVec;
import com.creativemd.littletiles.common.tile.place.PlacePreview;
import com.creativemd.littletiles.common.tile.preview.LittlePreview;
import com.creativemd.littletiles.common.tile.preview.LittlePreviews;
import com.creativemd.littletiles.common.tileentity.TileEntityLittleTiles;
import com.creativemd.littletiles.common.util.grid.IGridBased;
import com.creativemd.littletiles.common.util.grid.LittleGridContext;

import net.minecraft.block.SoundType;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.world.BlockEvent.MultiPlaceEvent;

public class Placement {
	
	public final EntityPlayer player;
	public final World world;
	public final PlacementMode mode;
	public final BlockPos pos;
	public final EnumFacing facing;
	public final LittlePreviews previews;
	public final LinkedHashMap<BlockPos, PlacementBlock> blocks = new LinkedHashMap<>();
	public final PlacementStructurePreview origin;
	public final List<PlacementStructurePreview> structures = new ArrayList<>();
	
	public final List<LittleTile> removedTiles = new ArrayList<>();
	public final List<LittleTile> unplaceableTiles = new ArrayList<>();
	public final List<SoundType> soundsToBePlayed = new ArrayList<>();
	
	protected ItemStack stack;
	protected boolean ignoreWorldBoundaries = true;
	protected Predicate<LittleTile> predicate;
	
	public Placement(EntityPlayer player, PlacementPreview preview) {
		this.player = player;
		this.world = preview.world;
		this.mode = preview.mode;
		this.pos = preview.pos;
		this.facing = preview.facing;
		this.previews = preview.previews;
		this.origin = createStructureTree(null, preview.previews);
		
		createPreviews(origin, preview.inBlockOffset, preview.pos);
		
		for (PlacementBlock block : blocks.values())
			block.convertToSmallest();
	}
	
	public Placement setIgnoreWorldBoundaries(boolean value) {
		this.ignoreWorldBoundaries = value;
		return this;
	}
	
	public Placement setPredicate(Predicate<LittleTile> predicate) {
		this.predicate = predicate;
		return this;
	}
	
	public Placement setStack(ItemStack stack) {
		this.stack = stack;
		return this;
	}
	
	public boolean canPlace() {
		for (BlockPos pos : blocks.keySet()) {
			if (!LittleAction.isAllowedToInteract(world, player, pos, true, EnumFacing.EAST)) {
				LittleAction.sendBlockResetToClient(world, (EntityPlayerMP) player, pos);
				return false;
			}
		}
		
		List<BlockPos> coordsToCheck = mode.getCoordsToCheck(blocks.keySet(), pos);
		if (coordsToCheck != null) {
			for (BlockPos pos : coordsToCheck) {
				PlacementBlock block = blocks.get(pos);
				
				if (block == null)
					continue;
				
			}
		}
		return true;
	}
	
	public PlacementResult place() throws LittleActionException {
		if (player != null && !world.isRemote) {
			if (player != null) {
				if (LittleTiles.CONFIG.isPlaceLimited(player) && previews.getVolumeIncludingChildren() > LittleTiles.CONFIG.survival.maxPlaceBlocks)
					throw new NotAllowedToPlaceException();
				
				if (LittleTiles.CONFIG.isTransparencyRestricted(player))
					for (LittlePreview preview : previews)
						LittleAction.isAllowedToPlacePreview(player, preview);
			}
			
			List<BlockSnapshot> snaps = new ArrayList<>();
			for (BlockPos snapPos : blocks.keySet())
				snaps.add(new BlockSnapshot(world, snapPos, BlockTile.getState(false, false)));
			
			MultiPlaceEvent event = new MultiPlaceEvent(snaps, world.getBlockState(facing == null ? pos : pos.offset(facing)), player, EnumHand.MAIN_HAND);
			MinecraftForge.EVENT_BUS.post(event);
			if (event.isCanceled()) {
				for (BlockPos snapPos : blocks.keySet())
					LittleAction.sendBlockResetToClient(world, (EntityPlayerMP) player, pos);
				return null;
			}
		}
		if (canPlace())
			return placeTiles();
		return null;
	}
	
	public PlacementResult tryPlace() {
		try {
			return place();
		} catch (LittleActionException e) {
			return null;
		}
	}
	
	protected PlacementResult placeTiles() throws LittleActionException {
		PlacementResult result = new PlacementResult(pos, origin.structure);
		
		for (PlacementBlock block : blocks.values())
			block.place(result);
		
		HashSet<BlockPos> blocksToUpdate = new HashSet<>(blocks.keySet());
		
		for (Iterator iterator = blocks.values().iterator(); iterator.hasNext();) {
			PlacementBlock block = (PlacementBlock) iterator.next();
			if (block.combineTilesSecretly()) {
				result.tileEntities.remove(block.cached);
				iterator.remove();
			}
		}
		
		for (PlacementBlock block : blocks.values())
			block.placeLate();
		
		if (origin.structure != null) {
			if (origin.structure.getMainTile() == null)
				throw new LittleActionException("Missing maintile of structure. Placed " + result.placedPreviews.size() + " tile(s).");
			notifyStructurePlaced();
		}
		
		constructStructureRelations();
		
		HashSet<BlockPos> blocksToNotify = new HashSet<>();
		for (BlockPos pos : blocksToUpdate) {
			for (int i = 0; i < 6; i++) {
				BlockPos neighbour = pos.offset(EnumFacing.VALUES[i]);
				if (!blocksToNotify.contains(neighbour) && !blocksToUpdate.contains(neighbour))
					blocksToNotify.add(neighbour);
			}
			
			TileEntity te = world.getTileEntity(pos);
			if (te instanceof TileEntityLittleTiles)
				((TileEntityLittleTiles) te).updateTiles(false);
		}
		
		for (BlockPos pos : blocksToNotify)
			world.neighborChanged(pos, LittleTiles.blockTileNoTicking, this.pos);
		
		for (int i = 0; i < soundsToBePlayed.size(); i++)
			world.playSound((EntityPlayer) null, pos, soundsToBePlayed.get(i).getPlaceSound(), SoundCategory.BLOCKS, (soundsToBePlayed.get(i).getVolume() + 1.0F) / 2.0F, soundsToBePlayed.get(i).getPitch() * 0.8F);
		
		return result;
	}
	
	public void notifyStructurePlaced() {
		origin.structure.placedStructure(stack);
	}
	
	public void constructStructureRelations() {
		updateRelations(origin);
	}
	
	private void updateRelations(PlacementStructurePreview preview) {
		for (int i = 0; i < preview.children.size(); i++) {
			PlacementStructurePreview child = preview.children.get(i);
			preview.structure.updateChildConnection(i, child.structure);
			child.structure.updateParentConnection(i, preview.structure);
			
			updateRelations(child);
		}
	}
	
	public PlacementBlock getorCreateBlock(BlockPos pos) {
		PlacementBlock block = blocks.get(pos);
		if (block == null) {
			block = new PlacementBlock();
			blocks.put(pos, block);
		}
		return block;
	}
	
	private PlacementStructurePreview createStructureTree(PlacementStructurePreview parent, LittlePreviews previews) {
		PlacementStructurePreview structure = new PlacementStructurePreview(parent, previews.createStructure(), previews instanceof LittlePreviews ? (LittlePreviews) previews : null);
		
		for (LittlePreviews child : previews.getChildren())
			structure.addChild(createStructureTree(structure, child));
		
		return structure;
	}
	
	private void createPreviews(PlacementStructurePreview current, LittleVec inBlockOffset, BlockPos pos) {
		
		if (current.previews != null) {
			HashMapList<BlockPos, PlacePreview> splitted = new HashMapList<BlockPos, PlacePreview>();
			for (PlacePreview pp : current.previews.getPlacePreviews(inBlockOffset))
				pp.split(current.previews.getContext(), splitted, pos);
			
			for (Entry<BlockPos, ArrayList<PlacePreview>> entry : splitted.entrySet())
				getorCreateBlock(pos).addPlacePreviews(current.index, entry.getValue());
		}
		
		for (PlacementStructurePreview child : current.children)
			createPreviews(child, inBlockOffset, pos);
	}
	
	public class PlacementBlock implements IGridBased {
		
		private TileEntityLittleTiles cached;
		private LittleGridContext context;
		private final List<PlacePreview>[] previews;
		private final List<PlacePreview>[] latePreviews;
		
		public PlacementBlock() {
			previews = new List[structures.size()];
			latePreviews = new List[structures.size()];
		}
		
		@Override
		public LittleGridContext getContext() {
			return context;
		}
		<commit_msg>Fixed crash when placing<commit_after>package com.creativemd.littletiles.common.util.place;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.function.Predicate;

import com.creativemd.creativecore.common.utils.type.HashMapList;
import com.creativemd.littletiles.LittleTiles;
import com.creativemd.littletiles.LittleTilesConfig.NotAllowedToPlaceException;
import com.creativemd.littletiles.common.action.LittleAction;
import com.creativemd.littletiles.common.action.LittleActionException;
import com.creativemd.littletiles.common.block.BlockTile;
import com.creativemd.littletiles.common.structure.LittleStructure;
import com.creativemd.littletiles.common.tile.LittleTile;
import com.creativemd.littletiles.common.tile.math.vec.LittleVec;
import com.creativemd.littletiles.common.tile.place.PlacePreview;
import com.creativemd.littletiles.common.tile.preview.LittlePreview;
import com.creativemd.littletiles.common.tile.preview.LittlePreviews;
import com.creativemd.littletiles.common.tileentity.TileEntityLittleTiles;
import com.creativemd.littletiles.common.util.grid.IGridBased;
import com.creativemd.littletiles.common.util.grid.LittleGridContext;

import net.minecraft.block.SoundType;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.world.BlockEvent.MultiPlaceEvent;

public class Placement {
	
	public final EntityPlayer player;
	public final World world;
	public final PlacementMode mode;
	public final BlockPos pos;
	public final EnumFacing facing;
	public final LittlePreviews previews;
	public final LinkedHashMap<BlockPos, PlacementBlock> blocks = new LinkedHashMap<>();
	public final PlacementStructurePreview origin;
	public final List<PlacementStructurePreview> structures = new ArrayList<>();
	
	public final List<LittleTile> removedTiles = new ArrayList<>();
	public final List<LittleTile> unplaceableTiles = new ArrayList<>();
	public final List<SoundType> soundsToBePlayed = new ArrayList<>();
	
	protected ItemStack stack;
	protected boolean ignoreWorldBoundaries = true;
	protected Predicate<LittleTile> predicate;
	
	public Placement(EntityPlayer player, PlacementPreview preview) {
		this.player = player;
		this.world = preview.world;
		this.mode = preview.mode;
		this.pos = preview.pos;
		this.facing = preview.facing;
		this.previews = preview.previews;
		this.origin = createStructureTree(null, preview.previews);
		
		createPreviews(origin, preview.inBlockOffset, preview.pos);
		
		for (PlacementBlock block : blocks.values())
			block.convertToSmallest();
	}
	
	public Placement setIgnoreWorldBoundaries(boolean value) {
		this.ignoreWorldBoundaries = value;
		return this;
	}
	
	public Placement setPredicate(Predicate<LittleTile> predicate) {
		this.predicate = predicate;
		return this;
	}
	
	public Placement setStack(ItemStack stack) {
		this.stack = stack;
		return this;
	}
	
	public boolean canPlace() {
		for (BlockPos pos : blocks.keySet()) {
			if (!LittleAction.isAllowedToInteract(world, player, pos, true, EnumFacing.EAST)) {
				LittleAction.sendBlockResetToClient(world, (EntityPlayerMP) player, pos);
				return false;
			}
		}
		
		List<BlockPos> coordsToCheck = mode.getCoordsToCheck(blocks.keySet(), pos);
		if (coordsToCheck != null) {
			for (BlockPos pos : coordsToCheck) {
				PlacementBlock block = blocks.get(pos);
				
				if (block == null)
					continue;
				
			}
		}
		return true;
	}
	
	public PlacementResult place() throws LittleActionException {
		if (player != null && !world.isRemote) {
			if (player != null) {
				if (LittleTiles.CONFIG.isPlaceLimited(player) && previews.getVolumeIncludingChildren() > LittleTiles.CONFIG.survival.maxPlaceBlocks)
					throw new NotAllowedToPlaceException();
				
				if (LittleTiles.CONFIG.isTransparencyRestricted(player))
					for (LittlePreview preview : previews)
						LittleAction.isAllowedToPlacePreview(player, preview);
			}
			
			List<BlockSnapshot> snaps = new ArrayList<>();
			for (BlockPos snapPos : blocks.keySet())
				snaps.add(new BlockSnapshot(world, snapPos, BlockTile.getState(false, false)));
			
			MultiPlaceEvent event = new MultiPlaceEvent(snaps, world.getBlockState(facing == null ? pos : pos.offset(facing)), player, EnumHand.MAIN_HAND);
			MinecraftForge.EVENT_BUS.post(event);
			if (event.isCanceled()) {
				for (BlockPos snapPos : blocks.keySet())
					LittleAction.sendBlockResetToClient(world, (EntityPlayerMP) player, pos);
				return null;
			}
		}
		if (canPlace())
			return placeTiles();
		return null;
	}
	
	public PlacementResult tryPlace() {
		try {
			return place();
		} catch (LittleActionException e) {
			return null;
		}
	}
	
	protected PlacementResult placeTiles() throws LittleActionException {
		PlacementResult result = new PlacementResult(pos, origin.structure);
		
		for (PlacementBlock block : blocks.values())
			block.place(result);
		
		HashSet<BlockPos> blocksToUpdate = new HashSet<>(blocks.keySet());
		
		for (Iterator iterator = blocks.values().iterator(); iterator.hasNext();) {
			PlacementBlock block = (PlacementBlock) iterator.next();
			if (block.combineTilesSecretly()) {
				result.tileEntities.remove(block.cached);
				iterator.remove();
			}
		}
		
		for (PlacementBlock block : blocks.values())
			block.placeLate();
		
		if (origin.structure != null) {
			if (origin.structure.getMainTile() == null)
				throw new LittleActionException("Missing maintile of structure. Placed " + result.placedPreviews.size() + " tile(s).");
			notifyStructurePlaced();
		}
		
		constructStructureRelations();
		
		HashSet<BlockPos> blocksToNotify = new HashSet<>();
		for (BlockPos pos : blocksToUpdate) {
			for (int i = 0; i < 6; i++) {
				BlockPos neighbour = pos.offset(EnumFacing.VALUES[i]);
				if (!blocksToNotify.contains(neighbour) && !blocksToUpdate.contains(neighbour))
					blocksToNotify.add(neighbour);
			}
			
			TileEntity te = world.getTileEntity(pos);
			if (te instanceof TileEntityLittleTiles)
				((TileEntityLittleTiles) te).updateTiles(false);
		}
		
		for (BlockPos pos : blocksToNotify)
			world.neighborChanged(pos, LittleTiles.blockTileNoTicking, this.pos);
		
		for (int i = 0; i < soundsToBePlayed.size(); i++)
			world.playSound((EntityPlayer) null, pos, soundsToBePlayed.get(i).getPlaceSound(), SoundCategory.BLOCKS, (soundsToBePlayed.get(i).getVolume() + 1.0F) / 2.0F, soundsToBePlayed.get(i).getPitch() * 0.8F);
		
		return result;
	}
	
	public void notifyStructurePlaced() {
		origin.structure.placedStructure(stack);
	}
	
	public void constructStructureRelations() {
		updateRelations(origin);
	}
	
	private void updateRelations(PlacementStructurePreview preview) {
		for (int i = 0; i < preview.children.size(); i++) {
			PlacementStructurePreview child = preview.children.get(i);
			preview.structure.updateChildConnection(i, child.structure);
			child.structure.updateParentConnection(i, preview.structure);
			
			updateRelations(child);
		}
	}
	
	public PlacementBlock getorCreateBlock(BlockPos pos) {
		PlacementBlock block = blocks.get(pos);
		if (block == null) {
			block = new PlacementBlock(previews.getContext());
			blocks.put(pos, block);
		}
		return block;
	}
	
	private PlacementStructurePreview createStructureTree(PlacementStructurePreview parent, LittlePreviews previews) {
		PlacementStructurePreview structure = new PlacementStructurePreview(parent, previews.createStructure(), previews instanceof LittlePreviews ? (LittlePreviews) previews : null);
		
		for (LittlePreviews child : previews.getChildren())
			structure.addChild(createStructureTree(structure, child));
		
		return structure;
	}
	
	private void createPreviews(PlacementStructurePreview current, LittleVec inBlockOffset, BlockPos pos) {
		
		if (current.previews != null) {
			HashMapList<BlockPos, PlacePreview> splitted = new HashMapList<BlockPos, PlacePreview>();
			for (PlacePreview pp : current.previews.getPlacePreviews(inBlockOffset))
				pp.split(current.previews.getContext(), splitted, pos);
			
			for (Entry<BlockPos, ArrayList<PlacePreview>> entry : splitted.entrySet())
				getorCreateBlock(pos).addPlacePreviews(current.index, entry.getValue());
		}
		
		for (PlacementStructurePreview child : current.children)
			createPreviews(child, inBlockOffset, pos);
	}
	
	public class PlacementBlock implements IGridBased {
		
		private TileEntityLittleTiles cached;
		private LittleGridContext context;
		private final List<PlacePreview>[] previews;
		private final List<PlacePreview>[] latePreviews;
		
		public PlacementBlock(LittleGridContext context) {
			this.context = context;
			previews = new List[structures.size()];
			latePreviews = new List[structures.size()];
		}
		
		@Override
		public LittleGridContext getContext() {
			return context;
		}
		
{'fraction_non_alphanumeric': 0.10831261485244838, 'fraction_numerical': 0.0010809642200843152, 'mean_word_length': 5.028999674160964, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3635443', 'n_tokens_mistral': 6273, 'n_tokens_neox': 5501, 'n_words': 1142}