package tc.oc.pgm.regions;

import java.util.Random;
import java.util.stream.Stream;
import javax.annotation.Nullable;

import org.bukkit.util.Vector;

public abstract class TransformedRegion extends Region.Impl {

    protected final @Inspect Region region;
    protected @Nullable Cuboid bounds;

    public TransformedRegion(Region region) {
        this.region = region;
    }

    @Override
    public Stream<? extends Region> dependencies() {
        return Stream.of(region);
    }

    @Override
    public boolean isBlockBounded() {
        return this.region.isBlockBounded();
    }

    @Override
    public boolean isEmpty() {
        return this.region.isEmpty();
    }

    @Override
    public boolean isEverywhere() {
        return region.isEverywhere();
    }

    @Override
    public Cuboid getBounds() {
        if(this.bounds == null) {
            this.bounds = this.getTransformedBounds();
        }
        return this.bounds;
    }

    /**
     * Generic bounding box transform - transform all 8 vertices
     * and find the minimum bounding box containing them.
     */
    protected Cuboid getTransformedBounds() {
        Vector[] oldVertices = this.region.getBounds().vertices();
        Vector[] newVertices = new Vector[8];
        for(int i = 0; i < oldVertices.length; i++) {
            newVertices[i] = this.transform(oldVertices[i]);
        }
        return Cuboid.enclosing(newVertices);
    }

    @Override
    public boolean contains(Vector point) {
        return this.region.contains(this.untransform(point));
    }

    @Override
    public boolean canGetRandom() {
        return this.region.canGetRandom();
    }

    /**
     * Generic random point generator that simply transforms a point generated by
     * the transformed region. This will work at least with affine transformations,
     * but other types may make the random distribution uneven.
     */
    @Override
    public Vector getRandom(Random random) {
        return this.transform(this.region.getRandom(random));
    }

    protected abstract Vector transform(Vector point);
    protected abstract Vector untransform(Vector point);
}
