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

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

import com.hypixel.hytale.math.vector.Vector3d;
import java.util.HashSet;
import java.util.Collection;
import java.util.Iterator;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.ArrayList;
import javax.annotation.Nonnull;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize;
import java.util.List;

public class GapPattern extends Pattern
{
    private List<List<PositionedPattern>> axisPositionedPatterns;
    private List<PositionedPattern> depthPositionedPatterns;
    private double gapSize;
    private double anchorSize;
    private double anchorRoughness;
    private int depthDown;
    private int depthUp;
    private Pattern gapPattern;
    private Pattern anchorPattern;
    private SpaceSize readSpaceSize;
    
    public GapPattern(@Nonnull final List<Float> angles, final double gapSize, final double anchorSize, final double anchorRoughness, final int depthDown, final int depthUp, @Nonnull final Pattern gapPattern, @Nonnull final Pattern anchorPattern) {
        if (gapSize < 0.0 || anchorSize < 0.0 || anchorRoughness < 0.0 || depthDown < 0 || depthUp < 0) {
            throw new IllegalArgumentException("negative sizes");
        }
        this.gapSize = gapSize;
        this.anchorSize = anchorSize;
        this.gapPattern = gapPattern;
        this.anchorPattern = anchorPattern;
        this.anchorRoughness = anchorRoughness;
        this.depthDown = depthDown;
        this.depthUp = depthUp;
        this.depthPositionedPatterns = this.renderDepths();
        this.axisPositionedPatterns = new ArrayList<List<PositionedPattern>>(angles.size());
        for (final float angle : angles) {
            final List<PositionedPattern> positions = this.renderPositions(angle);
            this.axisPositionedPatterns.add(positions);
        }
        Vector3i min = null;
        Vector3i max = null;
        for (final List<PositionedPattern> direction : this.axisPositionedPatterns) {
            for (final PositionedPattern pos : direction) {
                if (min == null) {
                    min = pos.position.clone();
                    max = pos.position.clone();
                }
                else {
                    min = Vector3i.min(min, pos.position);
                    max = Vector3i.max(max, pos.position);
                }
            }
        }
        if (max == null) {
            this.readSpaceSize = new SpaceSize(new Vector3i(), new Vector3i());
            return;
        }
        max.add(1, 1, 1);
        this.readSpaceSize = new SpaceSize(min, max);
    }
    
    @Nonnull
    private List<PositionedPattern> renderDepths() {
        final ArrayList<PositionedPattern> positions = new ArrayList<PositionedPattern>();
        Vector3i pointer = new Vector3i();
        final int stepsDown = this.depthDown - 1;
        for (int i = 0; i < this.depthDown; ++i) {
            pointer.add(0, -1, 0);
            positions.add(new PositionedPattern(this.gapPattern, pointer.clone()));
        }
        pointer = new Vector3i();
        final int stepsUp = this.depthUp - 1;
        for (int j = 0; j < this.depthUp; ++j) {
            pointer.add(0, 1, 0);
            positions.add(new PositionedPattern(this.gapPattern, pointer.clone()));
        }
        return positions;
    }
    
    @Nonnull
    private List<PositionedPattern> renderPositions(final float angle) {
        final ArrayList<PositionedPattern> positions = new ArrayList<PositionedPattern>();
        positions.addAll(this.renderHalfPositions(angle));
        positions.addAll(this.renderHalfPositions(3.1415927f + angle));
        final ArrayList<PositionedPattern> uniquePositions = new ArrayList<PositionedPattern>(positions.size());
        final HashSet<Vector3i> positionsSet = new HashSet<Vector3i>();
        for (final PositionedPattern e : positions) {
            if (positionsSet.contains(e.position)) {
                continue;
            }
            uniquePositions.add(e);
            positionsSet.add(e.position);
        }
        return uniquePositions;
    }
    
