package client.renderer.blockmodel;

import client.renderer.texture.Sprite;
import common.model.ModelRotation;
import common.util.ExtMath;
import common.util.Facing;
import common.util.Matrix4f;
import common.util.Vec3i;
import common.util.Vector3f;
import common.util.Vector4f;

public class FaceBakery
{
	private static enum EnumFaceDirection
	{
	    DOWN(new VertexInformation[]{new VertexInformation(Constants.WEST_INDEX, Constants.DOWN_INDEX, Constants.SOUTH_INDEX), new VertexInformation(Constants.WEST_INDEX, Constants.DOWN_INDEX, Constants.NORTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.DOWN_INDEX, Constants.NORTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.DOWN_INDEX, Constants.SOUTH_INDEX)}),
	    UP(new VertexInformation[]{new VertexInformation(Constants.WEST_INDEX, Constants.UP_INDEX, Constants.NORTH_INDEX), new VertexInformation(Constants.WEST_INDEX, Constants.UP_INDEX, Constants.SOUTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.UP_INDEX, Constants.SOUTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.UP_INDEX, Constants.NORTH_INDEX)}),
	    NORTH(new VertexInformation[]{new VertexInformation(Constants.EAST_INDEX, Constants.UP_INDEX, Constants.NORTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.DOWN_INDEX, Constants.NORTH_INDEX), new VertexInformation(Constants.WEST_INDEX, Constants.DOWN_INDEX, Constants.NORTH_INDEX), new VertexInformation(Constants.WEST_INDEX, Constants.UP_INDEX, Constants.NORTH_INDEX)}),
	    SOUTH(new VertexInformation[]{new VertexInformation(Constants.WEST_INDEX, Constants.UP_INDEX, Constants.SOUTH_INDEX), new VertexInformation(Constants.WEST_INDEX, Constants.DOWN_INDEX, Constants.SOUTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.DOWN_INDEX, Constants.SOUTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.UP_INDEX, Constants.SOUTH_INDEX)}),
	    WEST(new VertexInformation[]{new VertexInformation(Constants.WEST_INDEX, Constants.UP_INDEX, Constants.NORTH_INDEX), new VertexInformation(Constants.WEST_INDEX, Constants.DOWN_INDEX, Constants.NORTH_INDEX), new VertexInformation(Constants.WEST_INDEX, Constants.DOWN_INDEX, Constants.SOUTH_INDEX), new VertexInformation(Constants.WEST_INDEX, Constants.UP_INDEX, Constants.SOUTH_INDEX)}),
	    EAST(new VertexInformation[]{new VertexInformation(Constants.EAST_INDEX, Constants.UP_INDEX, Constants.SOUTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.DOWN_INDEX, Constants.SOUTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.DOWN_INDEX, Constants.NORTH_INDEX), new VertexInformation(Constants.EAST_INDEX, Constants.UP_INDEX, Constants.NORTH_INDEX)});

	    private final VertexInformation[] vertices;

	    private static EnumFaceDirection getFacing(Facing facing)
	    {
	        return FACINGS[facing.getIndex()];
	    }

	    private EnumFaceDirection(VertexInformation[] vertexInfosIn)
	    {
	        this.vertices = vertexInfosIn;
	    }

	    private VertexInformation getVertexInformation(int index)
	    {
	        return this.vertices[index];
	    }
	}

	private static final class Constants {
	    public static final int SOUTH_INDEX = Facing.SOUTH.getIndex();
	    public static final int UP_INDEX = Facing.UP.getIndex();
	    public static final int EAST_INDEX = Facing.EAST.getIndex();
	    public static final int NORTH_INDEX = Facing.NORTH.getIndex();
	    public static final int DOWN_INDEX = Facing.DOWN.getIndex();
	    public static final int WEST_INDEX = Facing.WEST.getIndex();
	}

	private static class VertexInformation {
	    public final int xIndex;
	    public final int yIndex;
	    public final int zIndex;

