/**
 * SeededRandom.js
 * A deterministic pseudo-random number generator using Mulberry32.
 * Essential for reproducible procedural generation.
 * @class
 */
export class SeededRandom {
  /**
   * @param {number | string} seed - Random seed (number or string)
   */
  constructor(seed) {
    // Hash the string seed to a number if necessary
    if (typeof seed === "string") {
      this.state = this.hashString(seed);
    } else {
      /** @type {number} */
      this.state = seed || Math.floor(Math.random() * 2147483647);
    }
  }

  /**
   * Hashes a string to a number.
   * @param {string} str - String to hash
   * @returns {number} - Hashed value
   * @private
   */
  hashString(str) {
    let hash = 1779033703 ^ str.length;
    for (let i = 0; i < str.length; i++) {
      hash = Math.imul(hash ^ str.charCodeAt(i), 3432918353);
      hash = (hash << 13) | (hash >>> 19);
    }
    return () => {
      hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
      hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
      return hash >>> 0;
    };
  }

  /**
   * Mulberry32 Algorithm - generates next random number.
   * @returns {number} - Random number between 0 and 1
   */
  next() {
    let t = (this.state += 0x6d2b79f5);
    t = Math.imul(t ^ (t >>> 15), t | 1);
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  }

  /**
   * Returns float between [min, max).
   * @param {number} min - Minimum value
   * @param {number} max - Maximum value
   * @returns {number} - Random float
   */
  range(min, max) {
    return min + this.next() * (max - min);
  }

  /**
   * Returns integer between [min, max] (inclusive).
   * @param {number} min - Minimum value
   * @param {number} max - Maximum value
   * @returns {number} - Random integer
   */
  rangeInt(min, max) {
    return Math.floor(this.range(min, max + 1));
  }

  /**
   * Returns true/false based on probability (0.0 - 1.0).
   * @param {number} probability - Probability between 0 and 1
   * @returns {boolean} - True if random value is less than probability
   */
  chance(probability) {
    return this.next() < probability;
  }
}
