@Component(modules = {
        MobTotemsModule.class
}, dependencies = {
        MinecraftComponent.class
})
public interface MobTotemsComponent extends MinecraftComponent {

    MobTotems mobTotems();
} @Mod.EventBusSubscriber(modid = Reference.MOD_ID)
public class ModBlocks {
    public static final HashSet<ItemBlock> ITEM_BLOCKS = new HashSet<>();

    public static final String TOTEM_WOOD_NAME = "totem_wood";
    public static final String SACRED_LIGHT_NAME = "sacred_light";
    public static final String INCENSE_KINDLING_BOX_NAME = "incense_kindling_box";
    public static final String OFFERING_BOX_NAME = "offering_box";

    public static final Block TOTEM_WOOD = new TotemWoodBlock().setUnlocalizedName(TOTEM_WOOD_NAME).setRegistryName(Reference.MOD_ID, TOTEM_WOOD_NAME);
    public static final Block SACRED_LIGHT = new SacredLightBlock().setUnlocalizedName(SACRED_LIGHT_NAME).setRegistryName(Reference.MOD_ID, SACRED_LIGHT_NAME);
    public static final Block INCENSE_KINDLING_BOX = new IncenseKindlingBox().setUnlocalizedName(INCENSE_KINDLING_BOX_NAME).setRegistryName(Reference.MOD_ID, INCENSE_KINDLING_BOX_NAME);
    public static final Block OFFERING_BOX = new OfferingBox().setUnlocalizedName(OFFERING_BOX_NAME).setRegistryName(Reference.MOD_ID, OFFERING_BOX_NAME);

    public static void init() {
    }

    @SubscribeEvent
    public static void registerBlocks(RegistryEvent.Register<Block> e) {
        final IForgeRegistry<Block> registry = e.getRegistry();
        final Block[] blocks = {
                TOTEM_WOOD,
                SACRED_LIGHT,
                INCENSE_KINDLING_BOX,
                OFFERING_BOX
        };

        registry.registerAll(blocks);
    }

    @SubscribeEvent
    public static void registerItemBlocks(RegistryEvent.Register<Item> e) {
        final IForgeRegistry<Item> registry = e.getRegistry();

        final ItemBlock[] items = {
                new TotemWoodItemBlock(TOTEM_WOOD),
                new ItemBlock(SACRED_LIGHT),
                new ItemBlock(INCENSE_KINDLING_BOX),
                new ItemBlock(OFFERING_BOX),
        };

        for(final ItemBlock item : items)
        {
            final Block block = item.getBlock();
            final ResourceLocation registryName = Preconditions.checkNotNull(block.getRegistryName(), "Block %s has null registry name", block);
            registry.register(item.setRegistryName(registryName));
            ITEM_BLOCKS.add(item);
        }

        registerTileEntities();
    }

    private static void registerTileEntities()
    {
        GameRegistry.registerTileEntity(TotemTileEntity.class, Reference.RESOURCE_PREFIX + TOTEM_WOOD_NAME);
        GameRegistry.registerTileEntity(SacredLightTileEntity.class, Reference.RESOURCE_PREFIX + SACRED_LIGHT_NAME);
        GameRegistry.registerTileEntity(IncenseKindlingBoxTileEntity.class, Reference.RESOURCE_PREFIX + INCENSE_KINDLING_BOX_NAME);
        GameRegistry.registerTileEntity(OfferingBoxTileEntity.class, Reference.RESOURCE_PREFIX + OFFERING_BOX_NAME);
    }

    @SubscribeEvent
    public static void registerRenders(ModelRegistryEvent e) {
        for (int i = 0; i < TotemType.values().length; i++) {
            Item item = Item.getItemFromBlock(TOTEM_WOOD);
            ModelResourceLocation modelResourceLocation = new ModelResourceLocation(Reference.RESOURCE_PREFIX + TOTEM_WOOD_NAME, "totem_type=" + TotemType.fromMeta(i).getName());
            ModelLoader.setCustomModelResourceLocation(item, i, modelResourceLocation);
        }

        registerRender(SACRED_LIGHT, SACRED_LIGHT_NAME);
        registerRender(INCENSE_KINDLING_BOX, INCENSE_KINDLING_BOX_NAME);
        registerRender(OFFERING_BOX, OFFERING_BOX_NAME);
    }

