package net.minecraft.client.renderer.block.model;

import net.minecraft.core.Vec3i;
import com.mojang.math.Quaternion;
import net.minecraft.client.renderer.FaceInfo;
import com.mojang.math.Matrix4f;
import com.mojang.math.Matrix3f;
import com.mojang.math.Vector4f;
import net.minecraft.core.BlockMath;
import com.mojang.math.Transformation;
import net.minecraft.util.Mth;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nullable;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.core.Direction;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import com.mojang.math.Vector3f;

public class FaceBakery {
    private static final float RESCALE_22_5;
    private static final float RESCALE_45;
    
    public BakedQuad bakeQuad(final Vector3f e1, final Vector3f e2, final BlockElementFace drp, final TextureAtlasSprite eaj, final Direction fp, final ModelState ecc, @Nullable final BlockElementRotation drq, final boolean boolean8, final ResourceLocation sm) {
        BlockFaceUV drr11 = drp.uv;
        if (ecc.isUvLocked()) {
            drr11 = recomputeUVs(drp.uv, fp, ecc.getRotation(), sm);
        }
        final float[] arr12 = new float[drr11.uvs.length];
        System.arraycopy(drr11.uvs, 0, arr12, 0, arr12.length);
        final float float13 = eaj.uvShrinkRatio();
        final float float14 = (drr11.uvs[0] + drr11.uvs[0] + drr11.uvs[2] + drr11.uvs[2]) / 4.0f;
        final float float15 = (drr11.uvs[1] + drr11.uvs[1] + drr11.uvs[3] + drr11.uvs[3]) / 4.0f;
        drr11.uvs[0] = Mth.lerp(float13, drr11.uvs[0], float14);
        drr11.uvs[2] = Mth.lerp(float13, drr11.uvs[2], float14);
        drr11.uvs[1] = Mth.lerp(float13, drr11.uvs[1], float15);
        drr11.uvs[3] = Mth.lerp(float13, drr11.uvs[3], float15);
        final int[] arr13 = this.makeVertices(drr11, eaj, fp, this.setupShape(e1, e2), ecc.getRotation(), drq, boolean8);
        final Direction fp2 = calculateFacing(arr13);
        System.arraycopy(arr12, 0, drr11.uvs, 0, arr12.length);
        if (drq == null) {
            this.recalculateWinding(arr13, fp2);
        }
        return new BakedQuad(arr13, drp.tintIndex, fp2, eaj);
    }
    
    public static BlockFaceUV recomputeUVs(final BlockFaceUV drr, final Direction fp, final Transformation d, final ResourceLocation sm) {
        final Matrix4f b5 = BlockMath.getUVLockTransform(d, fp, () -> "Unable to resolve UVLock for model: " + sm).getMatrix();
        final float float6 = drr.getU(drr.getReverseIndex(0));
        final float float7 = drr.getV(drr.getReverseIndex(0));
        final Vector4f f8 = new Vector4f(float6 / 16.0f, float7 / 16.0f, 0.0f, 1.0f);
        f8.transform(b5);
        final float float8 = 16.0f * f8.x();
        final float float9 = 16.0f * f8.y();
        final float float10 = drr.getU(drr.getReverseIndex(2));
        final float float11 = drr.getV(drr.getReverseIndex(2));
        final Vector4f f9 = new Vector4f(float10 / 16.0f, float11 / 16.0f, 0.0f, 1.0f);
        f9.transform(b5);
        final float float12 = 16.0f * f9.x();
        final float float13 = 16.0f * f9.y();
        float float14;
        float float15;
        if (Math.signum(float10 - float6) == Math.signum(float12 - float8)) {
            float14 = float8;
            float15 = float12;
        }
        else {
            float14 = float12;
            float15 = float8;
        }
        float float16;
        float float17;
        if (Math.signum(float11 - float7) == Math.signum(float13 - float9)) {
            float16 = float9;
            float17 = float13;
        }
        else {
            float16 = float13;
            float17 = float9;
        }
        final float float18 = (float)Math.toRadians(drr.rotation);
        final Vector3f e21 = new Vector3f(Mth.cos(float18), Mth.sin(float18), 0.0f);
        final Matrix3f a22 = new Matrix3f(b5);
        e21.transform(a22);
        final int integer23 = Math.floorMod(-(int)Math.round(Math.toDegrees(Math.atan2(e21.y(), e21.x())) / 90.0) * 90, 360);
        return new BlockFaceUV(new float[] { float14, float16, float15, float17 }, integer23);
    }
    
    private int[] makeVertices(final BlockFaceUV drr, final TextureAtlasSprite eaj, final Direction fp, final float[] arr, final Transformation d, @Nullable final BlockElementRotation drq, final boolean boolean7) {
        final int[] arr2 = new int[32];
        for (int integer10 = 0; integer10 < 4; ++integer10) {
            this.bakeVertex(arr2, integer10, fp, drr, arr, eaj, d, drq, boolean7);
        }
        return arr2;
    }
    
