/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const make = require('../make')
const random = require('../random')

class number extends make {
  /**
   * Returns a number that is more likely to exceed the supplied boundary
   * @param value {number}
   * @private
   */
  static _exceed (value) {
    switch (random.number(4)) {
      case 0:
        // Divisions
        return Math.ceil(value / random.range(2, 4))
      case 1:
        // Powers
        const offset = Math.pow(2, random.range(1, 7))
        return (value > 0) ? (value - offset) : value + offset
      default:
        // Slightly less than limit
        return (value > 0) ? (value - random.number(3)) : value + random.number(3)
    }
  }

  /**
   * Returns a int8 [-128, 127]
   * @param bypass {boolean} - Determines if the range should be exceeded
   * @returns {number}
   */
  static int8 (bypass = false) {
    if (bypass || random.chance(50)) {
      return number._exceed(random.choose([
        [1, -128],
        [10, 127]
      ]))
    }

    return random.range(-128, 127)
  }

  /**
   * Returns a uint8 [0, 255]
   * @param bypass {boolean} - Determines if the range should be exceeded
   * @returns {number}
   */
  static uint8 (bypass = false) {
    if (bypass || random.chance(50)) {
      return number._exceed(255)
    }

    return random.range(0, 255)
  }

  /**
   * Returns a int16 [-32768, 32767]
   * @param bypass {boolean} - Determines if the range should be exceeded
   * @returns {number}
   */
  static int16 (bypass = false) {
    if (bypass || random.chance(50)) {
      return number._exceed(random.choose([
        [1, -32768],
        [10, 32767]
      ]))
    }

    return random.range(-32768, 32767)
  }

  /**
   * Returns a uint16 [0, 65535]
   * @param bypass {boolean} - Determines if the range should be exceeded
   * @returns {*}
   */
  static uint16 (bypass = false) {
    if (bypass || random.chance(50)) {
      return number._exceed(65535)
    }

    return random.range(-0, 65535)
  }

  /**
   * Returns a int32 [-2147483648, 2147483647]
   * @param bypass {boolean} - Determines if the range should be exceeded
   * @returns {number}
   */
  static int32 (bypass = false) {
    if (bypass || random.chance(50)) {
      return number._exceed(random.choose([
        [1, -2147483648],
        [10, 2147483647]
      ]))
    }

    return random.range(-2147483648, 2147483647)
  }

  /**
   * Returns a uint32 [0, 4294967295]
   * @param bypass {boolean} - Determines if the range should be exceeded
   * @returns {number}
   */
  static uint32 (bypass = false) {
    if (bypass || random.chance(50)) {
      return number._exceed(4294967295)
    }

    return random.range(0, 4294967295)
  }

  /**
   * Returns a random floating point number
   * @returns {number}
   */
  static float () {
    /* if (random.chance(32)) {
      switch (random.number(4)) {
        case 0:
          return random.range(Number.MIN_VALUE, Number.MAX_VALUE)
        case 1:
          return Math.pow(10, 1) / Math.pow(10, random.number(307))
        case 2:
          return Math.pow(2, random.float() * random.float() * 64)
        case 3:
          return Math.pow(10, random.range(1, 9)) / Math.pow(10, random.range(1, 9))
      }
    } */

    return random.float()
  }

  /**
   * Returns a float value within the supplied range
   * @param {number} min - Start value
   * @param {number} max - End value
   * @param {number} precision
   * @returns {number}
   */
  static frange (min, max, precision) {
    let x = Math.random() * (min - max) + max
    if (precision) {
      let power = Math.pow(10, precision || 0)
      x = Math.round(x * power) / power
    }
    return x
  }

  /**
   * Returns a random power of 2 between 1 and 2048
   * @returns {number}
   */
  static tiny () {
    // Calling random.number twice prefers lower values
    return Math.pow(2, random.number(random.number(13)))
  }

  /**
   * Returns a random number adjacent to the supplied number
   * @param {number} number
   * @returns {number}
   */
  static even (number) {
    return number % 2 === 1 ? ++number : number
  }

  /**
   * Returns a random number that may be interesting
   * @returns {number}
   */
  static interesting () {
    return random.choose([
      [50, [-128, -1, 0, 1, 16, 32, 64, 100, 127]],
      [30, [-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767]],
      [1, [-2147483648, -100663046, -32769, 32768, 65535, 65536, 100663045, 2147483647]]
    ])
  }

  /**
   * Returns a random signed number
   * @returns {number}
   */
  static signed () {
    return random.choose([
      [50, [number.int8]],
      [30, [number.int16]],
      [1, [number.int32]]
    ])
  }

  /**
   * Returns a random unsigned number
   * @returns {number}
   */
  static unsigned () {
    return random.choose([
      [50, [number.uint8]],
      [30, [number.uint16]],
      [1, [number.uint32]]
    ])
  }

  /**
   * Returns a random number using the type base number generators above
   * @returns {number}
   */
  static any () {
    return random.choose([
      [50, [number.int8, number.uint8]],
      [30, [number.int16, number.uint16]],
      [1, [number.int32, number.uint32]]
    ])
  }
}

module.exports = number

@@ -3574,32 +3574,33 @@
 ue%0A   * @param %7B
+?
 number%7D precisio
@@ -3658,32 +3658,39 @@
 , max, precision
+ = null
 ) %7B%0A    let x = 
@@ -3788,13 +3788,8 @@
 sion
- %7C%7C 0
 )%0A  

Fix frange arguments