
package net.mcreator.watermelon_play_simulator.world.teleporter;

import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.level.portal.PortalInfo;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.NetherPortalBlock;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.Entity;
import net.minecraft.util.Mth;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.BlockUtil;

import net.mcreator.watermelon_play_simulator.init.WatermelonPlaySimulator2ModBlocks;

import javax.annotation.Nullable;

import java.util.function.Predicate;
import java.util.Optional;

public class Xgplaymnq2PortalShape {
	private static final int MIN_WIDTH = 2;
	public static final int MAX_WIDTH = 21;
	private static final int MIN_HEIGHT = 3;
	public static final int MAX_HEIGHT = 21;
	private static final BlockBehaviour.StatePredicate FRAME = (p_77720_, p_77721_, p_77722_) -> {
		return p_77720_.getBlock() == WatermelonPlaySimulator2ModBlocks.LIVER_STONE.get();
	};
	private static final float SAFE_TRAVEL_MAX_ENTITY_XY = 4.0F;
	private static final double SAFE_TRAVEL_MAX_VERTICAL_DELTA = 1.0D;
	private final LevelAccessor level;
	private final Direction.Axis axis;
	private final Direction rightDir;
	private int numPortalBlocks;
	@Nullable
	private BlockPos bottomLeft;
	private int height;
	private final int width;

	public static Optional<Xgplaymnq2PortalShape> findEmptyPortalShape(LevelAccessor p_77709_, BlockPos p_77710_, Direction.Axis p_77711_) {
		return findPortalShape(p_77709_, p_77710_, (p_77727_) -> {
			return p_77727_.isValid() && p_77727_.numPortalBlocks == 0;
		}, p_77711_);
	}

	public static Optional<Xgplaymnq2PortalShape> findPortalShape(LevelAccessor p_77713_, BlockPos p_77714_, Predicate<Xgplaymnq2PortalShape> p_77715_, Direction.Axis p_77716_) {
		Optional<Xgplaymnq2PortalShape> optional = Optional.of(new Xgplaymnq2PortalShape(p_77713_, p_77714_, p_77716_)).filter(p_77715_);
		if (optional.isPresent()) {
			return optional;
		} else {
			Direction.Axis direction$axis = p_77716_ == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X;
			return Optional.of(new Xgplaymnq2PortalShape(p_77713_, p_77714_, direction$axis)).filter(p_77715_);
		}
	}

	public Xgplaymnq2PortalShape(LevelAccessor p_77695_, BlockPos p_77696_, Direction.Axis p_77697_) {
		this.level = p_77695_;
		this.axis = p_77697_;
		this.rightDir = p_77697_ == Direction.Axis.X ? Direction.WEST : Direction.SOUTH;
		this.bottomLeft = this.calculateBottomLeft(p_77696_);
		if (this.bottomLeft == null) {
			this.bottomLeft = p_77696_;
			this.width = 1;
			this.height = 1;
		} else {
			this.width = this.calculateWidth();
			if (this.width > 0) {
				this.height = this.calculateHeight();
			}
		}
	}

	@Nullable
	private BlockPos calculateBottomLeft(BlockPos p_77734_) {
		for (int i = Math.max(this.level.getMinBuildHeight(), p_77734_.getY() - 21); p_77734_.getY() > i && isEmpty(this.level.getBlockState(p_77734_.below())); p_77734_ = p_77734_.below()) {
		}
		Direction direction = this.rightDir.getOpposite();
		int j = this.getDistanceUntilEdgeAboveFrame(p_77734_, direction) - 1;
		return j < 0 ? null : p_77734_.relative(direction, j);
	}

	private int calculateWidth() {
		int i = this.getDistanceUntilEdgeAboveFrame(this.bottomLeft, this.rightDir);
		return i >= 2 && i <= 21 ? i : 0;
	}

