content: package com.infinityraider.agricraft.capability;
  
  import com.google.common.collect.Maps;
  import com.infinityraider.agricraft.AgriCraft;
  import com.infinityraider.agricraft.capability.CapabilityGreenHouseParts.Impl;
  import com.infinityraider.agricraft.content.world.greenhouse.GreenHousePart;
  import com.infinityraider.agricraft.reference.AgriNBT;
  import com.infinityraider.agricraft.reference.Names;
  import com.infinityraider.infinitylib.capability.IInfSerializableCapabilityImplementation;
  import net.minecraft.nbt.CompoundTag;
  import net.minecraft.nbt.ListTag;
  import net.minecraft.resources.ResourceLocation;
  import net.minecraft.world.level.ChunkPos;
  import net.minecraft.world.level.Level;
  import net.minecraft.world.level.chunk.LevelChunk;
  import net.minecraftforge.common.capabilities.Capability;
  import net.minecraftforge.common.capabilities.CapabilityManager;
  import net.minecraftforge.common.capabilities.CapabilityToken;
  
  import javax.annotation.Nullable;
  import java.util.Map;
  import java.util.Optional;
  
  public class CapabilityGreenHouseParts implements IInfSerializableCapabilityImplementation<LevelChunk, Impl> {
      private static final CapabilityGreenHouseParts INSTANCE = new CapabilityGreenHouseParts();
  
      public static CapabilityGreenHouseParts getInstance() {
          return INSTANCE;
      }
  
      public static ResourceLocation KEY = new ResourceLocation(AgriCraft.instance.getModId().toLowerCase(), Names.Objects.GREENHOUSE_DATA);
  
      public static final Capability<CapabilityGreenHouseParts.Impl> CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});
  
      private CapabilityGreenHouseParts() {}
  
      public static void addPart(LevelChunk chunk, GreenHousePart part) {
          getInstance().getCapability(chunk).ifPresent(impl -> {
              impl.add(part);
              chunk.setUnsaved(true);
          });
      }
  
      public static Optional<GreenHousePart> removePart(LevelChunk chunk, int id) {
          return getInstance().getCapability(chunk).map(o -> o).flatMap(impl -> Optional.ofNullable(impl.remove(id)));
      }
  
      public static Optional<GreenHousePart> getPart(Level world, ChunkPos pos, int id) {
          return getInstance().getCapability(world.getChunk(pos.x, pos.z))
                  .map(o -> o)
                  .flatMap(impl -> impl.getPartHolder(id));
      }
  
      @Override
      public Class<Impl> getCapabilityClass() {
          return Impl.class;
      }
  
      @Override
      public Capability<Impl> getCapability() {
          return CAPABILITY;
      }
  
      @Override
      public boolean shouldApplyCapability(LevelChunk carrier) {
          return true;
      }
  
      @Override
      public Impl createNewValue(LevelChunk carrier) {
          return new Impl();
      }
  
      @Override
      public ResourceLocation getCapabilityKey() {
          return KEY;
      }
  
      @Override
      public Class<LevelChunk> getCarrierClass() {
          return LevelChunk.class;
      }
  
      public static class Impl implements Serializable<Impl> {
          private final Map<Integer, GreenHousePart> greenhouses;
  
          protected Impl() {
              this.greenhouses = Maps.newHashMap();
          }
  
          public Optional<GreenHousePart> getPartHolder(int id) {
              return Optional.ofNullable(this.greenhouses.get(id));
          }
  
          protected void add(GreenHousePart part) {
              this.greenhouses.put(part.getId(), part);
          }
  
          @Nullable
          protected GreenHousePart remove(int id) {
              return this.greenhouses.remove(id);
          }
  
          @Override
          public void copyDataFrom(Impl from) {
              this.greenhouses.clear();
              from.greenhouses.forEach((id, greenhouse) -> this.greenhouses.put(id.intValue(), greenhouse));
          }
  
          @Override
          public CompoundTag serializeNBT() {
              ListTag entries = new ListTag();
              this.greenhouses.forEach((id, part) -> entries.add(part.writeToTag()));
              CompoundTag tag = new CompoundTag();
              tag.put(AgriNBT.ENTRIES, entries);
              return tag;
          }
  
          @Override
          public void deserializeNBT(CompoundTag tag) {
              this.greenhouses.clear();
              AgriNBT.stream(tag, AgriNBT.ENTRIES).forEach(entry -> {
                  GreenHousePart part = new GreenHousePart(entry);
                  this.greenhouses.put(part.getId(), part);
              });
          }
      }
  }
  