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

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

import java.util.stream.Stream;
import com.hypixel.hytale.procedurallib.logic.CellularNoise;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.procedurallib.logic.point.PointConsumer;
import com.hypixel.hytale.procedurallib.logic.cell.jitter.CellJitter;
import com.hypixel.hytale.procedurallib.logic.cell.evaluator.PointEvaluator;
import javax.annotation.Nonnull;
import com.hypixel.hytale.procedurallib.logic.ResultBuffer;
import com.hypixel.hytale.procedurallib.logic.DoubleArray;

public class HexCellDistanceFunction implements CellDistanceFunction
{
    public static final HexCellDistanceFunction DISTANCE_FUNCTION;
    public static final CellPointFunction POINT_FUNCTION;
    protected static final double X_TO_GRID_X;
    protected static final double Y_TO_GRID_X = -0.3333333333333333;
    protected static final double Y_TO_GRID_Y = 0.6666666666666666;
    protected static final double X_TO_HEX_X;
    protected static final double Y_TO_HEX_X;
    protected static final double Y_TO_HEX_Y = 1.5;
    protected static final double NORMALIZATION = 0.3333333333333333;
    protected static final double SCALE;
    public static final DoubleArray.Double2[] HEX_CELL_2D;
    
    @Override
    public double scale(final double value) {
        return value * HexCellDistanceFunction.SCALE;
    }
    
    @Override
    public double invScale(final double value) {
        return value / HexCellDistanceFunction.SCALE;
    }
    
    @Override
    public int getCellX(final double x, final double y) {
        return toGridX(x, y);
    }
    
    @Override
    public int getCellY(final double x, final double y) {
        return toGridY(x, y);
    }
    
    @Override
    public void nearest2D(final int seed, final double x, final double y, final int cellX, final int cellY, @Nonnull final ResultBuffer.ResultBuffer2d buffer, @Nonnull final PointEvaluator pointEvaluator) {
        this.evalPoint(seed, x, y, cellX - 1, cellY - 1, buffer, pointEvaluator);
        this.evalPoint(seed, x, y, cellX + 0, cellY - 1, buffer, pointEvaluator);
        this.evalPoint(seed, x, y, cellX + 1, cellY - 1, buffer, pointEvaluator);
        this.evalPoint(seed, x, y, cellX - 1, cellY + 0, buffer, pointEvaluator);
        this.evalPoint(seed, x, y, cellX + 0, cellY + 0, buffer, pointEvaluator);
        this.evalPoint(seed, x, y, cellX + 1, cellY + 0, buffer, pointEvaluator);
        this.evalPoint(seed, x, y, cellX - 1, cellY + 1, buffer, pointEvaluator);
        this.evalPoint(seed, x, y, cellX + 0, cellY + 1, buffer, pointEvaluator);
        this.evalPoint(seed, x, y, cellX + 1, cellY + 1, buffer, pointEvaluator);
        buffer.distance *= 0.3333333333333333;
    }
    