	    private VertexInformation(int xIndexIn, int yIndexIn, int zIndexIn)
	    {
	        this.xIndex = xIndexIn;
	        this.yIndex = yIndexIn;
	        this.zIndex = zIndexIn;
	    }
	}

    private static final EnumFaceDirection[] FACINGS = new EnumFaceDirection[6];
    private static final float SCALE_ROTATION_22_5 = 1.0F / (float)Math.cos(0.39269909262657166D) - 1.0F;
    private static final float SCALE_ROTATION_GENERAL = 1.0F / (float)Math.cos((Math.PI / 4D)) - 1.0F;

	static {
	    FACINGS[Constants.DOWN_INDEX] = EnumFaceDirection.DOWN;
	    FACINGS[Constants.UP_INDEX] = EnumFaceDirection.UP;
	    FACINGS[Constants.NORTH_INDEX] = EnumFaceDirection.NORTH;
	    FACINGS[Constants.SOUTH_INDEX] = EnumFaceDirection.SOUTH;
	    FACINGS[Constants.WEST_INDEX] = EnumFaceDirection.WEST;
	    FACINGS[Constants.EAST_INDEX] = EnumFaceDirection.EAST;
	}

    public BakedQuad makeBakedQuad(Vector3f posFrom, Vector3f posTo, BlockPartFace face, Sprite sprite, Facing facing, ModelRotation modelRotationIn, BlockPartRotation partRotation, boolean uvLocked, boolean shade)
    {
        int[] aint = this.makeQuadVertexData(face, sprite, facing, this.getPositionsDiv16(posFrom, posTo), modelRotationIn, partRotation, uvLocked, shade);
        Facing enumfacing = getFacingFromVertexData(aint);

        if (uvLocked)
        {
            this.lockUv(aint, enumfacing, face.uv, sprite);
        }

        if (partRotation == null)
        {
            this.applyFacing(aint, enumfacing);
        }

        return new BakedQuad(aint, face.tint, enumfacing);
    }

    private int[] makeQuadVertexData(BlockPartFace partFace, Sprite sprite, Facing facing, float[] p_178405_4_, ModelRotation modelRotationIn, BlockPartRotation partRotation, boolean uvLocked, boolean shade)
    {
        int[] aint = new int[28];

        for (int i = 0; i < 4; ++i)
        {
            this.fillVertexData(aint, i, facing, partFace, p_178405_4_, sprite, modelRotationIn, partRotation, uvLocked, shade);
        }

        return aint;
    }

    private int getFaceShadeColor(Facing facing)
    {
        float f = this.getFaceBrightness(facing);
        int i = ExtMath.clampi((int)(f * 255.0F), 0, 255);
        return -16777216 | i << 16 | i << 8 | i;
    }

