// Path: src/main/java/com/latenighters/runicarcana/RunicArcana.java
// @Mod("runicarcana")
// public class RunicArcana
// {
//     // Directly reference a log4j logger.
//     private static final Logger LOGGER = LogManager.getLogger();
//     public static final String MODID = "runicarcana";
// 
//     //Capability Registration
//     @CapabilityInject(ISymbolHandler.class)
//     public static Capability<ISymbolHandler> SYMBOL_CAP = null;
// 
//     @CapabilityInject(IArcanaHandler.class)
//     public static Capability<IArcanaHandler> ARCANA_CAP = null;
// 
//     public static IProxy proxy;
// 
//     public RunicArcana() {
//         // Register the setup method for modloading
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetupComplete);
//         // Register the enqueueIMC method for modloading
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC);
//         // Register the processIMC method for modloading
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC);
//         // Register the doClientStuff method for modloading
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff);
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doServerStuff);
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(SymbolRegistryHandler::onCreateRegistryEvent);
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(SymbolRegistration::registerSymbols);
// 
//         // Register ourselves for server and other game events we are interested in
//         MinecraftForge.EVENT_BUS.register(this);
//         MinecraftForge.EVENT_BUS.register(new CommonEventHandler());
//         MinecraftForge.EVENT_BUS.register(new SymbolSyncer());
//         NetworkSync.registerPackets();
//         ClickableHandler.registerPackets();
//         Registration.init();
//         //SymbolRegistration.init();
// 
//     }
// 
//     private void setup(final FMLCommonSetupEvent event)
//     {
//         SymbolHandlerStorage storage = new SymbolHandlerStorage();
//         SymbolHandler.SymbolHandlerFactory factory = new SymbolHandler.SymbolHandlerFactory();
//         CapabilityManager.INSTANCE.register(ISymbolHandler.class, storage, factory);
// 
//         ArcanaHandlerStorage storage2 = new ArcanaHandlerStorage();
//         ArcanaHandler.ArcanaHandlerFactory factory2 = new ArcanaHandler.ArcanaHandlerFactory();
//         CapabilityManager.INSTANCE.register(IArcanaHandler.class, storage2, factory2);
// 
//         //TODO: register packets somewhere reasonable
//         registerPackets();
//     }
// 
//     private void doClientStuff(final FMLClientSetupEvent event) {
//         proxy = new ClientProxy();
//         KeyEventHandler.registerKeyBindings();
//     }
// 
//     private void doServerStuff(final FMLDedicatedServerSetupEvent event) {
//          proxy = new ServerProxy();
//     }
// 
//     private void enqueueIMC(final InterModEnqueueEvent event)
//     {
//         // some example code to dispatch IMC to another mod
// //        InterModComms.sendTo("examplemod", "helloworld", () -> { LOGGER.info("Hello world from the MDK"); return "Hello world";});
//         InterModComms.sendTo("curios", CuriosAPI.IMC.REGISTER_TYPE, () -> new CurioIMCMessage("ring").setSize(2));
//         InterModComms.sendTo("curios", CuriosAPI.IMC.REGISTER_TYPE, () -> new CurioIMCMessage("trinket").setSize(2));
// 
//     }
// 
//     private void processIMC(final InterModProcessEvent event)
//     {
//         // some example code to receive and process InterModComms from other mods
// //        LOGGER.info("Got IMC {}", event.getIMCStream().
// //                map(m->m.getMessageSupplier().get()).
// //                collect(Collectors.toList()));
//     }
// 
//     private void onSetupComplete(final FMLLoadCompleteEvent event)
//     {
//         PrincipicArmorEventHandler.onSetup(event);
//         SymbolCategory.generateCategories();  //TODO remove this when static initialization is working
//     }
// 
//     // You can use SubscribeEvent and let the Event Bus discover methods to call
//     @SubscribeEvent
//     public void onServerStarting(FMLServerStartingEvent event) {
//         // do something when the server starts
// //        LOGGER.info("HELLO from server starting");
//     }
// 
//     // You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD
//     // Event bus for receiving Registry Events)
//     @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
//     public static class RegistryEvents {
//         @SubscribeEvent
//         public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent) {
//             // register a new block here
// //            LOGGER.info("HELLO from Register Block");
//         }
//     }
// }