    @Override
    public void nearest3D(final int seed, final double x, final double y, final double z, final int cellX, final int cellY, final int cellZ, final ResultBuffer.ResultBuffer3d buffer, final PointEvaluator pointEvaluator) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public void transition2D(final int seed, final double x, final double y, final int cellX, final int cellY, @Nonnull final ResultBuffer.ResultBuffer2d buffer, @Nonnull final PointEvaluator pointEvaluator) {
        this.evalPoint2(seed, x, y, cellX - 1, cellY - 1, buffer, pointEvaluator);
        this.evalPoint2(seed, x, y, cellX + 0, cellY - 1, buffer, pointEvaluator);
        this.evalPoint2(seed, x, y, cellX + 1, cellY - 1, buffer, pointEvaluator);
        this.evalPoint2(seed, x, y, cellX - 1, cellY + 0, buffer, pointEvaluator);
        this.evalPoint2(seed, x, y, cellX + 0, cellY + 0, buffer, pointEvaluator);
        this.evalPoint2(seed, x, y, cellX + 1, cellY + 0, buffer, pointEvaluator);
        this.evalPoint2(seed, x, y, cellX - 1, cellY + 1, buffer, pointEvaluator);
        this.evalPoint2(seed, x, y, cellX + 0, cellY + 1, buffer, pointEvaluator);
        this.evalPoint2(seed, x, y, cellX + 1, cellY + 1, buffer, pointEvaluator);
        final CellJitter jitter = pointEvaluator.getJitter();
        if (jitter.getMaxX() > 0.5) {
            this.evalPoint2(seed, x, y, cellX - 2, cellY - 1, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX - 2, cellY + 0, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX - 2, cellY + 1, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX + 2, cellY + 0, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX + 2, cellY - 1, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX + 2, cellY + 1, buffer, pointEvaluator);
        }
        if (jitter.getMaxY() > 0.5) {
            this.evalPoint2(seed, x, y, cellX - 1, cellY - 2, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX + 0, cellY - 2, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX + 1, cellY - 2, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX - 1, cellY + 2, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX + 0, cellY + 2, buffer, pointEvaluator);
            this.evalPoint2(seed, x, y, cellX + 1, cellY + 2, buffer, pointEvaluator);
        }
        buffer.distance *= 0.3333333333333333;
        buffer.distance2 *= 0.3333333333333333;
    }
    
    @Override
    public void transition3D(final int seed, final double x, final double y, final double z, final int cellX, final int cellY, final int cellZ, final ResultBuffer.ResultBuffer3d buffer, final PointEvaluator pointEvaluator) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public void evalPoint(final int seed, final double x, final double y, final int cellX, final int cellY, final ResultBuffer.ResultBuffer2d buffer, @Nonnull final PointEvaluator pointEvaluator) {
        final int cellHash = getHash(seed, cellX, cellY);
        final DoubleArray.Double2 vec = HexCellDistanceFunction.HEX_CELL_2D[cellHash & 0xFF];
        final CellJitter jitter = pointEvaluator.getJitter();
        final double px = jitter.getPointX(cellX, vec);
        final double py = jitter.getPointY(cellY, vec);
        final double hx = toHexX(px, py);
        final double hy = toHexY(px, py);
        pointEvaluator.evalPoint(seed, x, y, cellHash, cellX, cellY, hx, hy, buffer);
    }
    
    @Override
    public void evalPoint(final int seed, final double x, final double y, final double z, final int cellX, final int cellY, final int cellZ, final ResultBuffer.ResultBuffer3d buffer, final PointEvaluator pointEvaluator) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public void evalPoint2(final int seed, final double x, final double y, final int cellX, final int cellY, final ResultBuffer.ResultBuffer2d buffer, @Nonnull final PointEvaluator pointEvaluator) {
        final int cellHash = getHash(seed, cellX, cellY);
        final DoubleArray.Double2 vec = HexCellDistanceFunction.HEX_CELL_2D[cellHash & 0xFF];
        final CellJitter jitter = pointEvaluator.getJitter();
        final double px = jitter.getPointX(cellX, vec);
        final double py = jitter.getPointY(cellY, vec);
        final double hx = toHexX(px, py);
        final double hy = toHexY(px, py);
        pointEvaluator.evalPoint2(seed, x, y, cellHash, cellX, cellY, hx, hy, buffer);
    }
    
