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

package com.hypixel.hytale.server.worldgen.zoom;

import it.unimi.dsi.fastutil.ints.IntCollection;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import javax.annotation.Nonnull;

public class PixelDistanceProvider
{
    private static final int TABLE_SIZE = 8;
    @Nonnull
    protected final PixelProvider image;
    protected final int width;
    protected final int height;
    protected final int cellsX;
    protected final int cellsY;
    @Nonnull
    protected final IPixelSet[] table;
    @Nonnull
    protected final IntSet pixels;
    
    public PixelDistanceProvider(@Nonnull final PixelProvider image) {
        this.image = image;
        this.width = image.getWidth();
        this.height = image.getHeight();
        this.cellsX = image.getWidth() / 8;
        this.cellsY = image.getHeight() / 8;
        this.table = new IPixelSet[this.cellsX * this.cellsY];
        this.pixels = new IntOpenHashSet();
        this.prepareSegmentTable();
    }
    
    @Nonnull
    public IntSet getColors() {
        return this.pixels;
    }
    
    public double distanceSqToDifferentPixel(final double ox, final double oy, int px, int py) {
        px = this.clampX(px);
        py = this.clampY(py);
        final int color = this.image.getPixel(px, py);
        final int cellX = px / 8;
        final int cellY = py / 8;
        double distance = Double.POSITIVE_INFINITY;
        final int minX = Math.max(cellX - 1, 0);
        final int maxX = Math.min(cellX + 2, this.cellsX);
        final int minY = Math.max(cellY - 1, 0);
        final int maxY = Math.min(cellY + 2, this.cellsY);
        for (int ix = minX; ix < maxX; ++ix) {
            for (int iy = minY; iy < maxY; ++iy) {
                final double dist = this.distanceSqToDiffInSeq(ox, oy, color, ix, iy);
                if (dist < distance) {
                    distance = dist;
                }
            }
        }
        return distance;
    }
    
    protected double distanceSqToDiffInSeq(final double ox, final double oy, final int pixel, final int cellX, final int cellY) {
        double distSq = Double.POSITIVE_INFINITY;
        if (this.hasDifferentPixel(cellX, cellY, pixel)) {
            final int offsetX = cellX * 8;
            final int offsetY = cellY * 8;
            for (int ix = 0; ix < 8; ++ix) {
                final int px = ix + offsetX;
                for (int iy = 0; iy < 8; ++iy) {
                    final int py = iy + offsetY;
                    if (pixel != this.image.getPixel(px, py)) {
                        final double dist = distanceSqToPixel(ox, oy, px, py);
                        if (dist < distSq) {
                            distSq = dist;
                        }
                    }
                }
            }
        }
        return distSq;
    }
    
    protected boolean hasDifferentPixel(final int cellX, final int cellY, final int pixel) {
        final IPixelSet pixelSet = this.table[this.cellIndex(cellX, cellY)];
        return !pixelSet.contains(pixel) || pixelSet.size() > 1;
    }
    
    private void prepareSegmentTable() {
        for (int cellX = 0; cellX < this.cellsX; ++cellX) {
            for (int cellY = 0; cellY < this.cellsY; ++cellY) {
                final IntSet colors = new IntOpenHashSet();
                final int offsetX = cellX * 8;
                final int offsetY = cellY * 8;
                for (int ix = 0; ix < 8; ++ix) {
                    for (int iy = 0; iy < 8; ++iy) {
                        final int x = ix + offsetX;
                        final int y = iy + offsetY;
                        if (x < this.width) {
                            if (y < this.height) {
                                colors.add(this.image.getPixel(x, y));
                            }
                        }
                    }
                }
                this.pixels.addAll(colors);
                if (colors.size() == 1) {
                    this.table[this.cellIndex(cellX, cellY)] = new SinglePixelSet(colors.iterator().nextInt());
                }
                else {
                    this.table[this.cellIndex(cellX, cellY)] = new MultiplePixelSet(colors);
                }
            }
        }
    }
    
    protected int clampX(final int x) {
        if (x < 0) {
            return 0;
        }
        if (x >= this.width) {
            return this.width - 1;
        }
        return x;
    }
    
    protected int clampY(final int y) {
        if (y < 0) {
            return 0;
        }
        if (y >= this.height) {
            return this.height - 1;
        }
        return y;
    }
    
    protected int cellIndex(final int cellX, final int cellY) {
        return cellX * this.cellsY + cellY;
    }
    
    private static double distanceSqToPixel(final double ox, final double oy, final int px, final int py) {
        final double dx = Math.max(Math.max(px - ox, ox - px - 1.0), 0.0);
        final double dy = Math.max(Math.max(py - oy, oy - py - 1.0), 0.0);
        return dx * dx + dy * dy;
    }
    
    private static class SinglePixelSet implements IPixelSet
    {
        private final int pixel;
        
        SinglePixelSet(final int pixel) {
            this.pixel = pixel;
        }
        
        @Override
        public boolean contains(final int pixel) {
            return this.pixel == pixel;
        }
        
        @Override
        public int size() {
            return 1;
        }
        
        @Nonnull
        @Override
        public String toString() {
            return "SinglePixelSet{pixel=" + this.pixel;
        }
    }
    
    private static class MultiplePixelSet implements IPixelSet
    {
        private final IntSet pixels;
        
        MultiplePixelSet(final IntSet pixels) {
            this.pixels = pixels;
        }
        
        @Override
        public boolean contains(final int pixel) {
            return this.pixels.contains(pixel);
        }
        
        @Override
        public int size() {
            return this.pixels.size();
        }
        
        @Nonnull
        @Override
        public String toString() {
            return "MultiplePixelSet{pixels=" + String.valueOf(this.pixels);
        }
    }
    
    private interface IPixelSet
    {
        boolean contains(final int p0);
        
        int size();
    }
}
