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

package com.hypixel.hytale.procedurallib.logic.cell.evaluator;

import com.hypixel.hytale.procedurallib.logic.DoubleArray;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.procedurallib.logic.ResultBuffer;
import com.hypixel.hytale.procedurallib.logic.cell.CellDistanceFunction;
import com.hypixel.hytale.procedurallib.logic.cell.jitter.CellJitter;
import javax.annotation.Nonnull;
import com.hypixel.hytale.procedurallib.logic.cell.CellPointFunction;
import com.hypixel.hytale.math.vector.Vector2i;

public class BranchEvaluator implements PointEvaluator
{
    protected static final int CARDINAL_MASK = 1;
    protected static final int CARDINAL_MASK_RESULT_X = 0;
    protected static final int CARDINAL_MASK_RESULT_Y = 1;
    protected static final int RANDOM_DIRECTION_MASK = 3;
    protected static final Vector2i[] RANDOM_DIRECTIONS;
    @Nonnull
    protected final CellPointFunction pointFunction;
    protected final Direction direction;
    protected final CellJitter jitter;
    protected final double branch2parentScale;
    protected final double invLineNormalization;
    
    public BranchEvaluator(@Nonnull final CellDistanceFunction parentFunction, @Nonnull final CellPointFunction linePointFunction, final Direction direction, final CellJitter jitter, final double branchScale) {
        this.pointFunction = linePointFunction;
        this.direction = direction;
        this.jitter = jitter;
        final double inverseScalar = 1.0 / linePointFunction.scale(branchScale);
        this.branch2parentScale = parentFunction.scale(inverseScalar);
        this.invLineNormalization = 1.0 / linePointFunction.normalize(1.0);
    }
    
    @Override
    public CellJitter getJitter() {
        return this.jitter;
    }
    
    @Override
    public void evalPoint(final int seed, final double x, final double y, final int hashA, final int cax, final int cay, final double ax, final double ay, @Nonnull final ResultBuffer.ResultBuffer2d buffer) {
        final int dx = getConnectionX(this.direction, buffer.ix2, buffer.x2, hashA, ax * this.branch2parentScale);
        final int dy = getConnectionY(this.direction, buffer.ix2, buffer.y2, hashA, ay * this.branch2parentScale);
        final int cbx = cax + dx;
        final int cby = cay + dy;
        final int hashB = this.pointFunction.getHash(seed, cbx, cby);
        final DoubleArray.Double2 offsetsB = this.pointFunction.getOffsets(hashB);
        final double rawBx = this.getJitter().getPointX(cbx, offsetsB);
        final double rawBy = this.getJitter().getPointY(cby, offsetsB);
        final double bx = this.pointFunction.getX(rawBx, rawBy);
        final double by = this.pointFunction.getY(rawBx, rawBy);
        if (!checkBounds(x, y, ax, ay, bx, by, buffer.distance2)) {
            return;
        }
        double dist2 = MathUtil.distanceToLineSq(x, y, ax, ay, bx, by);
        dist2 *= this.invLineNormalization;
        buffer.register(hashA, cax, cay, dist2, ax, ay);
    }
    
    @Override
    public void evalPoint2(final int seed, final double x, final double y, final int cellHash, final int xi, final int yi, final double vecX, final double vecY, final ResultBuffer.ResultBuffer2d buffer) {
    }
    
    @Override
    public void evalPoint(final int seed, final double x, final double y, final double z, final int cellHash, final int cellX, final int cellY, final int cellZ, final double cellPointX, final double cellPointY, final double cellPointZ, final ResultBuffer.ResultBuffer3d buffer) {
    }
    
    @Override
    public void evalPoint2(final int seed, final double x, final double y, final double z, final int cellHash, final int cellX, final int cellY, final int cellZ, final double cellPointX, final double cellPointY, final double cellPointZ, final ResultBuffer.ResultBuffer3d buffer) {
    }
    
    protected static int getConnectionX(final Direction direction, final int regionHash, final double regionCoord, final int cellHash, final double cellCoord) {
        if ((cellHash & 0x1) != 0x0) {
            return 0;
        }
        return switch (direction.ordinal()) {
            default -> throw new MatchException(null, null);
            case 1 -> (cellCoord > regionCoord) ? -1 : 1;
            case 0 -> (cellCoord < regionCoord) ? -1 : 1;
            case 2 -> BranchEvaluator.RANDOM_DIRECTIONS[regionHash & 0x3].x;
        };
    }
    
    protected static int getConnectionY(final Direction direction, final int regionHash, final double regionCoord, final int cellHash, final double cellCoord) {
        if ((cellHash & 0x1) != 0x1) {
            return 0;
        }
        return switch (direction.ordinal()) {
            default -> throw new MatchException(null, null);
            case 1 -> (cellCoord > regionCoord) ? -1 : 1;
            case 0 -> (cellCoord < regionCoord) ? -1 : 1;
            case 2 -> BranchEvaluator.RANDOM_DIRECTIONS[regionHash & 0x3].y;
        };
    }
    
    protected static boolean checkBounds(final double x, final double y, final double ax, final double ay, final double bx, final double by, final double thickness) {
        final double minX = Math.min(ax, bx) - thickness;
        final double minY = Math.min(ay, by) - thickness;
        final double maxX = Math.max(ax, bx) + thickness;
        final double maxY = Math.max(ay, by) + thickness;
        return x > minX && x < maxX && y > minY && y < maxY;
    }
    
    static {
        RANDOM_DIRECTIONS = new Vector2i[] { new Vector2i(1, 1), new Vector2i(1, -1), new Vector2i(-1, 1), new Vector2i(-1, -1) };
    }
    
    public enum Direction
    {
        OUTWARD, 
        INWARD, 
        RANDOM;
    }
}
