package net.minecraft.world.level.levelgen.feature;

import net.minecraft.world.level.block.state.AbstractStateHolder;
import com.google.common.collect.Lists;
import java.util.Iterator;
import java.util.List;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.LevelWriter;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.LogBlock;
import net.minecraft.world.level.block.state.BlockState;
import java.util.Objects;
import net.minecraft.util.Mth;
import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import java.util.Set;
import net.minecraft.core.BlockPos;
import java.util.Random;
import net.minecraft.world.level.LevelSimulatedRW;
import com.mojang.datafixers.Dynamic;
import java.util.function.Function;
import net.minecraft.world.level.levelgen.feature.configurations.SmallTreeConfiguration;

public class FancyTreeFeature extends AbstractTreeFeature<SmallTreeConfiguration> {
    public FancyTreeFeature(final Function<Dynamic<?>, ? extends SmallTreeConfiguration> function) {
        super(function);
    }
    
    private void crossSection(final LevelSimulatedRW bkb, final Random random, final BlockPos fk, final float float4, final Set<BlockPos> set, final BoundingBox clb, final SmallTreeConfiguration chi) {
        for (int integer9 = (int)(float4 + 0.618), integer10 = -integer9; integer10 <= integer9; ++integer10) {
            for (int integer11 = -integer9; integer11 <= integer9; ++integer11) {
                if (Math.pow(Math.abs(integer10) + 0.5, 2.0) + Math.pow(Math.abs(integer11) + 0.5, 2.0) <= float4 * float4) {
                    this.placeLeaf(bkb, random, fk.offset(integer10, 0, integer11), set, clb, chi);
                }
            }
        }
    }
    
    private float treeShape(final int integer1, final int integer2) {
        if (integer2 < integer1 * 0.3f) {
            return -1.0f;
        }
        final float float4 = integer1 / 2.0f;
        final float float5 = float4 - integer2;
        float float6 = Mth.sqrt(float4 * float4 - float5 * float5);
        if (float5 == 0.0f) {
            float6 = float4;
        }
        else if (Math.abs(float5) >= float4) {
            return 0.0f;
        }
        return float6 * 0.5f;
    }
    
    private float foliageShape(final int integer) {
        if (integer < 0 || integer >= 5) {
            return -1.0f;
        }
        if (integer == 0 || integer == 4) {
            return 2.0f;
        }
        return 3.0f;
    }
    
    private void foliageCluster(final LevelSimulatedRW bkb, final Random random, final BlockPos fk, final Set<BlockPos> set, final BoundingBox clb, final SmallTreeConfiguration chi) {
        for (int integer8 = 0; integer8 < 5; ++integer8) {
            this.crossSection(bkb, random, fk.above(integer8), this.foliageShape(integer8), set, clb, chi);
        }
    }
    
    private int makeLimb(final LevelSimulatedRW bkb, final Random random, final BlockPos fk3, final BlockPos fk4, final boolean boolean5, final Set<BlockPos> set, final BoundingBox clb, final SmallTreeConfiguration chi) {
        if (!boolean5 && Objects.equals(fk3, fk4)) {
            return -1;
        }
        final BlockPos fk5 = fk4.offset(-fk3.getX(), -fk3.getY(), -fk3.getZ());
        final int integer11 = this.getSteps(fk5);
        final float float12 = fk5.getX() / (float)integer11;
        final float float13 = fk5.getY() / (float)integer11;
        final float float14 = fk5.getZ() / (float)integer11;
        for (int integer12 = 0; integer12 <= integer11; ++integer12) {
            final BlockPos fk6 = fk3.offset(0.5f + integer12 * float12, 0.5f + integer12 * float13, 0.5f + integer12 * float14);
            if (boolean5) {
                this.setBlock(bkb, fk6, ((AbstractStateHolder<O, BlockState>)chi.trunkProvider.getState(random, fk6)).<Direction.Axis, Direction.Axis>setValue(LogBlock.AXIS, this.getLogAxis(fk3, fk6)), clb);
                set.add(fk6);
            }
            else if (!AbstractTreeFeature.isFree(bkb, fk6)) {
                return integer12;
            }
        }
        return -1;
    }
    
    private int getSteps(final BlockPos fk) {
        final int integer3 = Mth.abs(fk.getX());
        final int integer4 = Mth.abs(fk.getY());
        final int integer5 = Mth.abs(fk.getZ());
        if (integer5 > integer3 && integer5 > integer4) {
            return integer5;
        }
        if (integer4 > integer3) {
            return integer4;
        }
        return integer3;
    }
    
    private Direction.Axis getLogAxis(final BlockPos fk1, final BlockPos fk2) {
        Direction.Axis a4 = Direction.Axis.Y;
        final int integer5 = Math.abs(fk2.getX() - fk1.getX());
        final int integer6 = Math.abs(fk2.getZ() - fk1.getZ());
        final int integer7 = Math.max(integer5, integer6);
        if (integer7 > 0) {
            if (integer5 == integer7) {
                a4 = Direction.Axis.X;
            }
            else if (integer6 == integer7) {
                a4 = Direction.Axis.Z;
            }
        }
        return a4;
    }
    
