package common.rng;

class ImprovedGen {
	private static final double[] dir3X = new double[] { 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 0.0D,
			-1.0D, 0.0D };
	private static final double[] dir3Y = new double[] { 1.0D, 1.0D, -1.0D, -1.0D, 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D,
			1.0D, -1.0D };
	private static final double[] dir3Z = new double[] { 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 1.0D, -1.0D, -1.0D, 1.0D, 1.0D, -1.0D, -1.0D, 0.0D, 1.0D, 0.0D,
			-1.0D };
	private static final double[] dir2X = new double[] { 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 0.0D,
			-1.0D, 0.0D };
	private static final double[] dir2Z = new double[] { 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 1.0D, -1.0D, -1.0D, 1.0D, 1.0D, -1.0D, -1.0D, 0.0D, 1.0D, 0.0D,
			-1.0D };

	private final int[] permutations;
	private final double xCoord;
	private final double yCoord;
	private final double zCoord;

	protected ImprovedGen(Random random) {
		this.permutations = new int[512];
		this.xCoord = random.doublev() * 256.0D;
		this.yCoord = random.doublev() * 256.0D;
		this.zCoord = random.doublev() * 256.0D;

		for(int n = 0; n < 256; this.permutations[n] = n++) {
			;
		}

		for(int n = 0; n < 256; ++n) {
			int pos = random.zrange(256 - n) + n;
			int value = this.permutations[n];
			this.permutations[n] = this.permutations[pos];
			this.permutations[pos] = value;
			this.permutations[n + 256] = this.permutations[n];
		}
	}

	private final double lerp(double d, double n, double m) {
		return n + d * (m - n);
	}

	private final double grad(int n, double xm, double zm) {
		int i = n & 15;
		return dir2X[i] * xm + dir2Z[i] * zm;
	}

	private final double grad(int n, double xm, double ym, double zm) {
		int i = n & 15;
		return dir3X[i] * xm + dir3Y[i] * ym + dir3Z[i] * zm;
	}

	/**
	 * noiseArray should be xSize*ySize*zSize in size
	 */
	public void generate(double[] noise, double xoff, double yoff, double zoff, int xsize, int ysize, int zsize, double xscale, double yscale,
			double zscale, double scale) {
		if(ysize == 1) {
			int i5 = 0;
			int j5 = 0;
			int j = 0;
			int k5 = 0;
			double d14 = 0.0D;
			double d15 = 0.0D;
			int l5 = 0;
			double d16 = 1.0D / scale;

			for(int j2 = 0; j2 < xsize; ++j2) {
				double d17 = xoff + (double)j2 * xscale + this.xCoord;
				int i6 = (int)d17;

				if(d17 < (double)i6) {
					--i6;
				}

				int k2 = i6 & 255;
				d17 = d17 - (double)i6;
				double d18 = d17 * d17 * d17 * (d17 * (d17 * 6.0D - 15.0D) + 10.0D);

				for(int j6 = 0; j6 < zsize; ++j6) {
					double d19 = zoff + (double)j6 * zscale + this.zCoord;
					int k6 = (int)d19;

					if(d19 < (double)k6) {
						--k6;
					}

					int l6 = k6 & 255;
					d19 = d19 - (double)k6;
					double d20 = d19 * d19 * d19 * (d19 * (d19 * 6.0D - 15.0D) + 10.0D);
					i5 = this.permutations[k2] + 0;
					j5 = this.permutations[i5] + l6;
					j = this.permutations[k2 + 1] + 0;
					k5 = this.permutations[j] + l6;
					d14 = this.lerp(d18, this.grad(this.permutations[j5], d17, d19), this.grad(this.permutations[k5], d17 - 1.0D, 0.0D, d19));
					d15 = this.lerp(d18, this.grad(this.permutations[j5 + 1], d17, 0.0D, d19 - 1.0D),
							this.grad(this.permutations[k5 + 1], d17 - 1.0D, 0.0D, d19 - 1.0D));
					double d21 = this.lerp(d20, d14, d15);
					int i7 = l5++;
					noise[i7] += d21 * d16;
				}
			}
		}
		else {
			int i = 0;
			double d0 = 1.0D / scale;
			int k = -1;
			int l = 0;
			int i1 = 0;
			int j1 = 0;
			int k1 = 0;
			int l1 = 0;
			int i2 = 0;
			double d1 = 0.0D;
			double d2 = 0.0D;
			double d3 = 0.0D;
			double d4 = 0.0D;

			for(int l2 = 0; l2 < xsize; ++l2) {
				double d5 = xoff + (double)l2 * xscale + this.xCoord;
				int i3 = (int)d5;

				if(d5 < (double)i3) {
					--i3;
				}

				int j3 = i3 & 255;
				d5 = d5 - (double)i3;
				double d6 = d5 * d5 * d5 * (d5 * (d5 * 6.0D - 15.0D) + 10.0D);

				for(int k3 = 0; k3 < zsize; ++k3) {
					double d7 = zoff + (double)k3 * zscale + this.zCoord;
					int l3 = (int)d7;

					if(d7 < (double)l3) {
						--l3;
					}

					int i4 = l3 & 255;
					d7 = d7 - (double)l3;
					double d8 = d7 * d7 * d7 * (d7 * (d7 * 6.0D - 15.0D) + 10.0D);

					for(int j4 = 0; j4 < ysize; ++j4) {
						double d9 = yoff + (double)j4 * yscale + this.yCoord;
						int k4 = (int)d9;

						if(d9 < (double)k4) {
							--k4;
						}

						int l4 = k4 & 255;
						d9 = d9 - (double)k4;
						double d10 = d9 * d9 * d9 * (d9 * (d9 * 6.0D - 15.0D) + 10.0D);

						if(j4 == 0 || l4 != k) {
							k = l4;
							l = this.permutations[j3] + l4;
							i1 = this.permutations[l] + i4;
							j1 = this.permutations[l + 1] + i4;
							k1 = this.permutations[j3 + 1] + l4;
							l1 = this.permutations[k1] + i4;
							i2 = this.permutations[k1 + 1] + i4;
							d1 = this.lerp(d6, this.grad(this.permutations[i1], d5, d9, d7), this.grad(this.permutations[l1], d5 - 1.0D, d9, d7));
							d2 = this.lerp(d6, this.grad(this.permutations[j1], d5, d9 - 1.0D, d7),
									this.grad(this.permutations[i2], d5 - 1.0D, d9 - 1.0D, d7));
							d3 = this.lerp(d6, this.grad(this.permutations[i1 + 1], d5, d9, d7 - 1.0D),
									this.grad(this.permutations[l1 + 1], d5 - 1.0D, d9, d7 - 1.0D));
							d4 = this.lerp(d6, this.grad(this.permutations[j1 + 1], d5, d9 - 1.0D, d7 - 1.0D),
									this.grad(this.permutations[i2 + 1], d5 - 1.0D, d9 - 1.0D, d7 - 1.0D));
						}

						double d11 = this.lerp(d10, d1, d2);
						double d12 = this.lerp(d10, d3, d4);
						double d13 = this.lerp(d8, d11, d12);
						int j7 = i++;
						noise[j7] += d13 * d0;
					}
				}
			}
		}
	}
}