	private int getDistanceUntilEdgeAboveFrame(BlockPos p_77736_, Direction p_77737_) {
		BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
		for (int i = 0; i <= 21; ++i) {
			blockpos$mutableblockpos.set(p_77736_).move(p_77737_, i);
			BlockState blockstate = this.level.getBlockState(blockpos$mutableblockpos);
			if (!isEmpty(blockstate)) {
				if (FRAME.test(blockstate, this.level, blockpos$mutableblockpos)) {
					return i;
				}
				break;
			}
			BlockState blockstate1 = this.level.getBlockState(blockpos$mutableblockpos.move(Direction.DOWN));
			if (!FRAME.test(blockstate1, this.level, blockpos$mutableblockpos)) {
				break;
			}
		}
		return 0;
	}

	private int calculateHeight() {
		BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
		int i = this.getDistanceUntilTop(blockpos$mutableblockpos);
		return i >= 3 && i <= 21 && this.hasTopFrame(blockpos$mutableblockpos, i) ? i : 0;
	}

	private boolean hasTopFrame(BlockPos.MutableBlockPos p_77731_, int p_77732_) {
		for (int i = 0; i < this.width; ++i) {
			BlockPos.MutableBlockPos blockpos$mutableblockpos = p_77731_.set(this.bottomLeft).move(Direction.UP, p_77732_).move(this.rightDir, i);
			if (!FRAME.test(this.level.getBlockState(blockpos$mutableblockpos), this.level, blockpos$mutableblockpos)) {
				return false;
			}
		}
		return true;
	}

	private int getDistanceUntilTop(BlockPos.MutableBlockPos p_77729_) {
		for (int i = 0; i < 21; ++i) {
			p_77729_.set(this.bottomLeft).move(Direction.UP, i).move(this.rightDir, -1);
			if (!FRAME.test(this.level.getBlockState(p_77729_), this.level, p_77729_)) {
				return i;
			}
			p_77729_.set(this.bottomLeft).move(Direction.UP, i).move(this.rightDir, this.width);
			if (!FRAME.test(this.level.getBlockState(p_77729_), this.level, p_77729_)) {
				return i;
			}
			for (int j = 0; j < this.width; ++j) {
				p_77729_.set(this.bottomLeft).move(Direction.UP, i).move(this.rightDir, j);
				BlockState blockstate = this.level.getBlockState(p_77729_);
				if (!isEmpty(blockstate)) {
					return i;
				}
				if (blockstate.getBlock() == WatermelonPlaySimulator2ModBlocks.XGPLAYMNQ_2_PORTAL.get()) {
					++this.numPortalBlocks;
				}
			}
		}
		return 21;
	}

	private static boolean isEmpty(BlockState p_77718_) {
		return p_77718_.isAir() || p_77718_.getBlock() == WatermelonPlaySimulator2ModBlocks.XGPLAYMNQ_2_PORTAL.get();
	}

	public boolean isValid() {
		return this.bottomLeft != null && this.width >= 2 && this.width <= 21 && this.height >= 3 && this.height <= 21;
	}

	public void createPortalBlocks() {
		BlockState blockstate = WatermelonPlaySimulator2ModBlocks.XGPLAYMNQ_2_PORTAL.get().defaultBlockState().setValue(NetherPortalBlock.AXIS, this.axis);
		BlockPos.betweenClosed(this.bottomLeft, this.bottomLeft.relative(Direction.UP, this.height - 1).relative(this.rightDir, this.width - 1)).forEach((p_77725_) -> {
			this.level.setBlock(p_77725_, blockstate, 18);
			if (this.level instanceof ServerLevel)
				((ServerLevel) this.level).getPoiManager().add(p_77725_, Xgplaymnq2Teleporter.poi);
		});
	}

	public boolean isComplete() {
		return this.isValid() && this.numPortalBlocks == this.width * this.height;
	}

