import { Explosion } from "./gameObjects.js"
import { preloadImage } from "./preloader.js"
import { SoundEffect } from "./soundEffect.js"

export class Renderer {
  constructor(canvas, backgroundColor) {
    this.canvas = canvas
    this.context = this.canvas.getContext("2d", { alpha: false })
    this.meteors = []
    this.turrets = []
    this.bullets = []
    this.effects = []
    this.mouseX = -50
    this.mouseY = -50
    this.lastFrame = 0
    this.backgroundColor = backgroundColor
    this.bossImg = new Image()
    this.bossY = 0

    this.showStats = false
    this.showCollisionBox = false

    this.loadSprites()
  }

  async loadSprites() {
    this.explosionImg = await preloadImage("assets/img/effects/explosion.webp")
  }

  renderGameObjectSimple(gameObject) {
    this.context.save()
    this.context.translate(gameObject.x, gameObject.y)
    this.context.rotate(gameObject.angle - Math.PI / 2)
    this.context.drawImage(gameObject.sprite, 0, 0, 20, 20)
    this.context.restore()
  }

  renderGameObject(gameObject) {
    let destroy = false
    let posX = gameObject.x
    let posY = gameObject.y

    const applyRotation = gameObject.angle !== 0
    if (applyRotation) {
      this.context.save()
      const gameObjectCenterX = gameObject.x
      const gameObjectCenterY = gameObject.y
      this.context.translate(gameObjectCenterX, gameObjectCenterY)
      this.context.rotate(gameObject.angle + Math.PI / 2)
      this.context.translate(-gameObject.sizeX / 2, -gameObject.sizeY)
      posX = 0
      posY = 0
      // console.log(gameObject.sizeX, gameObject.sizeY, gameObjectCenterX, gameObjectCenterY, gameObject.angle)
    }

    if (gameObject.isAnimated) {
      const frameX = gameObject.heightX * (gameObject.framesCounter % 8)
      const frameY = gameObject.heightY * Math.floor(gameObject.framesCounter / 8)
      this.context.drawImage(gameObject.sprite, frameX, frameY, gameObject.heightX, gameObject.heightY, posX, posY, gameObject.sizeX, gameObject.sizeY)

      if (gameObject.animationRunning) {
        gameObject.framesCounter++
        if (gameObject.framesCounter === gameObject.frames) {
          if (gameObject.removeAfterAnimation) {
            destroy = true
          } else {
            gameObject.framesCounter = 0
          }
        }
      }

    } else {
      this.context.drawImage(gameObject.sprite, posX, posY, gameObject.sizeX, gameObject.sizeY)
    }

    if (applyRotation) {
      this.context.restore()
    }

    if (this.showCollisionBox) {
      this.renderCollisionBox(gameObject)
    }

    return destroy
  }

  renderBackground() {
    this.context.fillStyle = this.backgroundColor
    this.context.fillRect(0, 0, this.canvas.width, this.canvas.height)
    this.context.fill()
  }

  renderDebug(startTime, deltaTime) {
    const endTime = performance.now()
    const fps = Math.round(1 / (deltaTime / 1000))

    this.context.font = '25px serif';
    this.context.fillStyle = 'black'
    this.context.textAlign = 'start'
    this.context.fillText(`FPS: ${fps}`, 10, 50)
    this.context.fillText(`Delta: ${deltaTime}`, 10, 75)
    this.context.fillText(`Frame time: ${endTime - startTime}`, 10, 100)
    this.context.fillText(`Meteors: ${this.meteors.length}`, 10, 125)
    this.context.fillText(`Bullets: ${this.bullets.length}`, 10, 150)
    this.context.fillText(`Mouse x: ${this.mouseX} y:${this.mouseY}`, 10, 175)
  }

  renderTarget() {
    this.context.beginPath()
    this.context.arc(this.mouseX, this.mouseY, 25, 0, 2 * Math.PI, false)
    this.context.lineWidth = 1
    this.context.strokeStyle = 'red'
    this.context.stroke()
  }