    @Nonnull
    private List<PositionedPattern> renderHalfPositions(final float angle) {
        final ArrayList<PositionedPattern> positions = new ArrayList<PositionedPattern>();
        double halfGap = this.gapSize / 2.0 - 1.0 - this.anchorRoughness;
        halfGap = Math.max(0.0, halfGap);
        final double halfWall = this.anchorSize / 2.0;
        Vector3d pointer = new Vector3d(0.5, 0.5, 0.5);
        final Vector3d mov = new Vector3d(0.0, 0.0, -1.0);
        mov.rotateY(angle);
        final double stepSize = 0.5;
        mov.setLength(stepSize);
        for (int steps = (int)(halfGap / stepSize), s = 0; s < steps; ++s) {
            pointer.add(mov);
            positions.add(new PositionedPattern(this.gapPattern, pointer.toVector3i()));
        }
        positions.add(new PositionedPattern(this.gapPattern, new Vector3i()));
        pointer = mov.clone().setLength(halfGap).add(0.5, 0.5, 0.5);
        positions.add(new PositionedPattern(this.gapPattern, pointer.toVector3i()));
        final Vector3d anchor = mov.clone().setLength(this.gapSize / 2.0);
        pointer = anchor.clone().add(0.5, 0.5, 0.5);
        positions.add(new PositionedPattern(this.anchorPattern, anchor.toVector3i()));
        mov.rotateY(1.5707964f);
        final int steps = (int)(halfWall / stepSize);
        for (int s2 = 0; s2 < steps; ++s2) {
            pointer.add(mov);
            positions.add(new PositionedPattern(this.anchorPattern, pointer.toVector3i()));
        }
        Vector3d wallTip = anchor.clone().add(0.5, 0.5, 0.5);
        wallTip.add(mov.clone().setLength(halfWall));
        positions.add(new PositionedPattern(this.anchorPattern, wallTip.toVector3i()));
        mov.scale(-1.0);
        pointer = anchor.clone().add(0.5, 0.5, 0.5);
        for (int s3 = 0; s3 < steps; ++s3) {
            pointer.add(mov);
            positions.add(new PositionedPattern(this.anchorPattern, pointer.toVector3i()));
        }
        wallTip = anchor.clone().add(0.5, 0.5, 0.5);
        wallTip.add(mov.clone().setLength(halfWall));
        positions.add(new PositionedPattern(this.anchorPattern, wallTip.toVector3i()));
        return positions;
    }
    
    @Override
    public boolean matches(@Nonnull final Context context) {
        final Vector3i childPosition = new Vector3i();
        final Context childContext = new Context(context);
        childContext.position = childPosition;
        for (final PositionedPattern entry : this.depthPositionedPatterns) {
            childPosition.assign(entry.position).add(context.position);
            if (!entry.pattern.matches(childContext)) {
                return false;
            }
        }
        for (final List<PositionedPattern> patternsInDirection : this.axisPositionedPatterns) {
            boolean matchesDirection = true;
            for (final PositionedPattern entry2 : patternsInDirection) {
                childPosition.assign(entry2.position).add(context.position);
                if (entry2.pattern.matches(context)) {
                    continue;
                }
                matchesDirection = false;
                break;
            }
            if (matchesDirection) {
                return true;
            }
        }
        return false;
    }
    
    @Nonnull
    @Override
    public SpaceSize readSpace() {
        return this.readSpaceSize.clone();
    }
    
    public static class PositionedPattern
    {
        private Vector3i position;
        private Pattern pattern;
        
        public PositionedPattern(@Nonnull final Pattern pattern, @Nonnull final Vector3i position) {
            this.pattern = pattern;
            this.position = position.clone();
        }
        
        public int getX() {
            return this.position.x;
        }
        
        public int getY() {
            return this.position.y;
        }
        
        public int getZ() {
            return this.position.z;
        }
        
        public Pattern getPattern() {
            return this.pattern;
        }
        
        @Nonnull
        @Override
        protected PositionedPattern clone() {
            return new PositionedPattern(this.pattern, this.position.clone());
        }
    }
}
