001package fr.aumgn.dac2.shape;
002
003import java.util.ArrayDeque;
004import java.util.Deque;
005import java.util.HashSet;
006import java.util.Set;
007
008import org.bukkit.Material;
009import org.bukkit.World;
010import org.bukkit.block.Block;
011
012import fr.aumgn.bukkitutils.geom.Vector;
013import fr.aumgn.bukkitutils.geom.Vector2D;
014import fr.aumgn.dac2.DAC;
015import fr.aumgn.dac2.exceptions.TooLargeArbitraryShape;
016
017public class ArbitraryFlatShapeVisitor {
018
019    private static final int MAXIMUM_ITERATION = 100000;
020
021    private final DAC dac;
022    private final World world;
023    private final Material material;
024    private final byte data;
025
026    private final Set<Vector> visited;
027    private final Deque<Vector> queue;
028
029    private final Set<Vector2D> points;
030    private int minY;
031    private int maxY;
032    private int count;
033
034    public ArbitraryFlatShapeVisitor(DAC dac, World world, Vector pos) {
035        this.dac = dac;
036        this.world = world;
037        Block block = pos.toBlock(world);
038        this.material = block.getType();
039        this.data = block.getData();
040
041        this.visited = new HashSet<Vector>();
042        this.queue = new ArrayDeque<Vector>();
043        this.points = new HashSet<Vector2D>();
044
045        visited.add(pos);
046        pushNeighbors(pos);
047        points.add(pos.to2D());
048        minY = maxY = pos.getBlockY();
049
050        count = 0;
051    }
052
053    public ArbitraryFlatShape visit() {
054        while (!queue.isEmpty()) {
055            visitQueue();
056
057            count++;
058            if (count > MAXIMUM_ITERATION) {
059                throw new TooLargeArbitraryShape(dac);
060            }
061        }
062
063        return new ArbitraryFlatShape(points, minY, maxY);
064    }
065
066    private void visitQueue() {
067        Vector pos = queue.poll();
068        if (visited.contains(pos)) {
069            return;
070        }
071
072        visited.add(pos);
073        Block block = pos.toBlock(world);
074        if (block.getType() != material || block.getData() != data) {
075            return;
076        }
077
078        points.add(pos.to2D());
079        int y = pos.getBlockY();
080        if (y < minY) {
081            minY = y;
082        }
083        if (y > maxY) {
084            maxY = y;
085        }
086
087        pushNeighbors(pos);
088    }
089
090    private void pushNeighbors(Vector pos) {
091        queue.add(pos.subtractX(1));
092        queue.add(pos.addX(1));
093        queue.add(pos.subtractY(1));
094        queue.add(pos.addY(1));
095        queue.add(pos.subtractZ(1));
096        queue.add(pos.addZ(1));
097    }
098}