import * as THREE from './three/three.module.js'
// import { api } from '../../../scripts/api.js'
import { OrbitControls } from './three/OrbitControls.js'
import { RoomEnvironment } from './three/RoomEnvironment.js'

const api = {
  apiURL: url => {
    console.log(url)
    return url
  }
}

const visualizer = document.getElementById('visualizer')
const container = document.getElementById('container')
const progressDialog = document.getElementById('progress-dialog')
const progressIndicator = document.getElementById('progress-indicator')

const renderer = new THREE.WebGLRenderer({
  antialias: true,
  extensions: {
    derivatives: true
  }
})
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)

if (container) container.appendChild(renderer.domElement)

const pmremGenerator = new THREE.PMREMGenerator(renderer)

// scene
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x000000)
scene.environment = pmremGenerator.fromScene(
  new RoomEnvironment(renderer),
  0.04
).texture

const ambientLight = new THREE.AmbientLight(0xffffff)

const camera = new THREE.PerspectiveCamera(
  40,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)
camera.position.set(0, 0, 10)
const pointLight = new THREE.PointLight(0xffffff, 15)
camera.add(pointLight)

const controls = new OrbitControls(camera, renderer.domElement)
controls.target.set(0, 0, 0)
controls.update()
controls.enablePan = true
controls.enableDamping = true

// Handle window resize event
window.onresize = function () {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()

  renderer.setSize(window.innerWidth, window.innerHeight)
}

var lastReferenceImage = ''
var lastDepthMap = ''
var needUpdate = false

function frameUpdate () {
  var referenceImage = visualizer?.getAttribute('reference_image')
  var depthMap = visualizer?.getAttribute('depth_map')
  if (referenceImage == lastReferenceImage && depthMap == lastDepthMap) {
    if (needUpdate) {
      controls.update()
      renderer.render(scene, camera)
    }
    requestAnimationFrame(frameUpdate)
  } else {
    needUpdate = false
    scene.clear()
    if (progressDialog) progressDialog.open = true
    lastReferenceImage = referenceImage
    lastDepthMap = depthMap
    if (lastReferenceImage && lastReferenceImage != 'undefined') {
      // console.log('lastReferenceImage',typeof(lastReferenceImage),lastDepthMap)
      main(JSON.parse(lastReferenceImage), JSON.parse(lastDepthMap))
    }
  }
}

const onProgress = function (xhr) {
  if (xhr.lengthComputable) {
    progressIndicator.value = (xhr.loaded / xhr.total) * 100
  }
}

const onError = function (e) {
  console.error(e)
}

