content: package org.allaymc.server.blockentity.type;
  
  import com.google.common.base.Preconditions;
  import lombok.Getter;
  import me.sunlan.fastreflection.FastConstructor;
  import me.sunlan.fastreflection.FastMemberLoader;
  import org.allaymc.api.blockentity.BlockEntity;
  import org.allaymc.api.blockentity.BlockEntityInitInfo;
  import org.allaymc.api.blockentity.component.BlockEntityComponent;
  import org.allaymc.api.blockentity.type.BlockEntityType;
  import org.allaymc.api.registry.Registries;
  import org.allaymc.api.utils.identifier.Identifier;
  import org.allaymc.server.Allay;
  import org.allaymc.server.blockentity.component.BlockEntityBaseComponentImpl;
  import org.allaymc.server.component.ComponentProvider;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.function.Function;
  import java.util.function.Supplier;
  
  /**
   * @author daoge_cmd
   */
  public class AllayBlockEntityType<T extends BlockEntity> implements BlockEntityType<T> {
  
      protected final Function<BlockEntityInitInfo, T> instanceCreator;
      @Getter
      protected String name;
  
      protected AllayBlockEntityType(Function<BlockEntityInitInfo, T> instanceCreator, String name) {
          this.instanceCreator = instanceCreator;
          this.name = name;
      }
  
      public static <T extends BlockEntity> Builder builder(Class<T> clazz) {
          return new Builder(clazz);
      }
  
      @Override
      public T createBlockEntity(BlockEntityInitInfo info) {
          info.setBlockEntityType(this);
          return instanceCreator.apply(info);
      }
  
      public static class Builder {
          protected Class<?> clazz;
          protected Map<Identifier, ComponentProvider<? extends BlockEntityComponent>> componentProviders = new HashMap<>();
          protected String name;
  
          public Builder(Class<?> clazz) {
              this.clazz = clazz;
          }
  
          public Builder name(String name) {
              this.name = name;
              return this;
          }
  
          public Builder setComponents(Map<Identifier, ComponentProvider<? extends BlockEntityComponent>> providers) {
              Preconditions.checkNotNull(providers, "providers");
              this.componentProviders = new HashMap<>(providers);
              return this;
          }
  
          public Builder addComponents(Map<Identifier, ComponentProvider<? extends BlockEntityComponent>> providers) {
              this.componentProviders.putAll(providers);
              return this;
          }
  
          public Builder addComponent(Function<BlockEntityInitInfo, ? extends BlockEntityComponent> provider, Class<?> componentClass) {
              var p = new ComponentProvider.Simple<>(provider, componentClass);
              this.componentProviders.put(p.findComponentIdentifier(), p);
              return this;
          }
  
          public Builder addComponent(Supplier<? extends BlockEntityComponent> supplier, Class<?> componentClass) {
              return addComponent($ -> supplier.get(), componentClass);
          }
  
          public Builder addComponent(ComponentProvider<? extends BlockEntityComponent> provider) {
              this.componentProviders.put(provider.findComponentIdentifier(), provider);
              return this;
          }
  
          public <T extends BlockEntity> BlockEntityType<T> build() {
              return build(Allay.class.getClassLoader());
          }
  
          public <T extends BlockEntity> BlockEntityType<T> build(ClassLoader classLoader) {
              Preconditions.checkNotNull(name, "name");
              if (!componentProviders.containsKey(BlockEntityBaseComponentImpl.IDENTIFIER)) {
                  addComponent(BlockEntityBaseComponentImpl::new, BlockEntityBaseComponentImpl.class);
              }
  
              Function<BlockEntityInitInfo, T> instanceCreator;
              try {
                  var constructor = FastConstructor.create(clazz.getConstructor(BlockEntityInitInfo.class, List.class), new FastMemberLoader(classLoader), false);
                  var componentProviderList = new ArrayList<>(componentProviders.values());
                  instanceCreator = info -> {
                      try {
                          return (T) constructor.invoke(info, componentProviderList);
                      } catch (Throwable t) {
                          throw new BlockEntityTypeBuildException("Failed to create block entity instance!", t);
                      }
                  };
              } catch (Exception e) {
                  throw new BlockEntityTypeBuildException("Failed to create block entity type!", e);
              }
  
              var type = new AllayBlockEntityType<>(instanceCreator, name);
              Registries.BLOCK_ENTITIES.register(name, type);
              return type;
          }
      }
  }
  