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

package com.hypixel.hytale.procedurallib.logic;

import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.util.TrigMathUtil;
import com.hypixel.hytale.math.util.HashUtil;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.hypixel.hytale.procedurallib.property.NoiseProperty;
import com.hypixel.hytale.procedurallib.logic.cell.evaluator.PointEvaluator;
import com.hypixel.hytale.procedurallib.logic.cell.CellDistanceFunction;
import com.hypixel.hytale.procedurallib.NoiseFunction;

public class CellNoise implements NoiseFunction
{
    protected final CellDistanceFunction distanceFunction;
    protected final PointEvaluator pointEvaluator;
    protected final CellFunction cellFunction;
    @Nullable
    protected final NoiseProperty noiseLookup;
    
    public CellNoise(final CellDistanceFunction distanceFunction, final PointEvaluator pointEvaluator, final CellFunction cellFunction, @Nullable final NoiseProperty noiseLookup) {
        this.distanceFunction = distanceFunction;
        this.pointEvaluator = pointEvaluator;
        this.cellFunction = cellFunction;
        this.noiseLookup = noiseLookup;
    }
    
    public CellDistanceFunction getDistanceFunction() {
        return this.distanceFunction;
    }
    
    public CellFunction getCellFunction() {
        return this.cellFunction;
    }
    
    @Nullable
    public NoiseProperty getNoiseLookup() {
        return this.noiseLookup;
    }
    
    @Override
    public double get(final int seed, final int offsetSeed, double x, double y) {
        x = this.distanceFunction.scale(x);
        y = this.distanceFunction.scale(y);
        final int xr = this.distanceFunction.getCellX(x, y);
        final int yr = this.distanceFunction.getCellY(x, y);
        final ResultBuffer.ResultBuffer2d buffer = this.localBuffer2d();
        buffer.distance = Double.POSITIVE_INFINITY;
        this.distanceFunction.nearest2D(offsetSeed, x, y, xr, yr, buffer, this.pointEvaluator);
        return GeneralNoise.limit(this.cellFunction.eval(seed, offsetSeed, x, y, buffer, this.distanceFunction, this.noiseLookup)) * 2.0 - 1.0;
    }
    
    @Override
    public double get(final int seed, final int offsetSeed, final double x, final double y, final double z) {
        final int xr = this.distanceFunction.getCellX(x, y, z);
        final int yr = this.distanceFunction.getCellY(x, y, z);
        final int zr = this.distanceFunction.getCellZ(x, y, z);
        final ResultBuffer.ResultBuffer3d buffer = this.localBuffer3d();
        buffer.distance = Double.POSITIVE_INFINITY;
        this.distanceFunction.nearest3D(offsetSeed, x, y, z, xr, yr, zr, buffer, this.pointEvaluator);
        return GeneralNoise.limit(this.cellFunction.eval(seed, offsetSeed, x, y, z, buffer, this.distanceFunction, this.noiseLookup)) * 2.0 - 1.0;
    }
    
    protected ResultBuffer.ResultBuffer2d localBuffer2d() {
        return ResultBuffer.buffer2d;
    }
    
    protected ResultBuffer.ResultBuffer3d localBuffer3d() {
        return ResultBuffer.buffer3d;
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "CellNoise{distanceFunction=" + String.valueOf(this.distanceFunction) + ", pointEvaluator=" + String.valueOf(this.pointEvaluator) + ", cellFunction=" + String.valueOf(this.cellFunction) + ", noiseLookup=" + String.valueOf(this.noiseLookup);
    }
    
