package net.minecraft.client;

import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.level.ClipContext;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.util.Mth;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;

public class Camera {
    private boolean initialized;
    private BlockGetter level;
    private Entity entity;
    private Vec3 position;
    private final BlockPos.MutableBlockPos blockPosition;
    private final Vector3f forwards;
    private final Vector3f up;
    private final Vector3f left;
    private float xRot;
    private float yRot;
    private final Quaternion rotation;
    private boolean detached;
    private boolean mirror;
    private float eyeHeight;
    private float eyeHeightOld;
    
    public Camera() {
        this.position = Vec3.ZERO;
        this.blockPosition = new BlockPos.MutableBlockPos();
        this.forwards = new Vector3f(0.0f, 0.0f, 1.0f);
        this.up = new Vector3f(0.0f, 1.0f, 0.0f);
        this.left = new Vector3f(1.0f, 0.0f, 0.0f);
        this.rotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
    }
    
    public void setup(final BlockGetter bjg, final Entity akq, final boolean boolean3, final boolean boolean4, final float float5) {
        this.initialized = true;
        this.level = bjg;
        this.entity = akq;
        this.detached = boolean3;
        this.mirror = boolean4;
        this.setRotation(akq.getViewYRot(float5), akq.getViewXRot(float5));
        this.setPosition(Mth.lerp(float5, akq.xo, akq.getX()), Mth.lerp(float5, akq.yo, akq.getY()) + Mth.lerp(float5, this.eyeHeightOld, this.eyeHeight), Mth.lerp(float5, akq.zo, akq.getZ()));
        if (boolean3) {
            if (boolean4) {
                this.setRotation(this.yRot + 180.0f, -this.xRot);
            }
            this.move(-this.getMaxZoom(4.0), 0.0, 0.0);
        }
        else if (akq instanceof LivingEntity && ((LivingEntity)akq).isSleeping()) {
            final Direction fp7 = ((LivingEntity)akq).getBedOrientation();
            this.setRotation((fp7 != null) ? (fp7.toYRot() - 180.0f) : 0.0f, 0.0f);
            this.move(0.0, 0.3, 0.0);
        }
    }
    
    public void tick() {
        if (this.entity != null) {
            this.eyeHeightOld = this.eyeHeight;
            this.eyeHeight += (this.entity.getEyeHeight() - this.eyeHeight) * 0.5f;
        }
    }
    
    private double getMaxZoom(double double1) {
        for (int integer4 = 0; integer4 < 8; ++integer4) {
            float float5 = (float)((integer4 & 0x1) * 2 - 1);
            float float6 = (float)((integer4 >> 1 & 0x1) * 2 - 1);
            float float7 = (float)((integer4 >> 2 & 0x1) * 2 - 1);
            float5 *= 0.1f;
            float6 *= 0.1f;
            float7 *= 0.1f;
            final Vec3 cvl8 = this.position.add(float5, float6, float7);
            final Vec3 cvl9 = new Vec3(this.position.x - this.forwards.x() * double1 + float5 + float7, this.position.y - this.forwards.y() * double1 + float6, this.position.z - this.forwards.z() * double1 + float7);
            final HitResult cvi10 = this.level.clip(new ClipContext(cvl8, cvl9, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this.entity));
            if (cvi10.getType() != HitResult.Type.MISS) {
                final double double2 = cvi10.getLocation().distanceTo(this.position);
                if (double2 < double1) {
                    double1 = double2;
                }
            }
        }
        return double1;
    }
    
    protected void move(final double double1, final double double2, final double double3) {
        final double double4 = this.forwards.x() * double1 + this.up.x() * double2 + this.left.x() * double3;
        final double double5 = this.forwards.y() * double1 + this.up.y() * double2 + this.left.y() * double3;
        final double double6 = this.forwards.z() * double1 + this.up.z() * double2 + this.left.z() * double3;
        this.setPosition(new Vec3(this.position.x + double4, this.position.y + double5, this.position.z + double6));
    }
    
    protected void setRotation(final float float1, final float float2) {
        this.xRot = float2;
        this.yRot = float1;
        this.rotation.set(0.0f, 0.0f, 0.0f, 1.0f);
        this.rotation.mul(Vector3f.YP.rotationDegrees(-float1));
        this.rotation.mul(Vector3f.XP.rotationDegrees(float2));
        this.forwards.set(0.0f, 0.0f, 1.0f);
        this.forwards.transform(this.rotation);
        this.up.set(0.0f, 1.0f, 0.0f);
        this.up.transform(this.rotation);
        this.left.set(1.0f, 0.0f, 0.0f);
        this.left.transform(this.rotation);
    }
    
    protected void setPosition(final double double1, final double double2, final double double3) {
        this.setPosition(new Vec3(double1, double2, double3));
    }
    
    protected void setPosition(final Vec3 cvl) {
        this.position = cvl;
        this.blockPosition.set(cvl.x, cvl.y, cvl.z);
    }
    
    public Vec3 getPosition() {
        return this.position;
    }
    
    public BlockPos getBlockPosition() {
        return this.blockPosition;
    }
    
    public float getXRot() {
        return this.xRot;
    }
    
    public float getYRot() {
        return this.yRot;
    }
    
    public Quaternion rotation() {
        return this.rotation;
    }
    
    public Entity getEntity() {
        return this.entity;
    }
    
    public boolean isInitialized() {
        return this.initialized;
    }
    
    public boolean isDetached() {
        return this.detached;
    }
    
    public FluidState getFluidInCamera() {
        if (!this.initialized) {
            return Fluids.EMPTY.defaultFluidState();
        }
        final FluidState coj2 = this.level.getFluidState(this.blockPosition);
        if (!coj2.isEmpty() && this.position.y >= this.blockPosition.getY() + coj2.getHeight(this.level, this.blockPosition)) {
            return Fluids.EMPTY.defaultFluidState();
        }
        return coj2;
    }
    
    public final Vector3f getLookVector() {
        return this.forwards;
    }
    
    public final Vector3f getUpVector() {
        return this.up;
    }
    
    public void reset() {
        this.level = null;
        this.entity = null;
        this.initialized = false;
    }
}
