import { ref, onMounted, openBlock, createElementBlock } from 'vue';
import * as THREE from 'three';
import { Group, Vector3, Shape, ShapeBufferGeometry } from 'three';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';

class MockupModel extends Group {
  constructor(home) {
    super();

    this.animation = this.floatAnim;
    this.goingHome = false;
    this.homeTimeout = null;

    this.home = home;
    this.reset();
  }

  reset() {
    this.position.x = this.home.position.x;
    this.position.y = this.home.position.y;
    this.position.z = this.home.position.z;

    this.rotation.x = this.home.rotation.x;
    this.rotation.y = this.home.rotation.y;
    this.rotation.z = this.home.rotation.z;

    this.speed = {
      x: 0,
      y: 0,
      z: 0,
    };

    this.rotSpeed = {
      x: 0,
      y: 0,
      z: 0,
    };

    this.acceleration = {
      x: 0,
      y: 0,
      z: 0,
    };
  }

  homeAnim(dt) {
    if (!this.goingHome) {
      this.goingHome = true;

      const rT = 1;
      this.speed.x = (this.home.position.x - this.position.x) / rT;
      this.speed.y = (this.home.position.y - this.position.y) / rT;
      this.speed.z = (this.home.position.z - this.position.z) / rT;

      this.rotSpeed.x = (this.home.rotation.x - this.rotation.x) / rT;
      this.rotSpeed.y = (this.home.rotation.y - this.rotation.y) / rT;
      this.rotSpeed.z = (this.home.rotation.z - this.rotation.z) / rT;

      this.homeTimeout = setTimeout(() => {
        this.goingHome = false;
        this.reset();
        this.startFloat();
      }, rT * 1000);
    }

    this.position.x += this.speed.x * dt;
    this.position.y += this.speed.y * dt;
    this.position.z += this.speed.z * dt;

    this.rotation.x += this.rotSpeed.x * dt;
    this.rotation.y += this.rotSpeed.y * dt;
    this.rotation.z += this.rotSpeed.z * dt;
  }

  startFloat() {
    this.acceleration.y = -0.01;
    this.animation = this.floatAnim;
  }

  floatAnim() {
    const maxSpeed = 0.1;
    const acceleration = 0.01;

    if (this.position.y < -2) {
      this.acceleration.y = acceleration;
    }

    if (this.position.y > 2) {
      this.acceleration.y = -acceleration;
    }

    this.speed.y = Math.min(this.speed.y + this.acceleration.y, maxSpeed);
    this.position.y += this.speed.y;
  }

  rotateAnim() {
    this.rotation.y += 0.02;
  }

  lookAtAnim(dt, { x, y, z }) {
    const target = new Vector3();
    target.x = x;
    target.y = y;
    target.z = z;
    this.lookAt(target);
  }
}

function roundedPlane(width, height, radius) {
  const x = 1; const y = 1;
  const shape = new Shape();

  shape.moveTo(x, y + radius);
  shape.lineTo(x, y + height - radius);
  shape.quadraticCurveTo(x, y + height, x + radius, y + height);
  shape.lineTo(x + width - radius, y + height);
  shape.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
  shape.lineTo(x + width, y + radius);
  shape.quadraticCurveTo(x + width, y, x + width - radius, y);
  shape.lineTo(x + radius, y);
  shape.quadraticCurveTo(x, y, x, y + radius);

  return new ShapeBufferGeometry(shape);
}

var phoneObj = "data:model/obj;base64,[encoded data, 3450812 characters]";

