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

package com.hypixel.hytale.server.core.modules.collision;

import com.hypixel.hytale.math.util.MathUtil;
import javax.annotation.Nonnull;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.shape.Box;

public class MovingBoxBoxCollisionEvaluator extends BlockContactData implements IBlockCollisionEvaluator
{
    protected boolean touching;
    protected Box collider;
    @Nonnull
    protected final Vector3d pos;
    @Nonnull
    protected final Vector3d v;
    protected boolean checkForOnGround;
    private boolean computeOverlaps;
    @Nonnull
    protected final Collision1D cX;
    @Nonnull
    protected final Collision1D cY;
    @Nonnull
    protected final Collision1D cZ;
    
    public MovingBoxBoxCollisionEvaluator() {
        this.checkForOnGround = true;
        this.pos = new Vector3d();
        this.v = new Vector3d();
        this.cX = new Collision1D();
        this.cY = new Collision1D();
        this.cZ = new Collision1D();
    }
    
    @Override
    public double getCollisionStart() {
        return this.collisionStart;
    }
    
    @Override
    public void setCollisionData(@Nonnull final BlockCollisionData data, @Nonnull final CollisionConfig collisionConfig, final int hitboxIndex) {
        data.setStart(this.collisionPoint, this.collisionStart);
        data.setEnd(this.collisionEnd, this.collisionNormal);
        data.setBlockData(collisionConfig);
        data.setDetailBoxIndex(hitboxIndex);
        data.setTouchingOverlapping(this.touching, this.isOverlapping());
    }
    
    public boolean isCheckForOnGround() {
        return this.checkForOnGround;
    }
    
    public void setCheckForOnGround(final boolean checkForOnGround) {
        this.checkForOnGround = checkForOnGround;
    }
    
    public boolean isComputeOverlaps() {
        return this.computeOverlaps;
    }
    
    public void setComputeOverlaps(final boolean computeOverlaps) {
        this.computeOverlaps = computeOverlaps;
    }
    
    @Nonnull
    public MovingBoxBoxCollisionEvaluator setCollider(final Box collider) {
        this.collider = collider;
        return this;
    }
    
    @Nonnull
    public MovingBoxBoxCollisionEvaluator setMove(@Nonnull final Vector3d pos, @Nonnull final Vector3d v) {
        this.pos.assign(pos);
        this.v.assign(v);
        this.cX.v = v.x;
        this.cY.v = v.y;
        this.cZ.v = v.z;
        return this;
    }
    