    private int getShadeValue(final Direction fp) {
        final float float3 = this.getShade(fp);
        final int integer4 = Mth.clamp((int)(float3 * 255.0f), 0, 255);
        return 0xFF000000 | integer4 << 16 | integer4 << 8 | integer4;
    }
    
    private float getShade(final Direction fp) {
        switch (fp) {
            case DOWN: {
                return 0.5f;
            }
            case UP: {
                return 1.0f;
            }
            case NORTH:
            case SOUTH: {
                return 0.8f;
            }
            case WEST:
            case EAST: {
                return 0.6f;
            }
            default: {
                return 1.0f;
            }
        }
    }
    
    private float[] setupShape(final Vector3f e1, final Vector3f e2) {
        final float[] arr4 = new float[Direction.values().length];
        arr4[FaceInfo.Constants.MIN_X] = e1.x() / 16.0f;
        arr4[FaceInfo.Constants.MIN_Y] = e1.y() / 16.0f;
        arr4[FaceInfo.Constants.MIN_Z] = e1.z() / 16.0f;
        arr4[FaceInfo.Constants.MAX_X] = e2.x() / 16.0f;
        arr4[FaceInfo.Constants.MAX_Y] = e2.y() / 16.0f;
        arr4[FaceInfo.Constants.MAX_Z] = e2.z() / 16.0f;
        return arr4;
    }
    
    private void bakeVertex(final int[] arr, final int integer, final Direction fp, final BlockFaceUV drr, final float[] arr, final TextureAtlasSprite eaj, final Transformation d, @Nullable final BlockElementRotation drq, final boolean boolean9) {
        final Direction fp2 = Direction.rotate(d.getMatrix(), fp);
        final int integer2 = boolean9 ? this.getShadeValue(fp2) : -1;
        final FaceInfo.VertexInfo b13 = FaceInfo.fromFacing(fp).getVertexInfo(integer);
        final Vector3f e14 = new Vector3f(arr[b13.xFace], arr[b13.yFace], arr[b13.zFace]);
        this.applyElementRotation(e14, drq);
        this.applyModelRotation(e14, d);
        this.fillVertex(arr, integer, e14, integer2, eaj, drr);
    }
    
    private void fillVertex(final int[] arr, final int integer2, final Vector3f e, final int integer4, final TextureAtlasSprite eaj, final BlockFaceUV drr) {
        final int integer5 = integer2 * 8;
        arr[integer5] = Float.floatToRawIntBits(e.x());
        arr[integer5 + 1] = Float.floatToRawIntBits(e.y());
        arr[integer5 + 2] = Float.floatToRawIntBits(e.z());
        arr[integer5 + 3] = integer4;
        arr[integer5 + 4] = Float.floatToRawIntBits(eaj.getU(drr.getU(integer2)));
        arr[integer5 + 4 + 1] = Float.floatToRawIntBits(eaj.getV(drr.getV(integer2)));
    }
    
    private void applyElementRotation(final Vector3f e, @Nullable final BlockElementRotation drq) {
        if (drq == null) {
            return;
        }
        Vector3f e2 = null;
        Vector3f e3 = null;
        switch (drq.axis) {
            case X: {
                e2 = new Vector3f(1.0f, 0.0f, 0.0f);
                e3 = new Vector3f(0.0f, 1.0f, 1.0f);
                break;
            }
            case Y: {
                e2 = new Vector3f(0.0f, 1.0f, 0.0f);
                e3 = new Vector3f(1.0f, 0.0f, 1.0f);
                break;
            }
            case Z: {
                e2 = new Vector3f(0.0f, 0.0f, 1.0f);
                e3 = new Vector3f(1.0f, 1.0f, 0.0f);
                break;
            }
            default: {
                throw new IllegalArgumentException("There are only 3 axes");
            }
        }
        final Quaternion c6 = new Quaternion(e2, drq.angle, true);
        if (drq.rescale) {
            if (Math.abs(drq.angle) == 22.5f) {
                e3.mul(FaceBakery.RESCALE_22_5);
            }
            else {
                e3.mul(FaceBakery.RESCALE_45);
            }
            e3.add(1.0f, 1.0f, 1.0f);
        }
        else {
            e3.set(1.0f, 1.0f, 1.0f);
        }
        this.rotateVertexBy(e, drq.origin.copy(), new Matrix4f(c6), e3);
    }
    
    public void applyModelRotation(final Vector3f e, final Transformation d) {
        if (d == Transformation.identity()) {
            return;
        }
        this.rotateVertexBy(e, new Vector3f(0.5f, 0.5f, 0.5f), d.getMatrix(), new Vector3f(1.0f, 1.0f, 1.0f));
    }
    
    private void rotateVertexBy(final Vector3f e1, final Vector3f e2, final Matrix4f b, final Vector3f e4) {
        final Vector4f f6 = new Vector4f(e1.x() - e2.x(), e1.y() - e2.y(), e1.z() - e2.z(), 1.0f);
        f6.transform(b);
        f6.mul(e4);
        e1.set(f6.x() + e2.x(), f6.y() + e2.y(), f6.z() + e2.z());
    }
    
