content: package com.infinityraider.agricraft.content.irrigation;
  
  import com.infinityraider.agricraft.AgriCraft;
  import com.infinityraider.agricraft.content.AgriTileRegistry;
  import com.infinityraider.agricraft.render.blocks.TileEntityIrrigationChannelRenderer;
  import com.infinityraider.infinitylib.block.tile.InfinityTileEntityType;
  import com.infinityraider.infinitylib.reference.Constants;
  import net.minecraft.core.BlockPos;
  import net.minecraft.core.Direction;
  import net.minecraft.util.Mth;
  import net.minecraft.world.level.block.state.BlockState;
  import net.minecraftforge.api.distmarker.Dist;
  import net.minecraftforge.api.distmarker.OnlyIn;
  import net.minecraftforge.fluids.FluidStack;
  import net.minecraftforge.fluids.capability.IFluidHandler;
  
  import javax.annotation.Nonnull;
  import javax.annotation.Nullable;
  
  public class TileEntityIrrigationChannel extends TileEntityIrrigationComponent {
      private static final float MIN_Y = 6*Constants.UNIT;
      private static final float MAX_Y = 10*Constants.UNIT;
  
      private static final int HEIGHT_INTERVALS = 6;
      private static final float CONTENT_DELTA_FRACTION = 0.10F;
  
      private ValveState state = ValveState.NONE;
      private int counter = 0;
  
      public TileEntityIrrigationChannel(BlockPos pos, BlockState state) {
          super(AgriTileRegistry.getInstance().irrigation_channel.get(), pos, state, AgriCraft.instance.getConfig().channelCapacity(), MIN_Y, MAX_Y);
      }
  
      public static RenderFactory createRenderFactory() {
          return new RenderFactory();
      }
  
      public boolean hasHandWheel() {
          return ((BlockIrrigationChannelAbstract) this.getBlockState().getBlock()).hasHandWheel();
      }
  
      public boolean hasValve() {
          return BlockIrrigationChannelAbstract.VALVE.fetch(this.getBlockState()).hasValve();
      }
  
      public boolean isOpen() {
          return BlockIrrigationChannelAbstract.VALVE.fetch(this.getBlockState()).canTransfer();
      }
  
      public boolean isClosed() {
          return BlockIrrigationChannelAbstract.VALVE.fetch(this.getBlockState()) == BlockIrrigationChannelAbstract.Valve.CLOSED;
      }
  
      public void setValveState(ValveState state) {
          this.state = state;
          this.counter = state.getDuration();
      }
  
      public ValveState getValveState() {
          return this.state;
      }
  
      public float getValveAnimationProgress(float partialTicks) {
          return this.getValveState().getAnimationProgress(this.counter, partialTicks);
      }
  
      @Override
      protected void tickComponent() {
          if(this.getLevel() != null && this.getLevel().isClientSide()) {
              if (this.counter > 0) {
                  this.counter -= 1;
                  if (this.counter <= 0) {
                      this.state = state.getResult();
                  }
              }
          }
      }
  
      @Override
      protected boolean canConnect(@Nonnull TileEntityIrrigationComponent other) {
          return other.isSameMaterial(this);
      }
  
      @Override
      protected boolean canTransfer(@Nonnull TileEntityIrrigationComponent other, @Nonnull Direction dir) {
          return this.canConnect(other) && BlockIrrigationChannelAbstract.VALVE.fetch(this.getBlockState()).canTransfer();
      }
  
      @Override
      protected int getLevelIntervalCount() {
          return HEIGHT_INTERVALS;
      }
  
      @Override
      protected float getContentDeltaFraction() {
          return CONTENT_DELTA_FRACTION;
      }
  
      @Override
      protected String description() {
          return "channel";
      }
  
      @Override
      public boolean isFluidValid(FluidStack stack) {
          return false;
      }
  
      @Override
      public int fill(FluidStack resource, IFluidHandler.FluidAction action) {
          return 0;
      }
  
      @Nonnull
      @Override
      public FluidStack drain(int maxDrain, IFluidHandler.FluidAction action) {
          return FluidStack.EMPTY;
      }
  
      @Nonnull
      @Override
      public FluidStack drain(FluidStack resource, IFluidHandler.FluidAction action) {
          return FluidStack.EMPTY;
      }
  
      public enum ValveState {
          NONE(0),
          OPEN(1),
          CLOSED(0),
          OPENING(CLOSED, OPEN, 20),
          CLOSING(OPEN, CLOSED, 20);
  
          private final float target;
          private final ValveState from;
          private final ValveState to;
          private final int duration;
  
          ValveState(float target) {
              this.target = target;
              this.from = this;
              this.to = this;
              this.duration = -1;
          }
  
          ValveState(ValveState from, ValveState to, int duration) {
              this.from = from;
              this.to = to;
              this.duration = duration;
              this.target = this.getResult().target;
          }
  
          public float getAnimationProgress(int counter, float partialTick) {
              if(this.hasAnimation()) {
                  // to and from are inverted as the counter is counting down
                  return Mth.lerp(Math.min(1.0F, (counter + partialTick)/this.getDuration()), this.to.target, this.from.target);
              }
              return this.target;
          }
  
          public ValveState getResult() {
              return this.to;
          }
  
          public boolean hasAnimation() {
              return this.getDuration() > 0;
          }
  
          public int getDuration() {
              return this.duration;
          }
      }
  
      private static class RenderFactory implements InfinityTileEntityType.IRenderFactory<TileEntityIrrigationChannel> {
          @Nullable
          @OnlyIn(Dist.CLIENT)
          public TileEntityIrrigationChannelRenderer createRenderer() {
              return new TileEntityIrrigationChannelRenderer();
          }
      }
  }
  