  renderCollisionBox(gameObject) {
    this.context.strokeRect(gameObject.x - gameObject.sizeX, gameObject.y - gameObject.sizeY, gameObject.sizeX * 2, gameObject.sizeY * 2)
  }

  destroyMeteor(meteor, i) {
    SoundEffect.explosion.play()
    this.effects.push(new Explosion(meteor.x, meteor.y, this.explosionImg))
    this.meteors.splice(i, 1)
  }

  renderMeteorLoop(deltaTime) {
    let damageTaken = 0
    for (let i = this.meteors.length - 1; i >= 0; i--) {
      const meteor = this.meteors[i]

      meteor.step(deltaTime)

      if (meteor.y > this.canvas.height) {
        damageTaken += 1
        this.meteors.splice(i, 1)
      } else if(meteor.x < 0 || meteor.x > this.canvas.width) {
        this.meteors.splice(i, 1)
      } else if (meteor.health <= 0) {
        this.destroyMeteor(meteor)
      } else if (this.renderGameObject(meteor, i)) {
        this.meteors.splice(i, 1)
      }
    }

    return damageTaken
  }

  renderBulletLoop(deltaTime) {
    for (let i = this.bullets.length - 1; i >= 0; i--) {
      const bullet = this.bullets[i]
      let collision = false

      bullet.step(deltaTime)

      if (
        bullet.y > this.canvas.height || bullet.y < 0 || bullet.x > this.canvas.width || bullet.x < 0
      ) {
        this.bullets.splice(i, 1)
      } else {
        
        let bulletDestroyed = false
        for (let j = this.meteors.length - 1; j >= 0; j--) {
          const meteor = this.meteors[j]
          collision = bullet.collideWith(meteor)
          if (collision) {
            bullet.health--
            meteor.health -= bullet.damage
            this.effects.push(new Explosion(bullet.x, bullet.y, this.explosionImg, bullet.aoeSize * 2))

            if (meteor.health <= 0) {
              this.destroyMeteor(meteor, j)
            }
            
            if (!bullet.percing || bullet.health <= 0) {
              bulletDestroyed = true
              this.bullets.splice(i, 1)
              break
            }
          }
        }

        if (bullet.aoe && collision) {
          for (let j = this.meteors.length - 1; j >= 0; j--) {
            const meteor = this.meteors[j]
            if (bullet.collideWith(meteor, bullet.aoeSize, bullet.aoeSize)) {
              meteor.health -= bullet.damage

              if (meteor.health <= 0) {
                this.destroyMeteor(meteor, j)
              }
            }
          }
        }

        if (!bulletDestroyed) {
          this.renderGameObject(bullet)
        }
      }
    }
  }

  renderTurretLoop() {
    for (const turret of this.turrets) {
      this.renderGameObject(turret)
    }
  }

  renderEffectLoop(deltaTime) {
    for (let i = this.effects.length - 1; i >= 0; i--) {
      const effect = this.effects[i]

      effect.step(deltaTime)

      if (this.renderGameObject(effect)) {
        this.effects.splice(i, 1)
      }
    }
  }

  renderSun() {
    this.context.drawImage(this.bossImg, this.canvas.width / 2, (this.canvas.height + 250) - this.bossY, 100, 100)
  }

  render() {
    const startTime = performance.now()
    const deltaTime = startTime - this.lastFrame

    this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
    this.renderBackground()
    this.renderSun()

    const damageTaken = this.renderMeteorLoop(deltaTime)
    this.renderBulletLoop(deltaTime)
    this.renderTurretLoop()
    this.renderEffectLoop(deltaTime)

    this.renderTarget()

    if (this.showStats) {
      this.renderDebug(startTime, deltaTime)
    }

    this.lastFrame = startTime
    return damageTaken
  }
}