    public boolean isBoundingBoxColliding(@Nonnull final Box blockBoundingBox, final double x, final double y, final double z) {
        this.cX.p = this.pos.x - x;
        this.cY.p = this.pos.y - y;
        this.cZ.p = this.pos.z - z;
        this.onGround = false;
        this.touching = false;
        this.overlapping = false;
        if (!this.cX.isColliding(blockBoundingBox.getMin().x - this.collider.getMax().x, blockBoundingBox.getMax().x - this.collider.getMin().x)) {
            return false;
        }
        if (!this.cY.isColliding(blockBoundingBox.getMin().y - this.collider.getMax().y, blockBoundingBox.getMax().y - this.collider.getMin().y)) {
            return false;
        }
        if (!this.cZ.isColliding(blockBoundingBox.getMin().z - this.collider.getMax().z, blockBoundingBox.getMax().z - this.collider.getMin().z)) {
            return false;
        }
        if (this.cX.kind == 1 && this.cY.kind == 1 && this.cZ.kind == 1) {
            this.overlapping = true;
            if (!this.computeOverlaps) {
                return false;
            }
            this.collisionStart = 0.0;
            this.collisionEnd = Double.MAX_VALUE;
            this.collisionNormal.assign(0.0, 0.0, 0.0);
            if (this.cX.tLeave < this.collisionEnd) {
                this.collisionEnd = this.cX.tLeave;
                this.collisionNormal.assign(this.cX.normal, 0.0, 0.0);
            }
            if (this.cY.tLeave < this.collisionEnd) {
                this.collisionEnd = this.cY.tLeave;
                this.collisionNormal.assign(0.0, this.cY.normal, 0.0);
            }
            if (this.cZ.tLeave < this.collisionEnd) {
                this.collisionEnd = this.cZ.tLeave;
                this.collisionNormal.assign(0.0, 0.0, this.cZ.normal);
            }
            return true;
        }
        else {
            this.collisionStart = -1.7976931348623157E308;
            this.collisionEnd = Double.MAX_VALUE;
            if (this.cX.kind == 0) {
                this.collisionNormal.assign(this.cX.normal, 0.0, 0.0);
                this.collisionStart = this.cX.tEnter;
            }
            if (this.cY.kind == 0 && this.cY.tEnter > this.collisionStart) {
                this.collisionNormal.assign(0.0, this.cY.normal, 0.0);
                this.collisionStart = this.cY.tEnter;
            }
            if (this.cZ.kind == 0 && this.cZ.tEnter > this.collisionStart) {
                this.collisionNormal.assign(0.0, 0.0, this.cZ.normal);
                this.collisionStart = this.cZ.tEnter;
            }
            if (this.collisionStart <= -1.7976931348623157E308) {
                if (this.checkForOnGround && this.cY.kind == 3) {
                    this.collisionStart = MathUtil.maxValue(this.cX.tEnter, this.cY.tEnter, this.cZ.tEnter);
                    this.collisionEnd = MathUtil.minValue(this.cX.tLeave, this.cY.tLeave, this.cZ.tLeave);
                    this.collisionPoint.assign(this.pos);
                    this.collisionPoint.addScaled(this.v, this.collisionStart);
                    this.collisionNormal.assign(0.0, this.cY.normal, 0.0);
                    this.onGround = true;
                    this.touching = true;
                }
                return false;
            }
            this.collisionEnd = MathUtil.minValue(this.cX.tLeave, this.cY.tLeave, this.cZ.tLeave);
            if (this.collisionStart > this.collisionEnd) {
                return false;
            }
            this.collisionPoint.assign(this.pos);
            this.collisionPoint.addScaled(this.v, this.collisionStart);
            if (this.checkForOnGround && this.cY.kind == 3) {
                this.collisionNormal.assign(0.0, this.cY.normal, 0.0);
                this.onGround = true;
                this.touching = true;
                return false;
            }
            this.touching = (this.cX.kind >= 2 || this.cY.kind >= 2 || this.cZ.kind >= 2);
            return !this.touching;
        }
    }
    
    public boolean isTouching() {
        return this.touching;
    }
    
    public void setCollisionEnd(final double collisionEnd) {
        this.collisionEnd = collisionEnd;
    }
    
    private static class Collision1D
    {
        protected static final int COLLISION_OUTSIDE = 0;
        protected static final int COLLISION_INSIDE = 1;
        protected static final int COLLISION_TOUCH_MIN = 2;
        protected static final int COLLISION_TOUCH_MAX = 3;
        public double p;
        public double v;
        public double min;
        public double max;
        public double tEnter;
        public double tLeave;
        public double normal;
        public int kind;
        public boolean touching;
        
        boolean isColliding(final double min, final double max) {
            this.min = min;
            this.max = max;
            this.tEnter = -1.7976931348623157E308;
            this.tLeave = Double.MAX_VALUE;
            this.normal = 0.0;
            this.touching = false;
            double dist = min - this.p;
            if (dist >= -1.0E-5) {
                if (this.v < dist - 1.0E-5) {
                    return false;
                }
                this.normal = -1.0;
                this.computeTouchOrOutside(max, dist, 2);
                return true;
            }
            else {
                dist = max - this.p;
                if (dist > 1.0E-5) {
                    this.tEnter = 0.0;
                    if (this.v < 0.0) {
                        this.tLeave = this.clampPos((min - this.p) / this.v);
                        this.normal = 1.0;
                    }
                    else if (this.v > 0.0) {
                        this.tLeave = this.clampPos((max - this.p) / this.v);
                        this.normal = -1.0;
                    }
                    this.kind = 1;
                    return true;
                }
                if (this.v > dist + 1.0E-5) {
                    return false;
                }
                this.normal = 1.0;
                this.computeTouchOrOutside(min, dist, 3);
                return true;
            }
        }
        
        private void computeTouchOrOutside(final double border, final double dist, final int touchCode) {
            if (this.v != 0.0) {
                this.tEnter = MathUtil.clamp(dist / this.v, 0.0, 1.0);
                if (this.tEnter != 0.0 && this.tEnter < 1.0E-8) {
                    this.tEnter = 0.0;
                }
                this.tLeave = this.clampPos((border - this.p) / this.v);
                this.kind = 0;
            }
            else {
                this.tEnter = 0.0;
                this.kind = touchCode;
            }
        }
        
        private double clampPos(final double v) {
            return (v >= 0.0) ? v : 0.0;
        }
    }
}
