/*
* CraftBook Copyright (C) 2010-2017 sk89q <http://www.sk89q.com>
* CraftBook Copyright (C) 2011-2017 me4502 <http://www.me4502.com>
* CraftBook Copyright (C) Contributors
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not,
* see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.craftbook.sponge.mechanics.pipe;
import com.me4502.modularframework.module.Module;
import com.sk89q.craftbook.sponge.CraftBookPlugin;
import com.sk89q.craftbook.sponge.mechanics.pipe.parts.InputPipePart;
import com.sk89q.craftbook.sponge.mechanics.pipe.parts.OutputPipePart;
import com.sk89q.craftbook.sponge.mechanics.pipe.parts.PassthroughPipePart;
import com.sk89q.craftbook.sponge.mechanics.pipe.parts.PipePart;
import com.sk89q.craftbook.sponge.mechanics.types.SpongeBlockMechanic;
import com.sk89q.craftbook.sponge.util.BlockUtil;
import com.sk89q.craftbook.sponge.util.LocationUtil;
import org.spongepowered.api.block.BlockTypes;
import org.spongepowered.api.data.key.Keys;
import org.spongepowered.api.data.property.block.PoweredProperty;
import org.spongepowered.api.entity.EntityTypes;
import org.spongepowered.api.entity.Item;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.block.NotifyNeighborBlockEvent;
import org.spongepowered.api.event.cause.Cause;
import org.spongepowered.api.event.cause.NamedCause;
import org.spongepowered.api.event.filter.cause.First;
import org.spongepowered.api.item.inventory.Inventory;
import org.spongepowered.api.item.inventory.ItemStack;
import org.spongepowered.api.item.inventory.ItemStackSnapshot;
import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult;
import org.spongepowered.api.util.Direction;
import org.spongepowered.api.world.LocatableBlock;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@Module(id = "pipes", name = "Pipes", onEnable="onInitialize", onDisable="onDisable")
public class Pipes extends SpongeBlockMechanic {
private PipePart[] pipeParts;
@Override
public void onInitialize() {
List<PipePart> pipePartList = new ArrayList<>();
pipePartList.add(new PassthroughPipePart());
pipePartList.add(new InputPipePart());
pipePartList.add(new OutputPipePart());
pipeParts = pipePartList.toArray(new PipePart[pipePartList.size()]);
}
@Listener
public void onBlockUpdate(NotifyNeighborBlockEvent event, @First LocatableBlock source) {
if(isValid(source.getLocation())) {
PoweredProperty poweredProperty = source.getLocation().getProperty(PoweredProperty.class).orElse(null);
if(poweredProperty.getValue() != null && poweredProperty.getValue())
performPipeAction(source.getLocation());
}
}
private void performPipeAction(Location<World> location) {
Direction direction = location.get(Keys.DIRECTION).get();
Location<World> inventorySource = location.getRelative(direction);
//Let's try and find a source of items!
Optional<Inventory> inventory = LocationUtil.getInventoryForLocation(inventorySource);
if(inventory.isPresent()) {
Optional<ItemStack> itemStackOptional = inventory.get().poll(1);
if(itemStackOptional.isPresent()) {
ItemStack itemStack = itemStackOptional.get();
Set<Location> traversed = new HashSet<>();
try {
itemStack = doPipeIteration(location, itemStack, direction, traversed);
} catch(StackOverflowError e) {
CraftBookPlugin.spongeInst().getLogger().error("Pipe overflow. Please report this issue to the developers with images of setup.", e);
}
if(itemStack.getQuantity() > 0) {
for(ItemStackSnapshot snapshot : inventory.get().offer(itemStack).getRejectedItems()) {
Item item = (Item) location.getExtent().createEntity(EntityTypes.ITEM, location.getPosition());
item.offer(Keys.REPRESENTED_ITEM, snapshot);
location.getExtent().spawnEntity(item, Cause.of(NamedCause.source(location)));
}
}
}
}
}
private ItemStack doPipeIteration(Location<World> location, ItemStack itemStack, Direction fromDirection, Set<Location> traversed) {
if(traversed.contains(location))
return itemStack;
traversed.add(location);
if(itemStack.getQuantity() == 0)
return itemStack;
PipePart pipePart = getPipePart(location);
if(pipePart == null)
return itemStack;
for(Location<World> location1 : pipePart.findValidOutputs(location, itemStack, fromDirection)) {
if(pipePart instanceof OutputPipePart) {
Optional<Inventory> inventory = LocationUtil.getInventoryForLocation(location1);
if(inventory.isPresent()) {
InventoryTransactionResult result = inventory.get().offer(itemStack);
if(!result.getRejectedItems().isEmpty()) {
for (ItemStackSnapshot snapshot : result.getRejectedItems()) {
itemStack = snapshot.createStack();
}
} else {
itemStack.setQuantity(0);
}
}
} else
itemStack = doPipeIteration(location1, itemStack, BlockUtil.getFacing(location, location1), traversed);
}
return itemStack;
}
private PipePart getPipePart(Location location) {
for(PipePart pipePart : pipeParts)
if(pipePart.isValid(location.getBlock()))
return pipePart;
return null;
}
@Override
public boolean isValid(Location<World> location) {
return location.getBlockType() == BlockTypes.STICKY_PISTON;
}
}