// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.worldgen.cave.shape;

import com.hypixel.hytale.server.core.asset.type.blocktype.config.RequiredBlockFaceSupport;
import java.util.Map;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockFace;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import com.hypixel.hytale.procedurallib.condition.IBlockFluidCondition;
import com.hypixel.hytale.procedurallib.condition.ConstantBlockFluidCondition;
import com.hypixel.hytale.server.worldgen.chunk.ChunkGeneratorExecution;
import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry;
import com.hypixel.hytale.server.worldgen.cave.CaveType;
import java.util.Random;
import com.hypixel.hytale.procedurallib.supplier.IDoubleRange;
import com.hypixel.hytale.server.worldgen.cave.CaveNodeType;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.worldgen.cave.element.CaveNode;
import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds;
import javax.annotation.Nonnull;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.function.function.BiDoubleToDoubleFunction;

public class CaveNodeShapeUtils
{
    public static final BiDoubleToDoubleFunction LEFT;
    public static final BiDoubleToDoubleFunction RIGHT;
    public static final BiDoubleToDoubleFunction MIN;
    public static final BiDoubleToDoubleFunction MAX;
    
    @Nonnull
    public static Vector3d getBoxAnchor(@Nonnull final Vector3d vector, @Nonnull final IWorldBounds bounds, final double tx, final double ty, final double tz) {
        final double x = bounds.fractionX(tx);
        final double y = bounds.fractionY(ty);
        final double z = bounds.fractionZ(tz);
        return vector.assign(x, y, z);
    }
    
    @Nonnull
    public static Vector3d getLineAnchor(@Nonnull final Vector3d vector, @Nonnull final Vector3d o, @Nonnull final Vector3d v, final double t) {
        final double x = o.x + v.x * t;
        final double y = o.y + v.y * t;
        final double z = o.z + v.z * t;
        return vector.assign(x, y, z);
    }
    
    @Nonnull
    public static Vector3d getSphereAnchor(@Nonnull final Vector3d vector, @Nonnull final Vector3d origin, final double rx, final double ry, final double rz, final double tx, final double ty, final double tz) {
        final double fx = tx * 2.0 - 1.0;
        final double fy = ty * 2.0 - 1.0;
        final double fz = tz * 2.0 - 1.0;
        return getRadialProjection(vector, origin.x, origin.y, origin.z, rx, ry, rz, fx, fy, fz);
    }
    
    @Nonnull
    public static Vector3d getPipeAnchor(@Nonnull final Vector3d vector, @Nonnull final Vector3d o, @Nonnull final Vector3d v, final double rx, final double ry, final double rz, final double t, final double tv, final double th) {
        final double x = o.x + v.x * t;
        final double y = o.y + v.y * t;
        final double z = o.z + v.z * t;
        final double len = v.length();
        final double nx = v.x / len;
        final double ny = v.y / len;
        final double nz = v.z / len;
        final double fv = 2.0 * tv - 1.0;
        final double fh = 2.0 * th - 1.0;
        final double fx = -ny * fv - nz * fh;
        final double fy = nx * fv;
        final double fz = nx * fh;
        return getRadialProjection(vector, x, y, z, rx, ry, rz, fx, fy, fz);
    }
    
    @Nonnull
    public static Vector3d getOffset(@Nullable final CaveNode parent, @Nonnull final CaveNodeType.CaveNodeChildEntry childEntry) {
        Vector3d offset = childEntry.getOffset();
        if (offset == Vector3d.ZERO) {
            return offset;
        }
        if (parent != null && parent.getShape() instanceof PrefabCaveNodeShape) {
            offset = offset.clone();
            ((PrefabCaveNodeShape)parent.getShape()).getPrefabRotation().rotate(offset);
        }
        return offset;
    }
    
    public static double getEndRadius(@Nullable final CaveNode node, @Nonnull final IDoubleRange range, final Random random) {
        if (node != null) {
            final double radius = getEndRadius(node.getShape(), CaveNodeShapeUtils.MIN);
            if (radius != -1.0) {
                return radius;
            }
        }
        return range.getValue(random);
    }
    
    public static double getEndWidth(@Nullable final CaveNode node, @Nonnull final IDoubleRange range, final Random random) {
        if (node != null) {
            final double radius = getEndRadius(node.getShape(), CaveNodeShapeUtils.LEFT);
            if (radius != -1.0) {
                return radius;
            }
        }
        return range.getValue(random);
    }
    
    public static double getEndHeight(@Nullable final CaveNode node, @Nonnull final IDoubleRange range, final Random random) {
        if (node != null) {
            final double radius = getEndRadius(node.getShape(), CaveNodeShapeUtils.RIGHT);
            if (radius != -1.0) {
                return radius;
            }
        }
        return range.getValue(random);
    }
    
