/*
NOTE: this implementation is adapted from https://github.com/jwagner/simplex-noise.js
 */

// these #__PURE__ comments help uglifyjs with dead code removal
//
const F2 = /*#__PURE__*/ 0.5 * (Math.sqrt(3.0) - 1.0);
const G2 = /*#__PURE__*/ (3.0 - Math.sqrt(3.0)) / 6.0;
const F3 = 1.0 / 3.0;
const G3 = 1.0 / 6.0;
const F4 = /*#__PURE__*/ (Math.sqrt(5.0) - 1.0) / 4.0;
const G4 = /*#__PURE__*/ (5.0 - Math.sqrt(5.0)) / 20.0;
const grad2 = /*#__PURE__*/ new Int8Array([1, 1,
    -1, 1,
    1, -1,

    -1, -1,
    1, 0,
    -1, 0,

    1, 0,
    -1, 0,
    0, 1,

    0, -1,
    0, 1,
    0, -1]);

// double seems to be faster than single or int's
// probably because most operations are in double precision
const grad3 = /*#__PURE__*/ new Float64Array([1, 1, 0,
    -1, 1, 0,
    1, -1, 0,

    -1, -1, 0,
    1, 0, 1,
    -1, 0, 1,

    1, 0, -1,
    -1, 0, -1,
    0, 1, 1,

    0, -1, 1,
    0, 1, -1,
    0, -1, -1]);

// double is a bit quicker here as well
const grad4 = /*#__PURE__*/ new Float64Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,
    0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,
    1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,
    -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,
    1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,
    -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,
    1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,
    -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);


/**
 * Creates a 2D noise function
 * @param [random] the random function that will be used to build the permutation table
 * @returns {function(x:number, y:number):number} producing values in range -1 .. 1
 */
export function create_simplex_noise_2d(random = Math.random) {
    // allocate continuous chunk of memory
    const buffer = new ArrayBuffer(512 * 2);

    const perm = new Uint8Array(buffer, 0, 512);
    buildPermutationTable(random, perm);

    const permMod12 = new Uint8Array(buffer, 512, 512);

    for (let i = 0; i < 512; i++) {
        permMod12[i] = (perm[i] % 12) * 2;
    }

    /**
     * @param {number} x
     * @param {number} y
     */
    return function noise2D(x, y) {
        // if(!isFinite(x) || !isFinite(y)) return 0;
        let n0 = 0; // Noise contributions from the three corners
        let n1 = 0;
        let n2 = 0;
        // Skew the input space to determine which simplex cell we're in
        const s = (x + y) * F2; // Hairy factor for 2D

        const i = (x + s) | 0;
        const j = (y + s) | 0;

        const t = (i + j) * G2;
        const X0 = i - t; // Unskew the cell origin back to (x,y) space
        const Y0 = j - t;
        const x0 = x - X0; // The x,y distances from the cell origin
        const y0 = y - Y0;
        // For the 2D case, the simplex shape is an equilateral triangle.
        // Determine which simplex we are in.
        let i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
        if (x0 > y0) {
            i1 = 1;
            j1 = 0;
        } // lower triangle, XY order: (0,0)->(1,0)->(1,1)
        else {
            i1 = 0;
            j1 = 1;
        } // upper triangle, YX order: (0,0)->(0,1)->(1,1)
        // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
        // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
        // c = (3-sqrt(3))/6
        const x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
        const y1 = y0 - j1 + G2;
        const x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
        const y2 = y0 - 1.0 + 2.0 * G2;
        // Work out the hashed gradient indices of the three simplex corners
        const ii = i & 255;
        const jj = j & 255;
        // Calculate the contribution from the three corners
        let t0 = 0.5 - x0 * x0 - y0 * y0;

        if (t0 >= 0) {
            const gi0 = ii + perm[jj];
            const gi01 = permMod12[gi0];

            const g0x = grad2[gi01];
            const g0y = grad2[gi01 + 1];

            t0 *= t0;
            // n0 = t0 * t0 * (grad2[gi0] * x0 + grad2[gi0 + 1] * y0); // (x,y) of grad3 used for 2D gradient
            n0 = t0 * t0 * (g0x * x0 + g0y * y0);
        }

        let t1 = 0.5 - x1 * x1 - y1 * y1;

        if (t1 >= 0) {
            const gi1 = ii + i1 + perm[jj + j1];
            const gi11 = permMod12[gi1];

            const g1x = grad2[gi11];
            const g1y = grad2[gi11 + 1];

            t1 *= t1;
            // n1 = t1 * t1 * (grad2[gi1] * x1 + grad2[gi1 + 1] * y1);
            n1 = t1 * t1 * (g1x * x1 + g1y * y1);
        }

        let t2 = 0.5 - x2 * x2 - y2 * y2;

        if (t2 >= 0) {
            const gi2 = ii + 1 + perm[jj + 1];

            const gi21 = permMod12[gi2];

            const g2x = grad2[gi21];
            const g2y = grad2[gi21 + 1];

            t2 *= t2;
            // n2 = t2 * t2 * (grad2[gi2] * x2 + grad2[gi2 + 1] * y2);
            n2 = t2 * t2 * (g2x * x2 + g2y * y2);
        }

        // Add contributions from each corner to get the final noise value.
        // The result is scaled to return values in the interval [-1,1].
        return 70.0 * (n0 + n1 + n2);
    };
}

/**
 * Builds a random permutation table
 * @param {function} random
 * @param {Uint8Array} p result will be written here
 */
function buildPermutationTable(random, p) {
    const table_size = 512;

    const half_table_size = table_size >>> 1;

    for (let i = 0; i < half_table_size; i++) {
        p[i] = i;
    }

    for (let i = 0; i < half_table_size - 1; i++) {
        const r = i + ~~(random() * (256 - i));
        const aux = p[i];

        p[i] = p[r];
        p[r] = aux;
    }

    for (let i = 256; i < table_size; i++) {
        // second half of the table just repeats the first half
        p[i] = p[i - 256];
    }

    return p;
}