    public static Direction calculateFacing(final int[] arr) {
        final Vector3f e2 = new Vector3f(Float.intBitsToFloat(arr[0]), Float.intBitsToFloat(arr[1]), Float.intBitsToFloat(arr[2]));
        final Vector3f e3 = new Vector3f(Float.intBitsToFloat(arr[8]), Float.intBitsToFloat(arr[9]), Float.intBitsToFloat(arr[10]));
        final Vector3f e4 = new Vector3f(Float.intBitsToFloat(arr[16]), Float.intBitsToFloat(arr[17]), Float.intBitsToFloat(arr[18]));
        final Vector3f e5 = e2.copy();
        e5.sub(e3);
        final Vector3f e6 = e4.copy();
        e6.sub(e3);
        final Vector3f e7 = e6.copy();
        e7.cross(e5);
        e7.normalize();
        Direction fp8 = null;
        float float9 = 0.0f;
        for (final Direction fp9 : Direction.values()) {
            final Vec3i gg14 = fp9.getNormal();
            final Vector3f e8 = new Vector3f((float)gg14.getX(), (float)gg14.getY(), (float)gg14.getZ());
            final float float10 = e7.dot(e8);
            if (float10 >= 0.0f && float10 > float9) {
                float9 = float10;
                fp8 = fp9;
            }
        }
        if (fp8 == null) {
            return Direction.UP;
        }
        return fp8;
    }
    
    private void recalculateWinding(final int[] arr, final Direction fp) {
        final int[] arr2 = new int[arr.length];
        System.arraycopy(arr, 0, arr2, 0, arr.length);
        final float[] arr3 = new float[Direction.values().length];
        arr3[FaceInfo.Constants.MIN_X] = 999.0f;
        arr3[FaceInfo.Constants.MIN_Y] = 999.0f;
        arr3[FaceInfo.Constants.MIN_Z] = 999.0f;
        arr3[FaceInfo.Constants.MAX_X] = -999.0f;
        arr3[FaceInfo.Constants.MAX_Y] = -999.0f;
        arr3[FaceInfo.Constants.MAX_Z] = -999.0f;
        for (int integer6 = 0; integer6 < 4; ++integer6) {
            final int integer7 = 8 * integer6;
            final float float8 = Float.intBitsToFloat(arr2[integer7]);
            final float float9 = Float.intBitsToFloat(arr2[integer7 + 1]);
            final float float10 = Float.intBitsToFloat(arr2[integer7 + 2]);
            if (float8 < arr3[FaceInfo.Constants.MIN_X]) {
                arr3[FaceInfo.Constants.MIN_X] = float8;
            }
            if (float9 < arr3[FaceInfo.Constants.MIN_Y]) {
                arr3[FaceInfo.Constants.MIN_Y] = float9;
            }
            if (float10 < arr3[FaceInfo.Constants.MIN_Z]) {
                arr3[FaceInfo.Constants.MIN_Z] = float10;
            }
            if (float8 > arr3[FaceInfo.Constants.MAX_X]) {
                arr3[FaceInfo.Constants.MAX_X] = float8;
            }
            if (float9 > arr3[FaceInfo.Constants.MAX_Y]) {
                arr3[FaceInfo.Constants.MAX_Y] = float9;
            }
            if (float10 > arr3[FaceInfo.Constants.MAX_Z]) {
                arr3[FaceInfo.Constants.MAX_Z] = float10;
            }
        }
        final FaceInfo dql6 = FaceInfo.fromFacing(fp);
        for (int integer7 = 0; integer7 < 4; ++integer7) {
            final int integer8 = 8 * integer7;
            final FaceInfo.VertexInfo b9 = dql6.getVertexInfo(integer7);
            final float float10 = arr3[b9.xFace];
            final float float11 = arr3[b9.yFace];
            final float float12 = arr3[b9.zFace];
            arr[integer8] = Float.floatToRawIntBits(float10);
            arr[integer8 + 1] = Float.floatToRawIntBits(float11);
            arr[integer8 + 2] = Float.floatToRawIntBits(float12);
            for (int integer9 = 0; integer9 < 4; ++integer9) {
                final int integer10 = 8 * integer9;
                final float float13 = Float.intBitsToFloat(arr2[integer10]);
                final float float14 = Float.intBitsToFloat(arr2[integer10 + 1]);
                final float float15 = Float.intBitsToFloat(arr2[integer10 + 2]);
                if (Mth.equal(float10, float13) && Mth.equal(float11, float14) && Mth.equal(float12, float15)) {
                    arr[integer8 + 4] = arr2[integer10 + 4];
                    arr[integer8 + 4 + 1] = arr2[integer10 + 4 + 1];
                }
            }
        }
    }
    
    static {
        RESCALE_22_5 = 1.0f / (float)Math.cos(0.39269909262657166) - 1.0f;
        RESCALE_45 = 1.0f / (float)Math.cos(0.7853981852531433) - 1.0f;
    }
}