    public static double getEndRadius(@Nonnull final CaveNodeShape shape, @Nonnull final BiDoubleToDoubleFunction widthHeightSelector) {
        if (shape instanceof final CylinderCaveNodeShape cylinderCaveNodeShape) {
            return cylinderCaveNodeShape.getRadius2();
        }
        if (shape instanceof final PipeCaveNodeShape pipeCaveNodeShape) {
            return pipeCaveNodeShape.getRadius2();
        }
        if (shape instanceof final DistortedCaveNodeShape distortedCaveNodeShape) {
            final double width = distortedCaveNodeShape.getShape().getWidthAt(1.0);
            final double height = ((DistortedCaveNodeShape)shape).getShape().getHeightAt(1.0);
            return widthHeightSelector.apply(width, height);
        }
        return -1.0;
    }
    
    @Nullable
    public static BlockFluidEntry getFillingBlock(@Nonnull final CaveType cave, @Nonnull final CaveNodeType node, final int y, @Nonnull final Random random) {
        if (cave.getFluidLevel().getHeight() >= y) {
            return cave.getFluidLevel().getBlockEntry();
        }
        return node.getFilling(random);
    }
    
    protected static int getCoverHeight(final int lowest, final int lowestPossible, final int highest, final int highestPossible, final boolean heightLimited, @Nonnull final CaveNodeType.CaveNodeCoverEntry cover, @Nonnull final CaveNodeType.CaveNodeCoverEntry.Entry entry) {
        switch (cover.getType()) {
            case FLOOR: {
                if (lowest == Integer.MAX_VALUE || lowestPossible != lowest) {
                    return -1;
                }
                return lowest - 1 + entry.getOffset();
            }
            case CEILING: {
                if (heightLimited) {
                    return -1;
                }
                if (highest == Integer.MIN_VALUE || highestPossible != highest) {
                    return -1;
                }
                return highest + 1 - entry.getOffset();
            }
            default: {
                throw new AssertionError((Object)"Not all cases covered!");
            }
        }
    }
    
    public static boolean isCoverMatchingParent(final int cx, final int cz, final int y, @Nonnull final ChunkGeneratorExecution execution, @Nonnull final CaveNodeType.CaveNodeCoverEntry cover) {
        final int parentY = y + cover.getType().parentOffset;
        if (parentY < 0 || parentY > 319) {
            return false;
        }
        final IBlockFluidCondition parentCondition = cover.getParentCondition();
        if (parentCondition == ConstantBlockFluidCondition.DEFAULT_TRUE) {
            return true;
        }
        if (parentCondition == ConstantBlockFluidCondition.DEFAULT_FALSE) {
            return false;
        }
        final int parent = execution.getBlock(cx, parentY, cz);
        final int parentFluid = execution.getFluid(cx, parentY, cz);
        return parentCondition.eval(parent, parentFluid);
    }
    
    public static boolean invalidateCover(final int x, final int y, final int z, final CaveNodeType.CaveNodeCoverType type, @Nonnull final ChunkGeneratorExecution execution, @Nonnull final BlockTypeAssetMap<String, BlockType> blockTypeMap) {
        if (y < 0 || y > 319) {
            return false;
        }
        final byte priority = execution.getPriorityChunk().get(x, y, z);
        if (priority == 3) {
            return true;
        }
        if (priority != 5) {
            return false;
        }
        final int block = execution.getBlock(x, y, z);
        final BlockType blockType = blockTypeMap.getAsset(block);
        final Map<BlockFace, RequiredBlockFaceSupport[]> supportsMap = blockType.getSupport(execution.getRotationIndex(x, y, z));
        if (supportsMap == null) {
            return false;
        }
        return switch (type) {
            default -> throw new MatchException(null, null);
            case FLOOR -> supportsMap.containsKey(BlockFace.DOWN);
            case CEILING -> supportsMap.containsKey(BlockFace.UP);
        };
    }
    
    @Nonnull
    protected static Vector3d getRadialProjection(@Nonnull final Vector3d vector, double x, double y, double z, final double rx, final double ry, final double rz, final double tx, final double ty, final double tz) {
        final double len2 = tx * tx + ty * ty + tz * tz;
        if (len2 == 0.0) {
            return vector.assign(x, y, z);
        }
        final double invLen = Math.sqrt(1.0 / len2);
        final double dx = Math.abs(tx) * rx * invLen;
        final double dy = Math.abs(ty) * ry * invLen;
        final double dz = Math.abs(tz) * rz * invLen;
        x += dx * tx;
        y += dy * ty;
        z += dz * tz;
        return vector.assign(x, y, z);
    }
    
    static {
        LEFT = ((l, r) -> l);
        RIGHT = ((l, r) -> r);
        MIN = Math::min;
        MAX = Math::max;
    }
}
