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

package com.hypixel.hytale.builtin.hytalegenerator.scanners;

import com.hypixel.hytale.math.util.FastRandom;
import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize;
import javax.annotation.Nonnull;
import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator;
import javax.annotation.Nullable;
import com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions.BiDouble2DoubleFunction;

public class ColumnRandomScanner extends Scanner
{
    private final int minY;
    private final int maxY;
    private final boolean isRelativeToPosition;
    @Nullable
    private final BiDouble2DoubleFunction bedFunction;
    private final int resultsCap;
    @Nonnull
    private final SeedGenerator seedGenerator;
    @Nonnull
    private final Strategy strategy;
    @Nonnull
    private final SpaceSize scanSpaceSize;
    
    public ColumnRandomScanner(final int minY, final int maxY, final int resultsCap, final int seed, @Nonnull final Strategy strategy, final boolean isRelativeToPosition, @Nullable final BiDouble2DoubleFunction bedFunction) {
        if (resultsCap < 0) {
            throw new IllegalArgumentException();
        }
        this.bedFunction = bedFunction;
        this.minY = minY;
        this.maxY = maxY;
        this.isRelativeToPosition = isRelativeToPosition;
        this.resultsCap = resultsCap;
        this.seedGenerator = new SeedGenerator(seed);
        this.strategy = strategy;
        this.scanSpaceSize = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(1, 0, 1));
    }
    
    @Nonnull
    @Override
    public List<Vector3i> scan(@Nonnull final Context context) {
        return switch (this.strategy.ordinal()) {
            default -> throw new MatchException(null, null);
            case 0 -> this.scanDartThrow(context);
            case 1 -> this.scanPickValid(context);
        };
    }
    
    @Nonnull
    private List<Vector3i> scanPickValid(@Nonnull final Context context) {
        if (this.resultsCap == 0) {
            return Collections.emptyList();
        }
        int scanMinY;
        int scanMaxY;
        if (this.isRelativeToPosition) {
            scanMinY = Math.max(context.position.y + this.minY, context.materialSpace.minY());
            scanMaxY = Math.min(context.position.y + this.maxY, context.materialSpace.maxY());
        }
        else if (this.bedFunction != null) {
            final int bedY = (int)this.bedFunction.apply(context.position.x, context.position.z);
            scanMinY = Math.max(bedY + this.minY, context.materialSpace.minY());
            scanMaxY = Math.min(bedY + this.maxY, context.materialSpace.maxY());
        }
        else {
            scanMinY = Math.max(this.minY, context.materialSpace.minY());
            scanMaxY = Math.min(this.maxY, context.materialSpace.maxY());
        }
        final int numberOfPossiblePositions = Math.max(0, scanMaxY - scanMinY);
        final ArrayList<Vector3i> validPositions = new ArrayList<Vector3i>(numberOfPossiblePositions);
        final Vector3i patternPosition = context.position.clone();
        final Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace, context.workerId);
        for (int y = scanMinY; y < scanMaxY; ++y) {
            patternPosition.y = y;
            if (context.pattern.matches(patternContext)) {
                final Vector3i position = context.position.clone();
                position.setY(y);
                validPositions.add(position);
            }
        }
        if (validPositions.isEmpty()) {
            return validPositions;
        }
        if (validPositions.size() <= this.resultsCap) {
            return validPositions;
        }
        final ArrayList<Integer> usedIndices = new ArrayList<Integer>(this.resultsCap);
        final ArrayList<Vector3i> outPositions = new ArrayList<Vector3i>(this.resultsCap);
        final FastRandom random = new FastRandom(this.seedGenerator.seedAt(context.position.x, context.position.y, context.position.z));
        for (int i = 0; i < this.resultsCap; ++i) {
            final int pickedIndex = random.nextInt(validPositions.size());
            if (!usedIndices.contains(pickedIndex)) {
                usedIndices.add(pickedIndex);
                outPositions.add(validPositions.get(pickedIndex));
            }
        }
        return outPositions;
    }
    
    @Nonnull
    private List<Vector3i> scanDartThrow(@Nonnull final Context context) {
        if (this.resultsCap == 0) {
            return Collections.emptyList();
        }
        final int scanMinY = this.isRelativeToPosition ? Math.max(context.position.y + this.minY, context.materialSpace.minY()) : Math.max(this.minY, context.materialSpace.minY());
        final int scanMaxY = this.isRelativeToPosition ? Math.min(context.position.y + this.maxY, context.materialSpace.maxY()) : Math.min(this.maxY, context.materialSpace.maxY());
        final int range = scanMaxY - scanMinY;
        if (range == 0) {
            return Collections.emptyList();
        }
        final int TRY_MULTIPLIER = 1;
        final int numberOfTries = range * 1;
        final ArrayList<Vector3i> validPositions = new ArrayList<Vector3i>(this.resultsCap);
        final FastRandom random = new FastRandom(this.seedGenerator.seedAt(context.position.x, context.position.y, context.position.z));
        final ArrayList<Integer> usedYs = new ArrayList<Integer>(this.resultsCap);
        final Vector3i patternPosition = context.position.clone();
        final Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace, context.workerId);
        for (int i = 0; i < numberOfTries; ++i) {
            patternPosition.y = random.nextInt(range) + scanMinY;
            if (context.pattern.matches(patternContext)) {
                if (!usedYs.contains(patternPosition.y)) {
                    usedYs.add(patternPosition.y);
                    final Vector3i position = patternPosition.clone();
                    validPositions.add(position);
                    if (validPositions.size() == this.resultsCap) {
                        break;
                    }
                }
            }
        }
        return validPositions;
    }
    
    @Nonnull
    @Override
    public SpaceSize scanSpace() {
        return this.scanSpaceSize.clone();
    }
    
    public enum Strategy
    {
        DART_THROW, 
        PICK_VALID;
    }
}
