import React, { useEffect, useMemo, useRef, useState } from 'react';
import './App.scss';
import * as THREE from "three";
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader.js"

import { Tween, Easing, update } from "three/examples/jsm/libs/tween.module.js";

// import { CSS2DRenderer, CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer.js";
import Huajuan from "./components/huajuan";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js";
import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader.js";

import {
  loading_bg,
  img_culture,
  img_loading,
  label_title,
  logo,
  ico_up,
  ico_down,
  img_manshouxing,
  img_shouxinger,
  img_shouxingniu,
  img_zimukou,
  img_juanzhou,
  img_texture,
  ico_change_active,
  ico_change_normal,
  line_boat,
  line_friends,
  line_mount,
  imgtext
} from "./assets/images/loadImg.js";
import Toast from "./components/Toast/index";


function App() {
  const PING_GAI = "low_low_Group9890group1_group1"
  const PING_SHEN = "low_low_Group14579group1_group1"


  const MOVESPEED = 50
  const MOVETHRESHOLD = 30

  const MOVEPINGGAI = 8


  const INITCAMREA = {
    x: 0,
    y: 0,
    z: 1.2
  }
  let OpeningPingGai = false



  let container = null, containerW = 0, containerH = 0,
    emissiveTexture = useRef<THREE.Texture>(),
    scene: THREE.Scene,
    startMouse = useRef<THREE.Vector2>(new THREE.Vector2(0, 0)),
    showWenli = useRef<string>(''),
    controls = useRef<OrbitControls>(),
    showInfoRef = useRef<Boolean>(false),
    isOpenPingGai = useRef<Boolean>(false),

    camera = useRef<THREE.Camera | THREE.PerspectiveCamera>(),
    rafId: number,
    textrueLoader = useRef<THREE.TextureLoader>(new THREE.TextureLoader()),
    obj = useRef<THREE.Object3D<THREE.Event> | THREE.Group>(),
    composer: EffectComposer,
    outlinePass = null,
    renderPass = null,
    renderer: THREE.WebGLRenderer;

  const webglRef = useRef<HTMLDivElement>(null);


  const info = [
    {
      key: '年代：',
      name: '清'
    },
    {
      key: '级别：',
      name: '国家三级文物'
    },
    {
      key: '尺寸：',
      name: '通长37厘米、通宽37厘米 <br/> 通高64厘米，重13.565千克'
    },
    {
      key: '简介：',
      name: '通体为圆形，上盖下底，子母口，鼓腹，上腹圆阔，中腹内敛，下腹向外微撇，圈脚底，盖底和底足处露胎。短颈大口，腹上为圆肩，肩周平均分布个兽形耳。盖有板缘，中央突出为馒首形，上有一兽形钮，内有子圈，盖进口内。釉面滋润晶莹、透彻素雅，青花发色青翠漂亮，绘有山水花卉、树木房屋、人物鸟兽等图案,画工精细、娴熟，是难得的文物珍品，为国家三级文物。'
    }
  ]

  const cpArr = [
    {
      id: 1,
      name: '馒首形',
      img: img_manshouxing,
      wenli: textrueLoader.current!.load(`model/1-manshouxing.jpg`)
    },
    {
      id: 2,
      name: '兽形耳',
      img: img_shouxinger,
      wenli: textrueLoader.current!.load(`model/2-shouxinger.jpg`)
    },
    {
      id: 3,
      name: '子母口',
      img: img_zimukou,
      wenli: textrueLoader.current!.load(`model/3-zimukou.jpg`)
    },
    {
      id: 4,
      name: '兽形钮',
      img: img_shouxingniu,
      wenli: textrueLoader.current!.load(`model/4-shouxingniu.jpg`)
    }
  ]



  let [btnactive, setBtnactive] = useState({} as { name: string, id: number, wenli: string })
  let [changeLeft, setchangeLeft] = useState(0)
  let [loaded, setloaded] = useState(0)

  let [showInfo, setShowInfo] = useState(false)

  let [currentMsg, setcurrentMsg] = useState('')
  let [isReset, setIsReset] = useState(false)


  let fixinfo = useMemo(() => {
    showInfoRef.current = showInfo
    return showInfo ? info : [info[0], info[1]]
  }, [showInfo])



  const initRenderer = () => {
    //画布
    container = webglRef.current
    containerW = (webglRef.current as HTMLElement).getBoundingClientRect().width
    containerH = (webglRef.current as HTMLElement).getBoundingClientRect().height
    //渲染器
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(containerW, containerH);
    renderer.setClearAlpha(0);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.outputEncoding = THREE.sRGBEncoding;
    container?.appendChild(renderer.domElement);
  }

  const initSence = () => {
    const pmremGenerator = new THREE.PMREMGenerator(renderer);
    scene = new THREE.Scene();
    scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.4).texture;
  }


  const initCamera = () => {
    // 相机
    camera.current = new THREE.PerspectiveCamera(45, containerW / containerH, 0.1, 1000);
    camera.current.position.set(INITCAMREA.x, INITCAMREA.y, INITCAMREA.z);
    camera.current.lookAt(scene!.position);
  }

  const resetCamera = () => {
    controls.current!.enableZoom = false
    setIsReset(false)

    if (isOpenPingGai.current) {
      movePinggai(-MOVEPINGGAI)
    }

    let tween = new Tween({
      x1: camera.current!.position.x,
      y1: camera.current!.position.y,
      z1: camera.current!.position.z,
      x2: controls.current!.target.x,
      y2: controls.current!.target.y,
      z2: controls.current!.target.z
    })
    tween.to({
      x1: INITCAMREA.x,
      y1: INITCAMREA.y,
      z1: INITCAMREA.z,
      x2: 0,
      y2: 0,
      z2: 0
    }, 1e3).onUpdate(((e: any) => {
      camera.current!.position.x = e.x1
      camera.current!.position.y = e.y1
      camera.current!.position.z = e.z1
      controls.current!.target.x = e.x2
      controls.current!.target.y = e.y2
      controls.current!.target.z = e.z2
      controls.current!.update()
    }
    )).easing(Easing.Linear.None).onComplete(() => {
      controls.current!.enableZoom = true
      let t1 = setTimeout(() => {
        clearTimeout(t1)
        setIsReset(true)
      }, 500);
    }).start()
  }

  const initControl = () => {
    // 控制器
    controls.current = new OrbitControls(camera.current!, renderer.domElement);
    controls.current.enableDamping = true;
    controls.current.dampingFactor = 0.1;

    console.log('result:', controls);
    controls.current.minDistance = 0.8;
    controls.current.maxDistance = 2;

    // controls.maxPolarAngle = Math.PI;
    // controls.minPolarAngle = 0;
    controls.current.enablePan = false;
    controls.current.enableZoom = true;
  }

  // const initshadow = ()=> {

  // }

  const loadGlb = () => {
    const loader = new GLTFLoader();
    loader.load(
      `https://culture.4dage.com/demo/yantingxian/model/wl48-he.glb`,
      (gltf) => {
        obj.current = gltf.scene;
        obj.current.traverse((child) => {
          if (child instanceof THREE.Mesh) {
            if (child.name === PING_GAI) {
              outlineObj(child)
            }
          }
        })
        obj.current.scale.copy(new THREE.Vector3(0, 0, 0))
        onLoadFinish()
        scene?.add(obj.current);
        //加载完成
        animate();
      },
      function (progress) {
        if (progress.lengthComputable) {
          var hadload = progress.loaded / progress.total * 98;
          setloaded(Math.round(hadload))
        }
      },
      function (error) {
        console.error(error);
      }
    );
  }

  const loadObj = () => {
    new MTLLoader().setPath("model/").load("ytqinghuaci.mtl", (materials) => {
      materials.preload();

      new OBJLoader()
        .setMaterials(materials)
        .setPath("model/")
        .load(
          "YTqinghuaci.obj",
          (object) => {
            obj.current = object
            object.traverse((child) => {
              if (child instanceof THREE.Mesh) {
                if (child.isMesh) {
                  if (child instanceof THREE.Mesh) {
                    console.log('result:', child.name);
                    if (child.name === PING_GAI) {
                      outlineObj(child)
                    }
                  }
                  emissiveTexture.current = textrueLoader.current.load("model/shanchuanren.jpg");
                  let step = 1

                  setInterval(() => {
                    if (showWenli.current && showWenli.current === child.name) {
                      if (child.material.emissiveIntensity > 1.2) {
                        step = -1
                      }
                      if (child.material.emissiveIntensity < 0) {
                        step = 1
                      }
                      child.material.emissiveIntensity += 0.03 * step;
                    }
                    else {
                      child.material.emissiveMap = emissiveTexture.current;
                      child.material.emissiveIntensity = 0;
                    }
                  }, 50);

                  child.material.emissive = new THREE.Color(0xffffff);
                  child.material.dispose();
                }
              }
            });

            let s = 0

            obj.current.scale.set(s, s, s)
            scene?.add(obj.current);
            onLoadFinish()
            //加载完成
            animate();
          },
          (progress) => {
            if (progress.lengthComputable) {
              var hadload = progress.loaded / progress.total * 98;
              setloaded(Math.round(hadload))
            }
          },
          function (error) {
            console.error(error);
          }
        );
    });

  }

  const addHelper = () => {
    const box = new THREE.BoxHelper(obj.current!, 0xffff00);
    scene.add(box);
    const gridHelper = new THREE.GridHelper(100, 100, 0xff0000);
    scene.add(gridHelper);
    const axesHelper = new THREE.AxesHelper(100);
    scene.add(axesHelper);
  }

  const addLight = () => {
    let INSTANSITY = 0.6
    let POSIH = 0.30
    let POSIV = 0.1

    let preFix = 1.2
    var light = new THREE.AmbientLight(0xffffff, 1.4);
    scene.add(light)


    let array = [
      [0, -POSIV, POSIH],
      [0, -POSIV, -POSIH],
      [POSIH, -POSIV, 0],
      [-POSIH, -POSIV, 0],

      [0, POSIV, POSIH * preFix],
      [0, POSIV, -POSIH * preFix],
      [POSIH * preFix, POSIV, 0],
      [-POSIH * preFix, POSIV, 0],

      // [-0.341,0.2594,0.342],
      // [-0.486,-0.2250,-0.347],

      
 
      // [0.641,0.1994,-0.442],
      // [-0.486,-0.2250,0.347],
      // [-0.2,-0.3,0.4],

    ]

    array.forEach(item => {
      let temp = new THREE.SpotLight(0xffffff, INSTANSITY)
      temp.position.set(item[0], item[1], item[2])
      // let spotLightHelper = new THREE.SpotLightHelper(temp);
      // scene.add(spotLightHelper);
      scene.add(temp)
    });

  }

  const onLoadFinish = () => {
    addLight()
    controls.current!.enableZoom = false
    var a = {
      x: 0
    }
    var n = {
      x: 0.015
    }

    var t = setTimeout(() => {
      setloaded(100)
      clearTimeout(t)
      new Tween(a).to(n, 1e3).easing(Easing.Quadratic.Out).onUpdate((function () {
        obj.current?.scale.copy(new THREE.Vector3(a.x, a.x, a.x))
      }
      )).onComplete((function () {
        let t1 = setTimeout(() => {
          clearTimeout(t1)
          controls.current!.enableZoom = true
          controls.current!.autoRotate = true;
          controls.current!.autoRotateSpeed = 1;
          setIsReset(true)
        }, 1e3);
      }
      )).start()
    }, 1e3);

  }

  const animate = () => {
    rafId = requestAnimationFrame(animate);
    controls.current?.update();
    renderer.render(scene!, camera.current!);

    update();
    if (composer) {
      composer.render()
    }
  }

  // 高亮显示模型（呼吸灯）
  let outlineObj = (selectedObjects: any) => {
    if (!Array.isArray(selectedObjects)) {
      selectedObjects = [selectedObjects]
    }
    // 创建一个EffectComposer（效果组合器）对象，然后在该对象上添加后期处理通道。
    composer = new EffectComposer(renderer)
    // 新建一个场景通道  为了覆盖到原理来的场景上
    renderPass = new RenderPass(scene!, camera.current!)
    composer.addPass(renderPass);
    // 物体边缘发光通道
    outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene!, camera.current!, selectedObjects)
    outlinePass.selectedObjects = selectedObjects
    outlinePass.edgeStrength = 8 // 边框的亮度
    outlinePass.edgeGlow = 1.2// 光晕[0,1]
    outlinePass.usePatternTexture = false // 是否使用父级的材质
    // outlinePass.edgeThickness = 4.0 // 边框宽度
    // outlinePass.downSampleRatio = 1 // 边框弯曲度
    outlinePass.pulsePeriod = 5 // 呼吸闪烁的速度
    outlinePass.visibleEdgeColor.set(0xffffff) // 呼吸显示的颜色
    outlinePass.hiddenEdgeColor.set(0xffffff) // 呼吸消失的颜色
    outlinePass.clear = true
    composer.addPass(outlinePass)
    // 自定义的着色器通道 作为参数
    var effectFXAA = new ShaderPass(FXAAShader)
    effectFXAA.uniforms.resolution.value.set(1 / window.innerWidth, 1 / window.innerHeight)
    effectFXAA.renderToScreen = true
    composer.addPass(effectFXAA)
  }

  const movePinggai = (position: number, speed: number = 5e2) => {
    let pinggai_obj = obj.current!.getObjectByName(PING_GAI)

    let complete = () => {
      isOpenPingGai.current = !isOpenPingGai.current
      OpeningPingGai = false
    }
    new Tween(pinggai_obj!.position).to({
      x: pinggai_obj!.position.x,
      y: pinggai_obj!.position.y + position,
      z: pinggai_obj!.position.z
    }, speed).onUpdate((() => { OpeningPingGai = true }
    )).easing(Easing.Quadratic.Out).onComplete((complete)).start()
  }

  const pingGaiUpOrDown = () => {
    if (OpeningPingGai) return
    console.log('result:', isOpenPingGai.current);
    isOpenPingGai.current ? movePinggai(-MOVEPINGGAI) : movePinggai(MOVEPINGGAI)
  }

  const onMouseClickOrTouchEnd = (event: TouchEvent | MouseEvent) => {
    // 获取 raycaster 和所有模型相交的数组，其中的元素按照距离排序，越近的越靠前
    var intersects = getIntersects(event);
    if (intersects) {
      // 获取选中最近的 Mesh 对象
      if (intersects!.length !== 0 && intersects![0].object instanceof THREE.Mesh) {
        let selectObject = intersects![0].object;
        if (selectObject.name === PING_GAI) {
          pingGaiUpOrDown();
        }
      }
    }
  }


  const onMouseClickOrTouchStart = (event: TouchEvent | MouseEvent) => {
    if (event instanceof MouseEvent) {
      startMouse.current.x = event.clientX;
      startMouse.current.y = event.clientY;
    }
    else if (event instanceof TouchEvent) {

      if (0 === event.touches.length) return
      startMouse.current.x = event.touches[0].clientX;
      startMouse.current.y = event.touches[0].clientY;
    }
  }

  const onMouseOrTouchMove = (event: TouchEvent | MouseEvent) => {
    let clientX: number = 0, clientY: number = 0;
    if (event instanceof MouseEvent) {
      clientX = event.clientX;
      clientY = event.clientY;
    }
    else if (event instanceof TouchEvent) {
      if (0 === event.touches.length) return
      clientX = event.changedTouches[0].clientX;
      clientY = event.changedTouches[0].clientY;
    }
    if (showInfoRef.current) {
      if (clientX - startMouse.current.x >= MOVETHRESHOLD) {
        rightMove()
      } else if (clientX - startMouse.current.x < -MOVETHRESHOLD) {
        leftMove()
      }
    }

  }

  const leftMove = () => {
    setchangeLeft((prev) => {
      return prev + MOVESPEED
    })
  }

  const rightMove = () => {
    setchangeLeft((prev) => {
      return prev - MOVESPEED
    })
  }

  const getIntersects = (event: TouchEvent | MouseEvent) => {
    event.preventDefault();
    // 声明 raycaster 和 mouse 变量
    var raycaster = new THREE.Raycaster();
    var mouse = new THREE.Vector2();

    let clientX: number = 0, clientY: number = 0;
    if (event instanceof MouseEvent) {
      clientX = event.clientX;
      clientY = event.clientY;
    }
    else if (event instanceof TouchEvent) {
      if (0 !== event.touches.length) return
      clientX = event.changedTouches[0].clientX;
      clientY = event.changedTouches[0].clientY;
    }

    // 通过鼠标点击位置,计算出 raycaster 所需点的位置,以屏幕为中心点,范围 -1 到 1
    mouse.x = (clientX / containerW) * 2 - 1;
    mouse.y = -(clientY / containerH) * 2 + 1;

    //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
    raycaster.setFromCamera(mouse, camera.current!);

    // 获取与射线相交的对象数组，其中的元素按照距离排序，越近的越靠前
    var intersects = raycaster.intersectObjects(scene!.children);

    //返回选中的对象
    return intersects;
  }

  const onresize = () => {

  }

  //初始化
  const init = () => {
    initRenderer()
    initSence()
    initCamera()
    initControl()
    // loadGlb();
    loadObj()
    window.addEventListener("resize", onresize, false);
    window.addEventListener("click", onMouseClickOrTouchEnd, false);
    window.addEventListener("touchend", onMouseClickOrTouchEnd);
    window.addEventListener("touchstart", onMouseClickOrTouchStart, false);
    window.addEventListener("touchmove", onMouseOrTouchMove, false);
  }


  const toggleInfo = (event: any) => {
    event.preventDefault()
    setShowInfo(!showInfo)
  }
  const handleBtn = (event: any, item: any) => {
    console.log('result:', item);
    event.preventDefault()
    if (item.id !== btnactive.id) {
      setBtnactive(item)
    }

  }

  const handleBtnReset = (event: any) => {
    event.preventDefault()
    resetCamera()
  }


  useEffect(() => {
    init()

    return () => {
      window.removeEventListener("resize", onresize, false);
      window.removeEventListener("click", onMouseClickOrTouchEnd, false);
      window.removeEventListener("touchend", onMouseClickOrTouchEnd);
      window.removeEventListener("touchstart", onMouseClickOrTouchStart, false);
      window.removeEventListener("touchmove", onMouseOrTouchMove);
      cancelAnimationFrame(rafId!);
    }
  }, [])


  useEffect(() => {
    if (btnactive.wenli) {

      let pingshen = obj.current?.getObjectByName(PING_SHEN)
      let pinggai = obj.current?.getObjectByName(PING_GAI)

      let wlmesh: any = btnactive.id === 2 ? pingshen : pinggai
      wlmesh.material.emissiveMap = btnactive.wenli;
      showWenli.current = wlmesh.name
      wlmesh.material.dispose();
    }
  }, [btnactive])

  useEffect(() => {
    showInfoRef.current = !currentMsg
    if (currentMsg) {
      const timer = setTimeout(() => {
        clearTimeout(timer);
        setcurrentMsg('');
      }, 3000);
      return () => {
        clearTimeout(timer);
      };
    }
  }, [currentMsg]);




  return (
    <div className="App" style={{ background: `#3c454c url(${loading_bg}) no-repeat 50%/cover` }}>
      {loaded !== 100 && <div className='loadbox' style={{ background: `#3c454c url(${loading_bg}) no-repeat 50%/cover` }}>
        <div className="loadShow">
          <div className="title">
            <img src={label_title} alt='img' className='title' />
          </div>
          <div className="loading">
            <img src={img_loading} alt='img' className='loading' />
            <span>{loaded || 1}</span>
          </div>
          <div className="model">
            <img src={img_culture} alt='img' className='model' />
          </div>
        </div>
      </div>}

      <img src={logo} alt='img' className='logo' />

      <div className="mask" style={{ bottom: showInfo ? 0 : '-73%' }}>
        <div className="info">
          <h3>带盖青花瓷坛</h3>
          <ul>
            {
              fixinfo.map(item => {
                return (
                  <li key={item.key}>
                    <span>{item.key}</span>
                    <span dangerouslySetInnerHTML={{
                      __html: item.name
                    }}></span>
                  </li>
                )
              })
            }
          </ul>
        </div>

        <Huajuan
          handleClick={(item: any) => { setcurrentMsg(item.msg) }}
          onChange={(temp: any) => { setchangeLeft(temp) }}
          imgs={{
            img_juanzhou, img_texture, line_boat,
            line_friends,
            line_mount
          }}
          left={changeLeft}
        />

        <div onTouchEnd={toggleInfo} className="shoula">
          <img src={showInfo ? ico_down : ico_up} alt='img' className='logo' />
        </div>
      </div>

      <div className='btnlist'>
        <ul>
          {cpArr.map(item => {
            return (
              <li className={`${btnactive.id === item.id && 'active'}`} key={item.id} onTouchEnd={(event) => { handleBtn(event, item) }}>
                <div className='img'>
                  <img src={item.img} alt="cpArr" />
                  <div className='imgmask'></div>
                </div>
                <span>{item.name}</span>
              </li>
            )
          })}
        </ul>
      </div>

      <div className="reset">
        <img onTouchEnd={(event) => { handleBtnReset(event) }} src={isReset ? ico_change_active : ico_change_normal} alt='reset' className='reset' />
      </div>

      {
        currentMsg && <Toast bg={imgtext} message={currentMsg}></Toast>
      }

      <div id="webgl" ref={webglRef}></div>
    </div>
  );
}

export default App;