    private void makeFoliage(final LevelSimulatedRW bkb, final Random random, final int integer, final BlockPos fk, final List<FoliageCoords> list, final Set<BlockPos> set, final BoundingBox clb, final SmallTreeConfiguration chi) {
        for (final FoliageCoords a11 : list) {
            if (this.trimBranches(integer, a11.getBranchBase() - fk.getY())) {
                this.foliageCluster(bkb, random, a11, set, clb, chi);
            }
        }
    }
    
    private boolean trimBranches(final int integer1, final int integer2) {
        return integer2 >= integer1 * 0.2;
    }
    
    private void makeTrunk(final LevelSimulatedRW bkb, final Random random, final BlockPos fk, final int integer, final Set<BlockPos> set, final BoundingBox clb, final SmallTreeConfiguration chi) {
        this.makeLimb(bkb, random, fk, fk.above(integer), true, set, clb, chi);
    }
    
    private void makeBranches(final LevelSimulatedRW bkb, final Random random, final int integer, final BlockPos fk, final List<FoliageCoords> list, final Set<BlockPos> set, final BoundingBox clb, final SmallTreeConfiguration chi) {
        for (final FoliageCoords a11 : list) {
            final int integer2 = a11.getBranchBase();
            final BlockPos fk2 = new BlockPos(fk.getX(), integer2, fk.getZ());
            if (!fk2.equals(a11) && this.trimBranches(integer, integer2 - fk.getY())) {
                this.makeLimb(bkb, random, fk2, a11, true, set, clb, chi);
            }
        }
    }
    
    public boolean doPlace(final LevelSimulatedRW bkb, final Random random, final BlockPos fk, final Set<BlockPos> set4, final Set<BlockPos> set5, final BoundingBox clb, final SmallTreeConfiguration chi) {
        final Random random2 = new Random(random.nextLong());
        final int integer10 = this.checkLocation(bkb, random, fk, 5 + random2.nextInt(12), set4, clb, chi);
        if (integer10 == -1) {
            return false;
        }
        this.setDirtAt(bkb, fk.below());
        int integer11 = (int)(integer10 * 0.618);
        if (integer11 >= integer10) {
            integer11 = integer10 - 1;
        }
        final double double12 = 1.0;
        int integer12 = (int)(1.382 + Math.pow(1.0 * integer10 / 13.0, 2.0));
        if (integer12 < 1) {
            integer12 = 1;
        }
        final int integer13 = fk.getY() + integer11;
        int integer14 = integer10 - 5;
        final List<FoliageCoords> list17 = Lists.newArrayList();
        list17.add(new FoliageCoords(fk.above(integer14), integer13));
        while (integer14 >= 0) {
            final float float18 = this.treeShape(integer10, integer14);
            if (float18 >= 0.0f) {
                for (int integer15 = 0; integer15 < integer12; ++integer15) {
                    final double double13 = 1.0;
                    final double double14 = 1.0 * float18 * (random2.nextFloat() + 0.328);
                    final double double15 = random2.nextFloat() * 2.0f * 3.141592653589793;
                    final double double16 = double14 * Math.sin(double15) + 0.5;
                    final double double17 = double14 * Math.cos(double15) + 0.5;
                    final BlockPos fk2 = fk.offset(double16, integer14 - 1, double17);
                    final BlockPos fk3 = fk2.above(5);
                    if (this.makeLimb(bkb, random, fk2, fk3, false, set4, clb, chi) == -1) {
                        final int integer16 = fk.getX() - fk2.getX();
                        final int integer17 = fk.getZ() - fk2.getZ();
                        final double double18 = fk2.getY() - Math.sqrt(integer16 * integer16 + integer17 * integer17) * 0.381;
                        final int integer18 = (double18 > integer13) ? integer13 : ((int)double18);
                        final BlockPos fk4 = new BlockPos(fk.getX(), integer18, fk.getZ());
                        if (this.makeLimb(bkb, random, fk4, fk2, false, set4, clb, chi) == -1) {
                            list17.add(new FoliageCoords(fk2, fk4.getY()));
                        }
                    }
                }
            }
            --integer14;
        }
        this.makeFoliage(bkb, random, integer10, fk, list17, set5, clb, chi);
        this.makeTrunk(bkb, random, fk, integer11, set4, clb, chi);
        this.makeBranches(bkb, random, integer10, fk, list17, set4, clb, chi);
        return true;
    }
    
    private int checkLocation(final LevelSimulatedRW bkb, final Random random, final BlockPos fk, final int integer, final Set<BlockPos> set, final BoundingBox clb, final SmallTreeConfiguration chi) {
        if (!AbstractTreeFeature.isGrassOrDirtOrFarmland(bkb, fk.below())) {
            return -1;
        }
        final int integer2 = this.makeLimb(bkb, random, fk, fk.above(integer - 1), false, set, clb, chi);
        if (integer2 == -1) {
            return integer;
        }
        if (integer2 < 6) {
            return -1;
        }
        return integer2;
    }
    
    static class FoliageCoords extends BlockPos {
        private final int branchBase;
        
        public FoliageCoords(final BlockPos fk, final int integer) {
            super(fk.getX(), fk.getY(), fk.getZ());
            this.branchBase = integer;
        }
        
        public int getBranchBase() {
            return this.branchBase;
        }
    }
}
