package net.tropicraft.world.genlayer;

import net.minecraft.world.gen.layer.IntCache;

public class GenLayerTropiVoronoiZoom extends GenLayerTropicraft {

	public enum Mode {
		CARTESIAN, MANHATTAN;
	}

	public Mode zoomMode;

    public GenLayerTropiVoronoiZoom(long seed, GenLayerTropicraft parent, Mode zoomMode)
    {
        super(seed);
        super.parent = parent;
        this.zoomMode = zoomMode;
        this.setZoom(1);
    }

    /**
     * Returns a list of integer values generated by this layer. These may be interpreted as temperatures, rainfall
     * amounts, or biomeList[] indices based on the particular GenLayer subclass.
     */
    public int[] getInts(int x, int y, int width, int length)
    {
    	final int randomResolution = 1024;
    	final double half = 0.5D;
    	final double almostTileSize = 3.6D;
    	final double tileSize = 4D;
    	
        x -= 2;
        y -= 2;
        int scaledX = x >> 2;
        int scaledY = y >> 2;
        int scaledWidth = (width >> 2) + 2;
        int scaledLength = (length >> 2) + 2;
        int[] parentValues = this.parent.getInts(scaledX, scaledY, scaledWidth, scaledLength);
        int bitshiftedWidth = scaledWidth - 1 << 2; 
        int bitshiftedLength = scaledLength - 1 << 2;
        int[] aint1 = IntCache.getIntCache(bitshiftedWidth * bitshiftedLength);
        int i;

        for(int j = 0; j < scaledLength - 1; ++j) {
            i = 0;
            int baseValue = parentValues[i + 0 + (j + 0) * scaledWidth];

            for(int advancedValueJ = parentValues[i + 0 + (j + 1) * scaledWidth]; i < scaledWidth - 1; ++i) {
                this.initChunkSeed((long)(i + scaledX << 2), (long)(j + scaledY << 2));
                double offsetY = ((double)this.nextInt(randomResolution) / randomResolution - half) * almostTileSize;
                double offsetX = ((double)this.nextInt(randomResolution) / randomResolution - half) * almostTileSize;
                this.initChunkSeed((long)(i + scaledX + 1 << 2), (long)(j + scaledY << 2));
                double offsetYY = ((double)this.nextInt(randomResolution) / randomResolution - half) * almostTileSize + tileSize;
                double offsetXY = ((double)this.nextInt(randomResolution) / randomResolution - half) * almostTileSize;
                this.initChunkSeed((long)(i + scaledX << 2), (long)(j + scaledY + 1 << 2));
                double offsetYX = ((double)this.nextInt(randomResolution) / randomResolution - half) * almostTileSize;
                double offsetXX = ((double)this.nextInt(randomResolution) / randomResolution - half) * almostTileSize + tileSize;
                this.initChunkSeed((long)(i + scaledX + 1 << 2), (long)(j + scaledY + 1 << 2));
                double offsetYXY = ((double)this.nextInt(randomResolution) / randomResolution - half) * almostTileSize + tileSize;
                double offsetXXY = ((double)this.nextInt(randomResolution) / randomResolution - half) * almostTileSize + tileSize;
                
                int advancedValueI = parentValues[i + 1 + (j + 0) * scaledWidth] & 255;
                int advancedValueIJ = parentValues[i + 1 + (j + 1) * scaledWidth] & 255;

                for(int innerX = 0; innerX < 4; ++innerX) {
                    int index = ((j << 2) + innerX) * bitshiftedWidth + (i << 2);

                    for(int innerY = 0; innerY < 4; ++innerY) {
                    	double baseDistance;
                    	double distanceY;
                    	double distanceX;
                    	double distanceXY;
                    	switch(zoomMode) {
                    		case CARTESIAN:
                                baseDistance = ((double)innerX - offsetX) * ((double)innerX - offsetX) + ((double)innerY - offsetY) * ((double)innerY - offsetY);
                                distanceY = ((double)innerX - offsetXY) * ((double)innerX - offsetXY) + ((double)innerY - offsetYY) * ((double)innerY - offsetYY);
                                distanceX = ((double)innerX - offsetXX) * ((double)innerX - offsetXX) + ((double)innerY - offsetYX) * ((double)innerY - offsetYX);
                                distanceXY = ((double)innerX - offsetXXY) * ((double)innerX - offsetXXY) + ((double)innerY - offsetYXY) * ((double)innerY - offsetYXY);
                                break;
                    		case MANHATTAN:
                            	baseDistance = Math.abs(innerX - offsetX) + Math.abs(innerY - offsetY);
                            	distanceY = Math.abs(innerX - offsetXY) + Math.abs(innerY - offsetYY);
                            	distanceX = Math.abs(innerX - offsetXX) + Math.abs(innerY - offsetYX);
                            	distanceXY = Math.abs(innerX - offsetXXY) + Math.abs(innerY - offsetYXY);
                    			break;
                    		default: 
                                baseDistance = ((double)innerX - offsetX) * ((double)innerX - offsetX) + ((double)innerY - offsetY) * ((double)innerY - offsetY);
                                distanceY = ((double)innerX - offsetXY) * ((double)innerX - offsetXY) + ((double)innerY - offsetYY) * ((double)innerY - offsetYY);
                                distanceX = ((double)innerX - offsetXX) * ((double)innerX - offsetXX) + ((double)innerY - offsetYX) * ((double)innerY - offsetYX);
                                distanceXY = ((double)innerX - offsetXXY) * ((double)innerX - offsetXXY) + ((double)innerY - offsetYXY) * ((double)innerY - offsetYXY);
                    	}
                    	
                        if(baseDistance < distanceY && baseDistance < distanceX && baseDistance < distanceXY) {
                            aint1[index++] = baseValue;
                        } else if(distanceY < baseDistance && distanceY < distanceX && distanceY < distanceXY) {
                            aint1[index++] = advancedValueI;
                        } else if(distanceX < baseDistance && distanceX < distanceY && distanceX < distanceXY) {
                            aint1[index++] = advancedValueJ;
                        } else {
                            aint1[index++] = advancedValueIJ;
                        }
                    }
                }

                baseValue = advancedValueI;
                advancedValueJ = advancedValueIJ;
            }
        }

        int[] aint2 = IntCache.getIntCache(width * length);

        for(i = 0; i < length; ++i) {
            System.arraycopy(aint1, (i + (y & 3)) * bitshiftedWidth + (x & 3), aint2, i * width, width);
        }

        return aint2;
    }
    
    @Override
    public void setZoom(int zoom) {
    	this.zoom = zoom;
    	parent.setZoom(zoom * 4);
    }
    
}