var script = {
  name: 'Mockup',
  props: {
    screen: {
      type: null,
    },
    lightClr: {
      type: String,
      default: 'white',
    },
    phoneClr: {
      type: String,
      default: 'white',
    },
    position: {
      type: Object,
      default: () => ({}),
    },
    rotation: {
      type: Object,
      default: () => ({}),
    },
  },
  setup(props) {
    const container = ref(null);
    let camera;
    let scene;
    let phone;
    let renderer;
    let mouseX = 0;
    let mouseY = 0;

    function init() {
      const environmentInit = () => {
        camera = new THREE.PerspectiveCamera(
          45, container.value.clientWidth / container.value.clientHeight, 0.1, 10000,
        );
        scene = new THREE.Scene();

        const light = new THREE.DirectionalLight(props.lightClr);
        scene.add(light);

        light.position.set(0, 0, 300);
        camera.position.set(0, 0, 200);
      };

      const phoneInit = (screenSrc, home) => {
        const screenInit = () => {
          const scale = 6;
          const width = scale * 9; const height = scale * 19.3;
          const radius = 8;

          // const geometry = new THREE.PlaneGeometry(width, height);
          const geometry = roundedPlane(width, height, radius);

          let texture;

          if (typeof screenSrc === 'string') {
            const loader = new THREE.TextureLoader();
            texture = loader.load(screenSrc);
          } else {
            texture = new THREE.VideoTexture(screenSrc);
          }

          texture.anisotropy = renderer.capabilities.getMaxAnisotropy();

          const material = new THREE.MeshLambertMaterial({ map: texture });
          // material.map.minFilter = THREE.LinearFilter;
          const screen = new THREE.Mesh(geometry, material);

          const recomputeUVs = () => {
            const box = new THREE.Box3().setFromObject(screen);
            const size = new THREE.Vector3();
            box.getSize(size);
            const vec3 = new THREE.Vector3();
            const attPos = screen.geometry.attributes.position;
            const attUv = screen.geometry.attributes.uv;
            for (let i = 0; i < attPos.count; i += 1) {
              vec3.fromBufferAttribute(attPos, i);
              attUv.setXY(i,
                (vec3.x - box.min.x) / size.x,
                (vec3.y - box.min.y) / size.y);
            }
            // attUv.needsUpdate = true;
          };

          recomputeUVs();

          screen.translateX(-width / 2);
          screen.translateY(-height / 2);
          screen.translateZ(6);
          phone.add(screen);
        };

        const bodyInit = () => {
          const loader = new OBJLoader();
          loader.load(
            phoneObj,
            (body) => {
              body.traverse((child) => {
                if (child instanceof THREE.Mesh) {
                  child.material = new THREE.MeshLambertMaterial({ color: props.phoneClr });
                }
              });

              body.position.y = -60;
              phone.add(body);
            },
          );
        };

        phone = new MockupModel({
          position: {
            x: 0,
            y: 0,
            z: 0,
            ...home.position,
          },
          rotation: {
            x: -0.2,
            y: 0.3,
            z: 0.06,
            ...home.rotation,
          },
        });
        phone.startFloat();
        scene.add(phone);
        screenInit();
        bodyInit();
      };

      renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
      renderer.setSize(container.value.clientWidth, container.value.clientHeight);

      environmentInit();
      phoneInit(props.screen, { position: props.position, rotation: props.rotation });

      container.value.appendChild(renderer.domElement);
    }

    let previousTime = 0;
    function animate(currentTime) {
      currentTime *= 0.001;
      const deltaTime = currentTime - previousTime;
      previousTime = currentTime;

      requestAnimationFrame(animate);

      if (phone) {
        phone.animation(deltaTime, { x: mouseX, y: mouseY, z: camera.position.z });
      }

      renderer.render(scene, camera);
    }

    function handleMouseEnter() {
      if (phone) {
        phone.animation = phone.lookAtAnim;
        phone.goingHome = false;
        clearTimeout(phone.homeTimeout);
      }
    }

    function handleMouseLeave() {
      if (phone) { phone.animation = phone.homeAnim; }
    }

    function handleMouseMove(event) {
      const rect = container.value.getBoundingClientRect();
      mouseX = event.clientX - rect.left - rect.width / 2;
      mouseY = -(event.clientY - rect.top - rect.height / 2);
    }

    onMounted(() => {
      init();
      animate(0);
    });

    return {
      container,
      handleMouseEnter,
      handleMouseLeave,
      handleMouseMove,
    };
  },
};

function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (openBlock(), createElementBlock("div", {
    ref: "container",
    onMouseenter: _cache[0] || (_cache[0] = (...args) => ($setup.handleMouseEnter && $setup.handleMouseEnter(...args))),
    onMouseleave: _cache[1] || (_cache[1] = (...args) => ($setup.handleMouseLeave && $setup.handleMouseLeave(...args))),
    onMousemove: _cache[2] || (_cache[2] = (...args) => ($setup.handleMouseMove && $setup.handleMouseMove(...args)))
  }, null, 544 /* HYDRATE_EVENTS, NEED_PATCH */))
}

script.render = render;
script.__file = "src/Mockup.vue";

export { script as default };