async function main (referenceImageParams, depthMapParams) {
  let referenceTexture, depthTexture
  let imageWidth = 10 // Default width
  let imageHeight = 10 // Default height, will be updated based on the image's aspect ratio
  //   console.log('#referenceImageParams', referenceImageParams)
  if (referenceImageParams?.filename) {
    console.log('referenceImageParams', referenceImageParams)
    const referenceImageUrl = api
      .apiURL('/view?' + new URLSearchParams(referenceImageParams))
      .replace(/extensions.*\//, '')
    const referenceImageExt = referenceImageParams.filename.slice(
      referenceImageParams.filename.lastIndexOf('.') + 1
    )

    if (
      referenceImageExt === 'png' ||
      referenceImageExt === 'jpg' ||
      referenceImageExt === 'jpeg'
    ) {
      const referenceImageLoader = new THREE.TextureLoader()
      referenceTexture = await new Promise((resolve, reject) => {
        referenceImageLoader.load(
          referenceImageUrl,
          texture => {
            // Once the image is loaded, update the width and height based on the image's aspect ratio
            imageWidth = 10 // Keep the width as 10
            imageHeight = texture.image.height / (texture.image.width / 10)
            resolve(texture)
          },
          undefined,
          reject
        )
      })
    }
  }

  if (depthMapParams?.filename) {
    const depthMapUrl = api
      .apiURL('/view?' + new URLSearchParams(depthMapParams))
      .replace(/extensions.*\//, '')
    const depthMapExt = depthMapParams.filename.slice(
      depthMapParams.filename.lastIndexOf('.') + 1
    )

    if (
      depthMapExt === 'png' ||
      depthMapExt === 'jpg' ||
      depthMapExt === 'jpeg'
    ) {
      const depthMapLoader = new THREE.TextureLoader()
      depthTexture = await depthMapLoader.loadAsync(depthMapUrl)
    }
  }

  if (referenceTexture && depthTexture) {
    const depthMaterial = new THREE.ShaderMaterial({
      uniforms: {
        referenceTexture: { value: referenceTexture },
        depthTexture: { value: depthTexture },
        depthScale: { value: 5.0 },
        ambientLightColor: { value: new THREE.Color(0.2, 0.2, 0.2) },
        lightPosition: { value: new THREE.Vector3(2, 2, 2) },
        lightColor: { value: new THREE.Color(1, 1, 1) },
        lightIntensity: { value: 1.0 },
        shininess: { value: 30 }
      },
      vertexShader: `
            uniform sampler2D depthTexture;
            uniform float depthScale;
    
            varying vec2 vUv;
            varying float vDepth;
            varying vec3 vNormal;
            varying vec3 vViewPosition;
    
            void main() {
                vUv = uv;
                
                float depth = texture2D(depthTexture, uv).r;
                vec3 displacement = normal * depth * depthScale;
                vec3 displacedPosition = position + displacement;
                
                vec4 worldPosition = modelMatrix * vec4(displacedPosition, 1.0);
                vNormal = normalize(normalMatrix * normal);
                vViewPosition = (viewMatrix * worldPosition).xyz;
                
                gl_Position = projectionMatrix * viewMatrix * worldPosition;
                
                vDepth = depth;
            }
        `,
      fragmentShader: `
            uniform sampler2D referenceTexture;
    
            varying vec2 vUv;
            varying float vDepth;
    
            void main() {
                vec4 referenceColor = texture2D(referenceTexture, vUv);
                
                // Directly use reference color without fog
                gl_FragColor = referenceColor;
            }
        `
    })

    const planeGeometry = new THREE.PlaneGeometry(
      imageWidth,
      imageHeight,
      200,
      200
    )
    const depthMesh = new THREE.Mesh(planeGeometry, depthMaterial)
    scene.add(depthMesh)
  }

  needUpdate = true

  scene.add(ambientLight)
  scene.add(camera)

  progressDialog?.close()

  frameUpdate()
}

document
  .getElementById('screenshotButton')
  ?.addEventListener('click', takeScreenshot)

const sleep = (t = 1000) => {
  return new Promise((res, rej) => {
    setTimeout(() => res(1), t)
  })
}

// 方法：旋转摄像机并拍摄图片 // 每次旋转的角度增量，转换为弧度
async function captureImages (
  totalFrames = 20,
  angleIncrement = 1.5,
  scaleFactor = 1 // 添加放大倍数参数，默认为1
) {
  angleIncrement = THREE.MathUtils.degToRad(angleIncrement)

  // 计算场景中所有物体的中心点
  const box = new THREE.Box3().setFromObject(scene)
  const center = new THREE.Vector3()
  box.getCenter(center)

  // 计算当前相机距离中心点的半径
  const radius = camera.position.distanceTo(center)

  // 存储图片的数组
  let images = []

  // 记录初始相机位置和朝向
  const initialPosition = camera.position.clone()
  const initialTarget = center.clone()

  // 计算当前相机的初始角度
  const initialAngle = Math.atan2(
    camera.position.z - center.z,
    camera.position.x - center.x
  )

  // 起始角度为从当前角度往左旋转 20 度的位置
  const startAngle = initialAngle
  //  - (angleIncrement * totalFrames) / 2

  // 保存原始尺寸
  const originalWidth = renderer.domElement.width
  const originalHeight = renderer.domElement.height

  // 调整渲染器尺寸
  renderer.setSize(
    originalWidth * scaleFactor,
    originalHeight * scaleFactor,
    false
  )

  // 调整相机的视图矩阵（如果需要）
  camera.aspect = (originalWidth * scaleFactor) / (originalHeight * scaleFactor)
  camera.updateProjectionMatrix()

  for (let i = 0; i < totalFrames; i++) {
    const angle = startAngle + i * angleIncrement

    // 计算相机的位置
    camera.position.x = center.x + radius * Math.cos(angle)
    camera.position.z = center.z + radius * Math.sin(angle)
    camera.position.y = initialPosition.y // 保持相机高度不变
    camera.lookAt(center) // 相机看向中心点

    // 渲染当前帧
    renderer.render(scene, camera)

    // 将当前帧保存为图片
    const imgData = renderer.domElement.toDataURL('image/png')
    images.push(imgData)

    // 等待一段时间
    await new Promise(resolve => setTimeout(resolve, 500))
  }

  // 恢复渲染器尺寸
  renderer.setSize(originalWidth, originalHeight, false)

  // 恢复相机的视图矩阵
  camera.aspect = originalWidth / originalHeight
  camera.updateProjectionMatrix()

  // 恢复相机到初始位置和朝向
  camera.position.copy(initialPosition)
  camera.lookAt(initialTarget)

  return images
}

async function takeScreenshot () {
  // 更新相机的矩阵，以确保其世界矩阵是最新的
  camera.updateMatrixWorld()
  const imgs = await captureImages(12,3,4)

  // 获取当前网页的 URL
  const currentUrl = window.location.href

  // 创建一个 URL 对象
  const url = new URL(currentUrl)

  // 使用 URLSearchParams 获取参数
  const params = new URLSearchParams(url.search)

  // 获取参数 'id' 的值
  const id = params.get('id')

  window.parent.postMessage({ imgs, id }, '*')
}

main()

window.addEventListener('message', event => {
  // 这里可以添加对来源的验证，以确保安全
  // console.log('Message received from parent page:', event.data)
  let { reference_image, depth_map } = event.data
  if (reference_image && depth_map) {
    visualizer?.setAttribute('reference_image', JSON.stringify(reference_image))
    visualizer?.setAttribute('depth_map', JSON.stringify(depth_map))
    frameUpdate()
  }
})