    private static void registerRender(Block block, String key) {
        Item item = Item.getItemFromBlock(block);
        ModelLoader.setCustomModelResourceLocation(item, 0, new ModelResourceLocation(Reference.RESOURCE_PREFIX + key, "inventory"));
    }
} public class CowLogic extends TotemLogic {
    @Override
    public List<ItemStack> getCost() {
        List<ItemStack> cost = new ArrayList<>();
        cost.add(new ItemStack(Blocks.TALLGRASS, 4, 1));
        return cost;
    }

    @Nonnull
    @Override
    public EffectType getEffectType() {
        return EffectType.EFFECT;
    }

    public static class CowTotemEntityJoinWorldEvent {
        @SubscribeEvent
        public void onEntityJoinWorldEvent(EntityJoinWorldEvent e) {
            if (!e.getWorld().isRemote) {
                List<TileEntity> loadedTileEntityList = e.getWorld().loadedTileEntityList;

                for (int i = loadedTileEntityList.size() - 1; i >= 0; i--) {
                    TileEntity tileEntity = loadedTileEntityList.get(i);
                    if (tileEntity instanceof OfferingBoxTileEntity) {
                        if (((OfferingBoxTileEntity) tileEntity).getChargeLevel() > 0) {
                            if (TotemHelper.hasTotemType((OfferingBoxTileEntity) tileEntity, TotemType.COW)) {
                                Modifiers mods = TotemHelper.getModifiers(e.getWorld(), (OfferingBoxTileEntity) tileEntity);
                                int radius = TotemHelper.DEFAULT_RADIUS + (int) (TotemHelper.RANGE_BOOST * mods.range);

                                if (!canSpawnMobHere(tileEntity.getPos(), e.getEntity(), radius)) {
                                    //FMLLog.log(Level.ERROR, "Stopped mob from spawning with radius: " + radius);
                                    e.setCanceled(true);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        private boolean canSpawnMobHere(BlockPos pos, Entity mob, int radius) {
            double dist = mob.getPosition().getDistance(pos.getX(), pos.getY(), pos.getZ());

            return !(mob instanceof EntityAnimal && dist < radius);
        }
    }
} public class CreeperLogic extends TotemLogic {
    @Override
    public List<ItemStack> getCost() {
        List<ItemStack> cost = new ArrayList<>();
        cost.add(new ItemStack(Items.GUNPOWDER, 2, 0));
        return cost;
    }

    @Nonnull
    @Override
    public EffectType getEffectType() {
        return EffectType.EFFECT;
    }

    // This block limits what entities can spawn near it, we subscribe to the EntityJoinWorldEvent in order to stop entity spawning
    // based on proximity to TileEntities of our block's type
    public static class CreeperTotemEntityJoinWorldEvent {
        @SubscribeEvent
        public void onEntityJoinWorldEvent(EntityJoinWorldEvent e) {
            if (!e.getWorld().isRemote) {
                List<TileEntity> loadedTileEntityList = new ArrayList<>(e.getWorld().loadedTileEntityList);

                for (TileEntity tileEntity : loadedTileEntityList) {
                    if (tileEntity instanceof OfferingBoxTileEntity) {
                        if (((OfferingBoxTileEntity) tileEntity).getChargeLevel() > 0) {
                            if (TotemHelper.hasTotemType((OfferingBoxTileEntity) tileEntity, TotemType.CREEPER)) {
                                Modifiers mods = TotemHelper.getModifiers(e.getWorld(), (OfferingBoxTileEntity) tileEntity);
                                int radius = TotemHelper.DEFAULT_RADIUS + (int) (TotemHelper.RANGE_BOOST * mods.range);
                                if (!canSpawnMobHere(tileEntity.getPos(), e.getEntity(), radius)) {
                                    //FMLLog.log(Level.ERROR, "Stopped mob from spawning with radius: " + radius);
                                    e.setCanceled(true);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        private boolean canSpawnMobHere(BlockPos pos, Entity mob, int radius) {
            double dist = mob.getPosition().getDistance(pos.getX(), pos.getY(), pos.getZ());
            return !(mob instanceof IMob && dist < radius);
        }
    }
} public class EnderLogic extends TotemLogic {
    @Override
    public List<ItemStack> getCost() {
        List<ItemStack> cost = new ArrayList<>();
        cost.add(new ItemStack(Blocks.GRASS, 1, 0));
        return cost;
    }

    @Nonnull
    @Override
    public EffectType getEffectType() {
        return EffectType.EFFECT;
    }

    public static class EnderTotemEnderTeleportEvent {
        private static final int DEFAULT_RADIUS = 32;
        private static final int RANGE_BOOST = 16;

        @SubscribeEvent
        public void onEnderTeleportEvent(EnderTeleportEvent e) {
            World world = e.getEntityLiving().getEntityWorld();
            if (!world.isRemote) {
                List<TileEntity> loadedTileEntityList = new ArrayList<>(world.loadedTileEntityList);

                for (TileEntity tileEntity : loadedTileEntityList) {
                    if (tileEntity instanceof OfferingBoxTileEntity) {
                        if (((OfferingBoxTileEntity) tileEntity).getChargeLevel() > 0) {
                            if (TotemHelper.hasTotemType((OfferingBoxTileEntity) tileEntity, TotemType.ENDER)) {
                                Modifiers modifiers = TotemHelper.getModifiers(world, (OfferingBoxTileEntity) tileEntity);
                                int radius = DEFAULT_RADIUS + (int) (RANGE_BOOST * modifiers.range);

                                if (!isMobAffected(tileEntity.getPos(), e.getEntity(), radius)) {
                                    BlockPos validPos = getValidTeleportPosAround(world, tileEntity.getPos());
                                    if (validPos != null) {
                                        //FMLLog.log(Level.ERROR, "Redirected endermen with radius: " + radius + " to target: " + validPos);
                                        e.setTargetX(validPos.getX());
                                        e.setTargetY(validPos.getY());
                                        e.setTargetZ(validPos.getZ());
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        private boolean isMobAffected(BlockPos pos, Entity mob, int radius) {
            double dist = mob.getPosition().getDistance(pos.getX(), pos.getY(), pos.getZ());
            return !(mob instanceof EntityEnderman && dist < radius);
        }

        private BlockPos getValidTeleportPosAround(World world, BlockPos pos) {
            BlockPos validPos = null;
            List<BlockPos> validPositions = new ArrayList<>();
            for (int x = -2; x <= 2; x++) {
                for (int z = -2; z <= 2; z++) {
                    BlockPos posToCheck = new BlockPos(pos.getX() + x, pos.getY(), pos.getZ() + z);
                    if (world.getBlockState(posToCheck).getBlock().equals(Blocks.AIR)) {
                        validPositions.add(posToCheck);
                    }
                }
            }
            int idx = (int) (Math.random() * (validPositions.size() - 1));
            validPos = validPositions.get(idx);

            return validPos;
        }
    }
} public class ConfigurationHandler {
    public static Configuration configuration;

    /**
     * Config Properties
     **/


    public static void Init(File aConfigFile) {
        if (configuration == null) {
            configuration = new Configuration(aConfigFile);
            loadConfiguration();
        }
    }

    @SubscribeEvent
    public void onConfigurationChangedEvent(ConfigChangedEvent.OnConfigChangedEvent event) {
        if (Reference.MOD_ID.equalsIgnoreCase(event.getModID())) {
            loadConfiguration();
        }
    }

    private static void loadConfiguration() {

        if (configuration.hasChanged()) {
            configuration.save();
        }
    }
} public class ModEntities {
    private static int ENTITY_ID = 0;

    public static final String SPIRIT_WOLF = "spirit_wolf";

    private static Map<String, Class<? extends Entity>> mEntities = Collections.emptyMap();

    public static void init() {
        mEntities = new HashMap<String, Class<? extends Entity>>();
        mEntities.put(SPIRIT_WOLF, EntitySpiritWolf.class);
    }

    public static void registerEntities(Object modObject) {
        for (String key : mEntities.keySet()) {
            Class<? extends Entity> entityClass = mEntities.get(key);
            if (entityClass != null) {
                EntityRegistry.registerModEntity(new ResourceLocation(Reference.RESOURCE_PREFIX + key), entityClass, Reference.RESOURCE_PREFIX + key, ENTITY_ID++, modObject, 80, 3, false);
                EntityRegistry.registerEgg(new ResourceLocation(Reference.RESOURCE_PREFIX + key), 1, 2);
            }
        }
    }

    public static void registerEntityRenders() {
        RenderingRegistry.registerEntityRenderingHandler(EntitySpiritWolf.class, new SpiritWolfRenderFactory());
    }
} public class OfferingBoxGuiHandler implements IGuiHandler {
    @Nullable
    @Override
    public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
        OfferingBoxContainer ret = null;
        TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
        if (tileEntity instanceof OfferingBoxTileEntity) {
            ret = new OfferingBoxContainer(player.inventory, (IInventory) tileEntity);
        }
        return ret;
    }

    @Nullable
    @Override
    public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
        OfferingBoxGuiContainer ret = null;
        TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
        if (tileEntity instanceof OfferingBoxTileEntity) {
            ret = new OfferingBoxGuiContainer(player.inventory, (IInventory) tileEntity);
        }
        return ret;
    }
} @Mod.EventBusSubscriber(modid = Reference.MOD_ID)
public class ModItems {
    public static final Map<String, Item> ITEMS = new HashMap<String, Item>();

    public static final String WOLF_TOTEM_BAUBLE_NAME = "wolf_totem_bauble";
    public static final String TOTEMIC_FOCUS_NAME = "totemic_focus";
    public static final String CARVING_KNIFE_NAME = "carving_knife";

    // We're going to use the TotemicFocus for the CreativeTab:
    public static final Item TOTEMIC_FOCUS = new TotemicFocus().setUnlocalizedName(TOTEMIC_FOCUS_NAME).setRegistryName(Reference.MOD_ID, TOTEMIC_FOCUS_NAME);
    public static final Item WOLF_TOTEM_BAUBLE = new WolfTotemBauble().setUnlocalizedName(WOLF_TOTEM_BAUBLE_NAME).setRegistryName(Reference.MOD_ID, WOLF_TOTEM_BAUBLE_NAME);
    ;
    public static final Item CARVING_KNIFE = new CarvingKnife().setUnlocalizedName(CARVING_KNIFE_NAME).setRegistryName(Reference.MOD_ID, CARVING_KNIFE_NAME);
    ;

    public static void init() {
    }

    @SubscribeEvent
    public static void registerItems(RegistryEvent.Register<Item> e) {
        final IForgeRegistry<Item> registry = e.getRegistry();
        final Item[] items = {
                TOTEMIC_FOCUS,
                WOLF_TOTEM_BAUBLE,
                CARVING_KNIFE
        };

        for (Item item : items) {
            registry.register(item);
        }

        ITEMS.put(TOTEMIC_FOCUS_NAME, TOTEMIC_FOCUS);
        ITEMS.put(WOLF_TOTEM_BAUBLE_NAME, WOLF_TOTEM_BAUBLE);
        ITEMS.put(CARVING_KNIFE_NAME, CARVING_KNIFE);
    }

    public static void registerRenders() {
        for (String key : ITEMS.keySet()) {
            Item item = ITEMS.get(key);
            if (item != null) {
                registerRender(item, 0, key);
            }
        }
    }

    private static void registerRender(Item item, int meta, String key) {
        Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(item,
                meta,
                new ModelResourceLocation(Reference.RESOURCE_PREFIX + key, "inventory"));
    }
} public class Network {
    private static SimpleNetworkWrapper instance = null;
    private static int DISCRIMINATOR_ID = 0;

    public static void init() {
        instance = NetworkRegistry.INSTANCE.newSimpleChannel(Reference.MOD_ID);

        instance.registerMessage(ActivateBaubleMessage.class, ActivateBaubleMessage.class, getDiscriminatorId(), Side.SERVER);
        instance.registerMessage(ActivateKnifeMessage.class, ActivateKnifeMessage.class, getDiscriminatorId(), Side.SERVER);
        instance.registerMessage(SetKnifeMetaMessage.class, SetKnifeMetaMessage.class, getDiscriminatorId(), Side.SERVER);

        instance.registerMessage(OpenKnifeGuiMessage.class, OpenKnifeGuiMessage.class, getDiscriminatorId(), Side.CLIENT);
    }

    private static int getDiscriminatorId() {
        return DISCRIMINATOR_ID++;
    }

    public static void sendToServer(Message message) {
        instance.sendToServer(message);
    }

    public static void sendToAll(Message message) {
        instance.sendToAll(message);
    }

    public static void sendTo(Message message, EntityPlayerMP player) {
        instance.sendTo(message, player);
    }
} public class CommonProxy {
    public void registerRenders() {
    }

    public void registerEntityRenders() {
    }

    public void registerKeys() {
    }

    public void registerGui() {
    }

    public void registerParticleRenderer() {
    }

//    public MobTotemsComponent initializeDagger(MobTotems instance) {
//        MinecraftComponent minecraftComponent = initializeMinecraftComponent();
//        return DaggerMobTotemsComponent.builder()
//                .mobTotemsModule(new MobTotemsModule(instance))
//                .minecraftComponent(minecraftComponent)
//                .build();
//    }

//    protected MinecraftComponent initializeMinecraftComponent() {
//        return DaggerMinecraftComponent.builder()
//                .minecraftModule(new MinecraftModule(null, FMLCommonHandler.instance().getMinecraftServerInstance()))
//                .build();
//    }
}
import com.corwinjv.di.MobTotemsComponent;
import com.corwinjv.mobtotems.blocks.ModBlocks;
import com.corwinjv.mobtotems.blocks.tiles.TotemLogic.CowLogic;
import com.corwinjv.mobtotems.blocks.tiles.TotemLogic.CreeperLogic;
import com.corwinjv.mobtotems.blocks.tiles.TotemLogic.EnderLogic;
import com.corwinjv.mobtotems.config.ConfigurationHandler;
import com.corwinjv.mobtotems.entities.ModEntities;
import com.corwinjv.mobtotems.gui.OfferingBoxGuiHandler;
import com.corwinjv.mobtotems.items.ModItems;
import com.corwinjv.mobtotems.network.Network;
import com.corwinjv.mobtotems.proxy.CommonProxy;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.network.NetworkRegistry;
package com.corwinjv.mobtotems;
/**
 * Created by corwinjv on 8/30/14.
 */


@Mod(modid = Reference.MOD_ID, name = Reference.MOD_NAME, version = Reference.MOD_VERSION, guiFactory = Reference.GUI_FACTORY_CLASS,
        dependencies = "before:guideapi")
public class MobTotems {
    @Mod.Instance
    public static MobTotems instance;

    @SidedProxy(clientSide = Reference.CLIENT_SIDE_PROXY_CLASS, serverSide = Reference.SERVER_SIDE_PROXY_CLASS)
    public static CommonProxy proxy;

    private static MobTotemsComponent mobTotemsComponent;

    @Mod.EventHandler
    public void preInit(FMLPreInitializationEvent event) {
//        //Dagger 2 implementation
//        mobTotemsComponent = proxy.initializeDagger(instance);

        // Network & Messages
        Network.init();