    public enum CellMode
    {
        CELL_VALUE((CellFunction)new CellFunction() {
            @Override
            public double eval(final int seed, final int offsetSeed, final double x, final double y, @Nonnull final ResultBuffer.ResultBuffer2d buffer, final CellDistanceFunction cellFunction, final NoiseProperty noiseLookup) {
                return HashUtil.random(offsetSeed, buffer.ix, buffer.iy);
            }
            
            @Override
            public double eval(final int seed, final int offsetSeed, final double x, final double y, final double z, @Nonnull final ResultBuffer.ResultBuffer3d buffer, final CellDistanceFunction cellFunction, final NoiseProperty noiseLookup) {
                return HashUtil.random(offsetSeed, buffer.ix, buffer.iy, buffer.iz);
            }
            
            @Nonnull
            @Override
            public String toString() {
                return "CellValueCellFunction{}";
            }
        }), 
        NOISE_LOOKUP((CellFunction)new CellFunction() {
            @Override
            public double eval(final int seed, final int offsetSeed, final double x, final double y, @Nonnull final ResultBuffer.ResultBuffer2d buffer, @Nonnull final CellDistanceFunction cellFunction, @Nonnull final NoiseProperty noiseLookup) {
                final double px = cellFunction.invScale(buffer.x);
                final double py = cellFunction.invScale(buffer.y);
                return noiseLookup.get(seed, px, py);
            }
            
            @Override
            public double eval(final int seed, final int offsetSeed, final double x, final double y, final double z, @Nonnull final ResultBuffer.ResultBuffer3d buffer, @Nonnull final CellDistanceFunction cellFunction, @Nonnull final NoiseProperty noiseLookup) {
                final double px = cellFunction.invScale(buffer.x);
                final double py = cellFunction.invScale(buffer.y);
                final double pz = cellFunction.invScale(buffer.z);
                return noiseLookup.get(seed, px, py, pz);
            }
            
            @Nonnull
            @Override
            public String toString() {
                return "NoiseLookupCellFunction{}";
            }
        }), 
        DISTANCE((CellFunction)new CellFunction() {
            @Override
            public double eval(final int seed, final int offsetSeed, final double x, final double y, @Nonnull final ResultBuffer.ResultBuffer2d buffer, final CellDistanceFunction cellFunction, final NoiseProperty noiseLookup) {
                return Math.sqrt(buffer.distance);
            }
            
            @Override
            public double eval(final int seed, final int offsetSeed, final double x, final double y, final double z, @Nonnull final ResultBuffer.ResultBuffer3d buffer, final CellDistanceFunction cellFunction, final NoiseProperty noiseLookup) {
                return Math.sqrt(buffer.distance);
            }
            
            @Nonnull
            @Override
            public String toString() {
                return "DistanceCellFunction{}";
            }
        }), 
        DIRECTION((CellFunction)new CellFunction() {
            @Override
            public double eval(final int seed, final int offsetSeed, final double x, final double y, @Nonnull final ResultBuffer.ResultBuffer2d buffer, final CellDistanceFunction cellFunction, final NoiseProperty noiseLookup) {
                final float angle = (float)this.getAngleNoise(seed, offsetSeed, buffer, noiseLookup) * 6.2831855f;
                final float dx = TrigMathUtil.sin(angle);
                final float dy = TrigMathUtil.cos(angle);
                final double ax = buffer.x;
                final double ay = buffer.y;
                final double bx = ax + dx;
                final double by = ay + dy;
                final double distance2 = MathUtil.distanceToInfLineSq(x, y, ax, ay, bx, by);
                final double distance3 = MathUtil.clamp(Math.sqrt(distance2), 0.0, 1.0);
                final int side = MathUtil.sideOfLine(x, y, ax, ay, bx, by);
                return 0.5 + side * distance3 * 0.5;
            }
            
            @Override
            public double eval(final int seed, final int offsetSeed, final double x, final double y, final double z, final ResultBuffer.ResultBuffer3d buffer, final CellDistanceFunction cellFunction, final NoiseProperty noiseLookup) {
                throw new UnsupportedOperationException();
            }
            
            @Nonnull
            @Override
            public String toString() {
                return "DirectionCellFunction{}";
            }
            
            private double getAngleNoise(final int seed, final int offsetSeed, @Nonnull final ResultBuffer.ResultBuffer2d buffer, @Nullable final NoiseProperty noiseProperty) {
                if (noiseProperty != null) {
                    return noiseProperty.get(seed, buffer.x, buffer.y);
                }
                return HashUtil.random(offsetSeed, buffer.ix, buffer.iy);
            }
        });
        
        private final CellFunction function;
        
        private CellMode(final CellFunction function) {
            this.function = function;
        }
        
        public CellFunction getFunction() {
            return this.function;
        }
    }
    
    public interface CellFunction
    {
        double eval(final int p0, final int p1, final double p2, final double p3, final ResultBuffer.ResultBuffer2d p4, final CellDistanceFunction p5, final NoiseProperty p6);
        
        double eval(final int p0, final int p1, final double p2, final double p3, final double p4, final ResultBuffer.ResultBuffer3d p5, final CellDistanceFunction p6, final NoiseProperty p7);
    }
}