import com.latenighters.runicarcana.RunicArcana;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Tuple;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull;
import java.util.HashSet;
import java.util.Set;
    public void tick() {
        if (this.canExport() && this.getExportRate() > 0) {
            upstreamLinks.forEach(link -> {

                TileEntity tileTo = this.world.getTileEntity(link.getB().getB());
                if(tileTo instanceof ArcanaMachine)
                    this.getChamberFromSlot(link.getA()).transferToArcanaChamber(((ArcanaMachine) tileTo)
                            .getChamberFromSlot(link.getB().getA()),this.getExportRate());
                else
                    upstreamLinks.remove(link);
            });
        }
    }

    public void clearLinks()
    {
        this.upstreamLinks.clear();
        this.downstreamLinks.clear();
    }

    public void addExportLink(int localSlot, BlockPos blockPos, int machineSlot){
        this.upstreamLinks.add(new Tuple<>(localSlot, new Tuple<>(machineSlot, blockPos)));
    }

    public abstract int getChamberSlotFromHitVec(Vec3d hitVec);
    public abstract ArcanaChamber getChamberFromSlot(int slot);


    public ArcanaMix addMix(ArcanaChamber chamber, ArcanaMix mix)
    {
// Path: src/main/java/com/latenighters/runicarcana/RunicArcana.java
// @Mod("runicarcana")
// public class RunicArcana
// {
//     // Directly reference a log4j logger.
//     private static final Logger LOGGER = LogManager.getLogger();
//     public static final String MODID = "runicarcana";
// 
//     //Capability Registration
//     @CapabilityInject(ISymbolHandler.class)
//     public static Capability<ISymbolHandler> SYMBOL_CAP = null;
// 
//     @CapabilityInject(IArcanaHandler.class)
//     public static Capability<IArcanaHandler> ARCANA_CAP = null;
// 
//     public static IProxy proxy;
// 
//     public RunicArcana() {
//         // Register the setup method for modloading
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetupComplete);
//         // Register the enqueueIMC method for modloading
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC);
//         // Register the processIMC method for modloading
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC);
//         // Register the doClientStuff method for modloading
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff);
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doServerStuff);
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(SymbolRegistryHandler::onCreateRegistryEvent);
//         FMLJavaModLoadingContext.get().getModEventBus().addListener(SymbolRegistration::registerSymbols);
// 
//         // Register ourselves for server and other game events we are interested in
//         MinecraftForge.EVENT_BUS.register(this);
//         MinecraftForge.EVENT_BUS.register(new CommonEventHandler());
//         MinecraftForge.EVENT_BUS.register(new SymbolSyncer());
//         NetworkSync.registerPackets();
//         ClickableHandler.registerPackets();
//         Registration.init();
//         //SymbolRegistration.init();
// 
//     }
// 
//     private void setup(final FMLCommonSetupEvent event)
//     {
//         SymbolHandlerStorage storage = new SymbolHandlerStorage();
//         SymbolHandler.SymbolHandlerFactory factory = new SymbolHandler.SymbolHandlerFactory();
//         CapabilityManager.INSTANCE.register(ISymbolHandler.class, storage, factory);
// 
//         ArcanaHandlerStorage storage2 = new ArcanaHandlerStorage();
//         ArcanaHandler.ArcanaHandlerFactory factory2 = new ArcanaHandler.ArcanaHandlerFactory();
//         CapabilityManager.INSTANCE.register(IArcanaHandler.class, storage2, factory2);
// 
//         //TODO: register packets somewhere reasonable
//         registerPackets();
//     }
// 
//     private void doClientStuff(final FMLClientSetupEvent event) {
//         proxy = new ClientProxy();
//         KeyEventHandler.registerKeyBindings();
//     }
// 
//     private void doServerStuff(final FMLDedicatedServerSetupEvent event) {
//          proxy = new ServerProxy();
//     }
// 
//     private void enqueueIMC(final InterModEnqueueEvent event)
//     {
//         // some example code to dispatch IMC to another mod
// //        InterModComms.sendTo("examplemod", "helloworld", () -> { LOGGER.info("Hello world from the MDK"); return "Hello world";});
//         InterModComms.sendTo("curios", CuriosAPI.IMC.REGISTER_TYPE, () -> new CurioIMCMessage("ring").setSize(2));
//         InterModComms.sendTo("curios", CuriosAPI.IMC.REGISTER_TYPE, () -> new CurioIMCMessage("trinket").setSize(2));
// 
//     }
// 
//     private void processIMC(final InterModProcessEvent event)
//     {
//         // some example code to receive and process InterModComms from other mods
// //        LOGGER.info("Got IMC {}", event.getIMCStream().
// //                map(m->m.getMessageSupplier().get()).
// //                collect(Collectors.toList()));
//     }
// 
//     private void onSetupComplete(final FMLLoadCompleteEvent event)
//     {
//         PrincipicArmorEventHandler.onSetup(event);
//         SymbolCategory.generateCategories();  //TODO remove this when static initialization is working
//     }
// 
//     // You can use SubscribeEvent and let the Event Bus discover methods to call
//     @SubscribeEvent
//     public void onServerStarting(FMLServerStartingEvent event) {
//         // do something when the server starts
// //        LOGGER.info("HELLO from server starting");
//     }
// 
//     // You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD
//     // Event bus for receiving Registry Events)
//     @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
//     public static class RegistryEvents {
//         @SubscribeEvent
//         public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent) {
//             // register a new block here
// //            LOGGER.info("HELLO from Register Block");
//         }
//     }
// }


// Path: src/main/java/com/latenighters/runicarcana/common/arcana/ArcanaMachine.java
import com.latenighters.runicarcana.RunicArcana;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Tuple;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull;
import java.util.HashSet;
import java.util.Set;

    public void tick() {
        if (this.canExport() && this.getExportRate() > 0) {
            upstreamLinks.forEach(link -> {

                TileEntity tileTo = this.world.getTileEntity(link.getB().getB());
                if(tileTo instanceof ArcanaMachine)
                    this.getChamberFromSlot(link.getA()).transferToArcanaChamber(((ArcanaMachine) tileTo)
                            .getChamberFromSlot(link.getB().getA()),this.getExportRate());
                else
                    upstreamLinks.remove(link);
            });
        }
    }

    public void clearLinks()
    {
        this.upstreamLinks.clear();
        this.downstreamLinks.clear();
    }

    public void addExportLink(int localSlot, BlockPos blockPos, int machineSlot){
        this.upstreamLinks.add(new Tuple<>(localSlot, new Tuple<>(machineSlot, blockPos)));
    }

    public abstract int getChamberSlotFromHitVec(Vec3d hitVec);
    public abstract ArcanaChamber getChamberFromSlot(int slot);


    public ArcanaMix addMix(ArcanaChamber chamber, ArcanaMix mix)
    {
        this.getCapability(RunicArcana.ARCANA_CAP).ifPresent(cap->{