    @Override
    public void evalPoint2(final int seed, final double x, final double y, final double z, final int cellX, final int cellY, final int cellZ, final ResultBuffer.ResultBuffer3d buffer, final PointEvaluator pointEvaluator) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public <T> void collect(final int originalSeed, final int seed, int minX, int minY, int maxX, int maxY, @Nonnull final ResultBuffer.Bounds2d bounds, final T ctx, @Nonnull final PointConsumer<T> collector, @Nonnull final PointEvaluator pointEvaluator) {
        --minX;
        --minY;
        ++maxX;
        final int height = ++maxY - minY;
        final int width = maxX - minX + (height >> 1);
        final CellJitter jitter = pointEvaluator.getJitter();
        for (int dy = 0; dy <= height; ++dy) {
            final int cy = minY + dy;
            final int startX = minX - (dy >> 1);
            for (int dx = 0; dx <= width; ++dx) {
                final int cx = startX + dx;
                final int cellHash = getHash(seed, cx, cy);
                final DoubleArray.Double2 vec = HexCellDistanceFunction.HEX_CELL_2D[cellHash & 0xFF];
                final double px = jitter.getPointX(cx, vec);
                final double py = jitter.getPointY(cy, vec);
                double hx = toHexX(px, py);
                double hy = toHexY(px, py);
                if (bounds.contains(hx, hy)) {
                    hx /= HexCellDistanceFunction.SCALE;
                    hy /= HexCellDistanceFunction.SCALE;
                    pointEvaluator.collectPoint(cellHash, cx, cy, hx, hy, ctx, collector);
                }
            }
        }
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "HexCellDistanceFunction{}";
    }
    
    public static int getHash(final int seed, final int x, final int y) {
        return SquirrelHash.hash(seed, x, y);
    }
    
    public static int toGridX(final double x, final double y) {
        return (int)MathUtil.fastRound(HexCellDistanceFunction.X_TO_GRID_X * x + -0.3333333333333333 * y);
    }
    
    public static int toGridY(final double x, final double y) {
        return (int)MathUtil.fastRound(0.6666666666666666 * y);
    }
    
    public static double toHexX(final double hx, final double hy) {
        return HexCellDistanceFunction.X_TO_HEX_X * hx + HexCellDistanceFunction.Y_TO_HEX_X * hy;
    }
    
    public static double toHexY(final double hx, final double hy) {
        return 1.5 * hy;
    }
    
    static {
        DISTANCE_FUNCTION = new HexCellDistanceFunction();
        POINT_FUNCTION = new CellPointFunction() {
            @Override
            public double scale(final double value) {
                return value * HexCellDistanceFunction.SCALE;
            }
            
            @Override
            public double normalize(final double value) {
                return value * 0.3333333333333333;
            }
            
            @Override
            public int getHash(final int seed, final int cellX, final int cellY) {
                return HexCellDistanceFunction.getHash(seed, cellX, cellY);
            }
            
            @Override
            public double getX(final double x, final double y) {
                return HexCellDistanceFunction.toHexX(x, y);
            }
            
            @Override
            public double getY(final double x, final double y) {
                return HexCellDistanceFunction.toHexY(x, y);
            }
            
            @Override
            public DoubleArray.Double2 getOffsets(final int hash) {
                return HexCellDistanceFunction.HEX_CELL_2D[hash & 0xFF];
            }
        };
        X_TO_GRID_X = Math.sqrt(3.0) / 3.0;
        X_TO_HEX_X = Math.sqrt(3.0);
        Y_TO_HEX_X = Math.sqrt(3.0) / 2.0;
        SCALE = (HexCellDistanceFunction.X_TO_HEX_X + 1.5) / 2.0;
        HEX_CELL_2D = Stream.of(CellularNoise.CELL_2D).map(d -> new DoubleArray.Double2(d.x - 0.5, d.y - 0.5)).toArray(DoubleArray.Double2[]::new);
    }
    
    public static class SquirrelHash
    {
        protected static final int HASH0 = 198491317;
        protected static final int BIT_NOISE1 = -1255572915;
        protected static final int BIT_NOISE2 = -1255572915;
        protected static final int BIT_NOISE3 = -1255572915;
        
        public static int hash(final int seed, final int x, final int y) {
            int hash = x + y * 198491317;
            hash *= -1255572915;
            hash += seed;
            hash ^= hash >> 8;
            hash -= 1255572915;
            hash ^= hash << 8;
            hash *= -1255572915;
            hash ^= hash >> 8;
            return hash;
        }
    }
}