	public static Vec3 getRelativePosition(BlockUtil.FoundRectangle p_77739_, Direction.Axis p_77740_, Vec3 p_77741_, EntityDimensions p_77742_) {
		double d0 = (double) p_77739_.axis1Size - (double) p_77742_.width;
		double d1 = (double) p_77739_.axis2Size - (double) p_77742_.height;
		BlockPos blockpos = p_77739_.minCorner;
		double d2;
		if (d0 > 0.0D) {
			float f = (float) blockpos.get(p_77740_) + p_77742_.width / 2.0F;
			d2 = Mth.clamp(Mth.inverseLerp(p_77741_.get(p_77740_) - (double) f, 0.0D, d0), 0.0D, 1.0D);
		} else {
			d2 = 0.5D;
		}
		double d4;
		if (d1 > 0.0D) {
			Direction.Axis direction$axis = Direction.Axis.Y;
			d4 = Mth.clamp(Mth.inverseLerp(p_77741_.get(direction$axis) - (double) blockpos.get(direction$axis), 0.0D, d1), 0.0D, 1.0D);
		} else {
			d4 = 0.0D;
		}
		Direction.Axis direction$axis1 = p_77740_ == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X;
		double d3 = p_77741_.get(direction$axis1) - ((double) blockpos.get(direction$axis1) + 0.5D);
		return new Vec3(d2, d4, d3);
	}

	public static PortalInfo createPortalInfo(ServerLevel p_259301_, BlockUtil.FoundRectangle p_259931_, Direction.Axis p_259901_, Vec3 p_259630_, Entity p_259166_, Vec3 p_260043_, float p_259853_, float p_259667_) {
		BlockPos blockpos = p_259931_.minCorner;
		BlockState blockstate = p_259301_.getBlockState(blockpos);
		Direction.Axis direction$axis = blockstate.getOptionalValue(BlockStateProperties.HORIZONTAL_AXIS).orElse(Direction.Axis.X);
		double d0 = (double) p_259931_.axis1Size;
		double d1 = (double) p_259931_.axis2Size;
		EntityDimensions entitydimensions = p_259166_.getDimensions(p_259166_.getPose());
		int i = p_259901_ == direction$axis ? 0 : 90;
		Vec3 vec3 = p_259901_ == direction$axis ? p_260043_ : new Vec3(p_260043_.z, p_260043_.y, -p_260043_.x);
		double d2 = (double) entitydimensions.width / 2.0D + (d0 - (double) entitydimensions.width) * p_259630_.x();
		double d3 = (d1 - (double) entitydimensions.height) * p_259630_.y();
		double d4 = 0.5D + p_259630_.z();
		boolean flag = direction$axis == Direction.Axis.X;
		Vec3 vec31 = new Vec3((double) blockpos.getX() + (flag ? d2 : d4), (double) blockpos.getY() + d3, (double) blockpos.getZ() + (flag ? d4 : d2));
		Vec3 vec32 = findCollisionFreePosition(vec31, p_259301_, p_259166_, entitydimensions);
		return new PortalInfo(vec32, vec3, p_259853_ + (float) i, p_259667_);
	}

	private static Vec3 findCollisionFreePosition(Vec3 p_260315_, ServerLevel p_259704_, Entity p_259626_, EntityDimensions p_259816_) {
		if (!(p_259816_.width > 4.0F) && !(p_259816_.height > 4.0F)) {
			double d0 = (double) p_259816_.height / 2.0D;
			Vec3 vec3 = p_260315_.add(0.0D, d0, 0.0D);
			VoxelShape voxelshape = Shapes.create(AABB.ofSize(vec3, (double) p_259816_.width, 0.0D, (double) p_259816_.width).expandTowards(0.0D, 1.0D, 0.0D).inflate(1.0E-6D));
			Optional<Vec3> optional = p_259704_.findFreePosition(p_259626_, voxelshape, vec3, (double) p_259816_.width, (double) p_259816_.height, (double) p_259816_.width);
			Optional<Vec3> optional1 = optional.map((p_259019_) -> {
				return p_259019_.subtract(0.0D, d0, 0.0D);
			});
			return optional1.orElse(p_260315_);
		} else {
			return p_260315_;
		}
	}
}
