export default class v3 {
  constructor () {}

  static dumps (inputVector, separator = " ") {
    return `${inputVector.x}${separator}${inputVector.y}${separator}${inputVector.z}`
  }

  static shiftUp (inputLocation,distance=1) {
    return { x: inputLocation.x, y: inputLocation.y + distance, z: inputLocation.z }
  }

  static xz(inputLocation){
    return{
      x: inputLocation.x,
      y: 0,
      z: inputLocation.z
    }
  }
  static angle(v1, v2){
    return Math.acos((v1.x*v2.x+v1.y*v2.y+v1.z*v2.z)/(this.magnitude(v1)*this.magnitude(v2)))
  }
  static magnitude(v){
    return Math.sqrt(v.x**2+v.y**2+v.z**2)
  }
  static floor (inputLocation) {
    return {
      x: Math.floor(inputLocation.x),
      y: Math.floor(inputLocation.y),
      z: Math.floor(inputLocation.z)
    }
  }

  static center (inputLocation) {
    let floored = v3.floor(inputLocation)
    return {
      x: floored.x + 0.5,
      y: floored.y,
      z: floored.z + 0.5
    }
  }

  // receive a Vector3 location and return a modified Vector3 so I don't modify the original
  static shift (inputLocation, modifX, modifY, modifZ) {
    return {
      x: inputLocation.x + modifX,
      y: inputLocation.y + modifY,
      z: inputLocation.z + modifZ
    }
  }

  static addV3 (vector_1,vector_2){
    return {
      x: vector_1.x + vector_2.x,
      y: vector_1.y + vector_2.y,
      z: vector_1.z + vector_2.z
    }
  }
  static multiply (inputV3,mult){
    return {
      x: inputV3.x * mult,
      y: inputV3.y * mult,
      z: inputV3.z * mult
    }
  }
  static cross( vector_1, vector_2){
    return{
      x: vector_1.y*vector_2.z - vector_1.z*vector_2.y,
      y: vector_1.z*vector_2.x - vector_1.x*vector_2.z,
      z: vector_1.x*vector_2.y - vector_1.y*vector_2.x
    }
  }
  static normalize(inputV3){
    const lenght = this.magnitude(inputV3)
    if(lenght === 0) return { x:0, y:0, z:0}
    return {x: inputV3.x/lenght, y: inputV3.y/lenght, z: inputV3.z/lenght}
  }
  static distance(v1,v2){
    return this.magnitude({
      x:v2.x-v1.x,
      y:v2.y-v1.y,
      z:v2.z-v1.z
    })
  }
  static direction(v1,v2){
    return this.normalize({
      x:v2.x-v1.x,
      y:v2.y-v1.y,
      z:v2.z-v1.z
    })
  }

  // takes a string like "5 20 10" and returns a Vector3 object
  static loads (inputString){
    if(inputString === undefined){
      console.warn("An undefined string has been passed to the 'v3.loads' method")
      return undefined
    }

    if(inputString.length<5){
      console.warn(`An string that is too short has been passed to the 'v3.loads' method: ${inputString}`)
      return undefined
    }

    const stringsArray=inputString.split()
    if(coordsArray.length!==3){
      console.warn(`An unparsable string has been passed to the 'v3.loads' method: ${inputString}`)
      return undefined
    }
    try {
      let coordsArray={
        x: Number(stringsArray[0]),
        y: Number(stringsArray[1]),
        z: Number(stringsArray[2])
      }
    } catch (error) {
      console.warn(`An unparsable string has been passed to the 'v3.loads' method: ${inputString}`)
      return undefined
    }

    return coordsArray

  }

  // receive a Vector3 and use X and Z axes in order to return out an angle rounded to the closest multiple of angleSnap
  static rotationRound(inputV3,angleSnap=90){
    let entityAngle = (Math.atan(inputV3.x, inputV3.z) * 180) / Math.PI
    // console.warn(`v3.angleRound => entityAngle: ${entityAngle}`,true)
    let output = Math.floor(entityAngle + (angleSnap/2)/angleSnap)*angleSnap
    // console.warn(`v3.angleRound => output: ${output}`,true)
    return output
  }

  // receive an angle and return an angle rounded to the closest multiple of angleSnap
  static angleRound (inputValue, angleSnap = 90) {
    return Math.floor((inputValue + angleSnap / 2) / angleSnap) * angleSnap
  }

  // receive a Vector3 + a rotation angle(theoretically multiple of 90). Return the location rotated by that angle
  // formula and explanation here https://youtu.be/fH5MJT3HI58?t=581
  static rotate(inputV3,rotationAngle=0){
    // since js is dumb and doesn't return consistent results for sin and cos, I have to hardcode it
    rotationAngle = (rotationAngle + 360) % 360
    let daCos = 0
    let daSin = 0
    switch (rotationAngle) {
      case 0:
        daSin = 0
        daCos = 1
        break;
      case 90:
        daSin = 1
        daCos = 0
        break;
      case 180:
        daSin = 0
        daCos = -1
        break;
      case 270:
        daSin = -1
        daCos = 0
        break;
      default:
        break;
    }
    const outputV3 = {
      x: (inputV3.x*daCos) - (inputV3.z*daSin),
      y: inputV3.y,
      z: (inputV3.x*daSin) + (inputV3.z*daCos)
    }
    // console.warn(`inputv3: ${v3.dumps(inputV3)} || outputV3: ${v3.dumps(outputV3)})`)

    return outputV3
  }

  // return an int between 0 and max
  static getRandomInt(max) {
    return Math.floor(Math.random() * (max+1));
  }
}