    private float getFaceBrightness(Facing facing)
    {
        switch (facing)
        {
            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[] getPositionsDiv16(Vector3f pos1, Vector3f pos2)
    {
        float[] afloat = new float[Facing.values().length];
        afloat[Constants.WEST_INDEX] = pos1.x / 16.0F;
        afloat[Constants.DOWN_INDEX] = pos1.y / 16.0F;
        afloat[Constants.NORTH_INDEX] = pos1.z / 16.0F;
        afloat[Constants.EAST_INDEX] = pos2.x / 16.0F;
        afloat[Constants.UP_INDEX] = pos2.y / 16.0F;
        afloat[Constants.SOUTH_INDEX] = pos2.z / 16.0F;
        return afloat;
    }

    private void fillVertexData(int[] faceData, int vertexIndex, Facing facing, BlockPartFace partFace, float[] p_178402_5_, Sprite sprite, ModelRotation modelRotationIn, BlockPartRotation partRotation, boolean uvLocked, boolean shade)
    {
        Facing enumfacing = modelRotationIn.rotateFace(facing);
        int i = shade ? this.getFaceShadeColor(enumfacing) : -1;
        VertexInformation enumfacedirection$vertexinformation = EnumFaceDirection.getFacing(facing).getVertexInformation(vertexIndex);
        Vector3f vector3f = new Vector3f(p_178402_5_[enumfacedirection$vertexinformation.xIndex], p_178402_5_[enumfacedirection$vertexinformation.yIndex], p_178402_5_[enumfacedirection$vertexinformation.zIndex]);
        this.rotatePart(vector3f, partRotation);
        int j = this.rotateVertex(vector3f, facing, vertexIndex, modelRotationIn, uvLocked);
        this.storeVertexData(faceData, j, vertexIndex, vector3f, i, sprite, partFace.uv);
    }

    private void storeVertexData(int[] faceData, int storeIndex, int vertexIndex, Vector3f position, int shadeColor, Sprite sprite, BlockFaceUV faceUV)
    {
        int i = storeIndex * 7;
        faceData[i] = Float.floatToRawIntBits(position.x);
        faceData[i + 1] = Float.floatToRawIntBits(position.y);
        faceData[i + 2] = Float.floatToRawIntBits(position.z);
        faceData[i + 3] = shadeColor;
        faceData[i + 4] = Float.floatToRawIntBits(sprite.getInterpolatedU((double)faceUV.getU(vertexIndex)));
        faceData[i + 4 + 1] = Float.floatToRawIntBits(sprite.getInterpolatedV((double)faceUV.getV(vertexIndex)));
    }

    private void rotatePart(Vector3f p_178407_1_, BlockPartRotation partRotation)
    {
        if (partRotation != null)
        {
            Matrix4f matrix4f = this.getMatrixIdentity();
            Vector3f vector3f = new Vector3f(0.0F, 0.0F, 0.0F);

            switch (partRotation.axis)
            {
                case X:
                    Matrix4f.rotate(partRotation.angle * 0.017453292F, new Vector3f(1.0F, 0.0F, 0.0F), matrix4f, matrix4f);
                    vector3f.set(0.0F, 1.0F, 1.0F);
                    break;

                case Y:
                    Matrix4f.rotate(partRotation.angle * 0.017453292F, new Vector3f(0.0F, 1.0F, 0.0F), matrix4f, matrix4f);
                    vector3f.set(1.0F, 0.0F, 1.0F);
                    break;

                case Z:
                    Matrix4f.rotate(partRotation.angle * 0.017453292F, new Vector3f(0.0F, 0.0F, 1.0F), matrix4f, matrix4f);
                    vector3f.set(1.0F, 1.0F, 0.0F);
            }

            if (partRotation.rescale)
            {
                if (Math.abs(partRotation.angle) == 22.5F)
                {
                    vector3f.scale(SCALE_ROTATION_22_5);
                }
                else
                {
                    vector3f.scale(SCALE_ROTATION_GENERAL);
                }

                Vector3f.add(vector3f, new Vector3f(1.0F, 1.0F, 1.0F), vector3f);
            }
            else
            {
                vector3f.set(1.0F, 1.0F, 1.0F);
            }

            this.rotateScale(p_178407_1_, new Vector3f(partRotation.origin), matrix4f, vector3f);
        }
    }

    public int rotateVertex(Vector3f position, Facing facing, int vertexIndex, ModelRotation modelRotationIn, boolean uvLocked)
    {
        if (modelRotationIn == ModelRotation.X0_Y0)
        {
            return vertexIndex;
        }
        else
        {
            this.rotateScale(position, new Vector3f(0.5F, 0.5F, 0.5F), modelRotationIn.getMatrix4d(), new Vector3f(1.0F, 1.0F, 1.0F));
            return modelRotationIn.rotateVertex(facing, vertexIndex);
        }
    }

    private void rotateScale(Vector3f position, Vector3f rotationOrigin, Matrix4f rotationMatrix, Vector3f scale)
    {
        Vector4f vector4f = new Vector4f(position.x - rotationOrigin.x, position.y - rotationOrigin.y, position.z - rotationOrigin.z, 1.0F);
        Matrix4f.transform(rotationMatrix, vector4f, vector4f);
        vector4f.x *= scale.x;
        vector4f.y *= scale.y;
        vector4f.z *= scale.z;
        position.set(vector4f.x + rotationOrigin.x, vector4f.y + rotationOrigin.y, vector4f.z + rotationOrigin.z);
    }

    private Matrix4f getMatrixIdentity()
    {
        Matrix4f matrix4f = new Matrix4f();
        matrix4f.setIdentity();
        return matrix4f;
    }

    public static Facing getFacingFromVertexData(int[] faceData)
    {
        Vector3f vector3f = new Vector3f(Float.intBitsToFloat(faceData[0]), Float.intBitsToFloat(faceData[1]), Float.intBitsToFloat(faceData[2]));
        Vector3f vector3f1 = new Vector3f(Float.intBitsToFloat(faceData[7]), Float.intBitsToFloat(faceData[8]), Float.intBitsToFloat(faceData[9]));
        Vector3f vector3f2 = new Vector3f(Float.intBitsToFloat(faceData[14]), Float.intBitsToFloat(faceData[15]), Float.intBitsToFloat(faceData[16]));
        Vector3f vector3f3 = new Vector3f();
        Vector3f vector3f4 = new Vector3f();
        Vector3f vector3f5 = new Vector3f();
        Vector3f.sub(vector3f, vector3f1, vector3f3);
        Vector3f.sub(vector3f2, vector3f1, vector3f4);
        Vector3f.cross(vector3f4, vector3f3, vector3f5);
        float f = (float)Math.sqrt((double)(vector3f5.x * vector3f5.x + vector3f5.y * vector3f5.y + vector3f5.z * vector3f5.z));
        vector3f5.x /= f;
        vector3f5.y /= f;
        vector3f5.z /= f;
        Facing enumfacing = null;
        float f1 = 0.0F;

        for (Facing enumfacing1 : Facing.values())
        {
            Vec3i vec3i = enumfacing1.getDirectionVec();
            Vector3f vector3f6 = new Vector3f((float)vec3i.getX(), (float)vec3i.getY(), (float)vec3i.getZ());
            float f2 = Vector3f.dot(vector3f5, vector3f6);

            if (f2 >= 0.0F && f2 > f1)
            {
                f1 = f2;
                enumfacing = enumfacing1;
            }
        }

        if (enumfacing == null)
        {
            return Facing.UP;
        }
        else
        {
            return enumfacing;
        }
    }

    public void lockUv(int[] p_178409_1_, Facing facing, BlockFaceUV p_178409_3_, Sprite p_178409_4_)
    {
        for (int i = 0; i < 4; ++i)
        {
            this.lockVertexUv(i, p_178409_1_, facing, p_178409_3_, p_178409_4_);
        }
    }

    private void applyFacing(int[] p_178408_1_, Facing p_178408_2_)
    {
        int[] aint = new int[p_178408_1_.length];
        System.arraycopy(p_178408_1_, 0, aint, 0, p_178408_1_.length);
        float[] afloat = new float[Facing.values().length];
        afloat[Constants.WEST_INDEX] = 999.0F;
        afloat[Constants.DOWN_INDEX] = 999.0F;
        afloat[Constants.NORTH_INDEX] = 999.0F;
        afloat[Constants.EAST_INDEX] = -999.0F;
        afloat[Constants.UP_INDEX] = -999.0F;
        afloat[Constants.SOUTH_INDEX] = -999.0F;

        for (int i = 0; i < 4; ++i)
        {
            int j = 7 * i;
            float f = Float.intBitsToFloat(aint[j]);
            float f1 = Float.intBitsToFloat(aint[j + 1]);
            float f2 = Float.intBitsToFloat(aint[j + 2]);

            if (f < afloat[Constants.WEST_INDEX])
            {
                afloat[Constants.WEST_INDEX] = f;
            }

            if (f1 < afloat[Constants.DOWN_INDEX])
            {
                afloat[Constants.DOWN_INDEX] = f1;
            }

            if (f2 < afloat[Constants.NORTH_INDEX])
            {
                afloat[Constants.NORTH_INDEX] = f2;
            }

            if (f > afloat[Constants.EAST_INDEX])
            {
                afloat[Constants.EAST_INDEX] = f;
            }

            if (f1 > afloat[Constants.UP_INDEX])
            {
                afloat[Constants.UP_INDEX] = f1;
            }

            if (f2 > afloat[Constants.SOUTH_INDEX])
            {
                afloat[Constants.SOUTH_INDEX] = f2;
            }
        }

        EnumFaceDirection enumfacedirection = EnumFaceDirection.getFacing(p_178408_2_);

        for (int i1 = 0; i1 < 4; ++i1)
        {
            int j1 = 7 * i1;
            VertexInformation enumfacedirection$vertexinformation = enumfacedirection.getVertexInformation(i1);
            float f8 = afloat[enumfacedirection$vertexinformation.xIndex];
            float f3 = afloat[enumfacedirection$vertexinformation.yIndex];
            float f4 = afloat[enumfacedirection$vertexinformation.zIndex];
            p_178408_1_[j1] = Float.floatToRawIntBits(f8);
            p_178408_1_[j1 + 1] = Float.floatToRawIntBits(f3);
            p_178408_1_[j1 + 2] = Float.floatToRawIntBits(f4);

            for (int k = 0; k < 4; ++k)
            {
                int l = 7 * k;
                float f5 = Float.intBitsToFloat(aint[l]);
                float f6 = Float.intBitsToFloat(aint[l + 1]);
                float f7 = Float.intBitsToFloat(aint[l + 2]);

                if (ExtMath.absf(f5 - f8) < 1.0E-5F && ExtMath.absf(f6 - f3) < 1.0E-5F && ExtMath.absf(f7 - f4) < 1.0E-5F)
                {
                    p_178408_1_[j1 + 4] = aint[l + 4];
                    p_178408_1_[j1 + 4 + 1] = aint[l + 4 + 1];
                }
            }
        }
    }

    private void lockVertexUv(int p_178401_1_, int[] p_178401_2_, Facing facing, BlockFaceUV p_178401_4_, Sprite p_178401_5_)
    {
        int i = 7 * p_178401_1_;
        float f = Float.intBitsToFloat(p_178401_2_[i]);
        float f1 = Float.intBitsToFloat(p_178401_2_[i + 1]);
        float f2 = Float.intBitsToFloat(p_178401_2_[i + 2]);

        if (f < -0.1F || f >= 1.1F)
        {
            f -= (float)ExtMath.floorf(f);
        }

        if (f1 < -0.1F || f1 >= 1.1F)
        {
            f1 -= (float)ExtMath.floorf(f1);
        }

        if (f2 < -0.1F || f2 >= 1.1F)
        {
            f2 -= (float)ExtMath.floorf(f2);
        }

        float f3 = 0.0F;
        float f4 = 0.0F;

        switch (facing)
        {
            case DOWN:
                f3 = f * 16.0F;
                f4 = (1.0F - f2) * 16.0F;
                break;

            case UP:
                f3 = f * 16.0F;
                f4 = f2 * 16.0F;
                break;

            case NORTH:
                f3 = (1.0F - f) * 16.0F;
                f4 = (1.0F - f1) * 16.0F;
                break;

            case SOUTH:
                f3 = f * 16.0F;
                f4 = (1.0F - f1) * 16.0F;
                break;

            case WEST:
                f3 = f2 * 16.0F;
                f4 = (1.0F - f1) * 16.0F;
                break;

            case EAST:
                f3 = (1.0F - f2) * 16.0F;
                f4 = (1.0F - f1) * 16.0F;
        }

        int j = p_178401_4_.getVIndex(p_178401_1_) * 7;
        p_178401_2_[j + 4] = Float.floatToRawIntBits(p_178401_5_.getInterpolatedU((double)f3));
        p_178401_2_[j + 4 + 1] = Float.floatToRawIntBits(p_178401_5_.getInterpolatedV((double)f4));
    }
}
