<template>
  <div id="root">
    <header role="banner">
      <div class="wrapper">
        <h1
          class="site-title"
          tabindex="0"
        >
          Muntadas
        </h1>
      </div>
    </header>
    <section role="main-content">
      <div class="wrapper">
        <renderer :obj="myRenderer" :size="size">
          <scene :obj="myScene" @update:obj="handleScene">

            <!-- <SkyBox :size="{x:500,y:500,z:500}" :position="{x:0,y:0,z:0}" :color="0x992222" /> -->

            <!-- <orbit-controls ref="cam_controls" :position="init_cam_pos" :rotation="{ x: 0, y: 0, z: 0 }">
              <camera />
            </orbit-controls> -->
            <camera ref="camera" :position="init_cam_pos" :rotation="{x:0, y:0, z:0}" />

            <!-- <light :hex="0xffffff" :intensity="5" :position="{x:-100,y:150,z:50}" />
            <light :hex="0xffffff" :intensity="2" :position="{x:100,y:-150,z:-50}" :options="light_opts" /> -->

            <animation :fn="animate" :speed="3" />

            <template v-if="debug">
              <cube :size="{x:30,y:1,z:1}" :position="{x:15,y:0,z:0}" :color="0x992222" />
              <cube :size="{x:1,y:30,z:1}" :position="{x:0,y:15,z:0}" :color="0x00BBFF" />
              <cube :size="{x:1,y:1,z:30}" :position="{x:0,y:0,z:15}" :color="0x17d100" />
            </template>

            <ground :size="{w:5000,h:5000}" :position="{x:0,y:0,z:0}" :color="0x133f52" :rotation="{x:90,y:0,z:0}" />

            <template v-if="projects.length">
              <project
                v-for="(project, index) in projects"
                :key="project.id"
                :data="project"
                :len="projects.length"
                :index="index"
              />
            </template>

          </scene>
        </renderer>
        <Content v-if="content_data" :data="content_data" @onClose="onCloseContent" />
      </div>
    </section>
    <footer />
  </div>
</template>

<script>
import { mapState, mapActions } from 'vuex'
import mixins from 'components/mixins'
import * as THREE from 'three'
import TWEEN from '@tweenjs/tween.js'

import BgVertex from 'assets/glsl/BgVertex'
import SkyFragment from 'assets/glsl/SkyFragment'
import WaterFragment from 'assets/glsl/WaterFragment'

import Ground from './components/objects/Ground'
import Cube from './components/objects/Cube'
import Project from './components/objects/Project'
import Content from './components/Content'

// const TWEEN = require('@tweenjs/tween.js')
const _debug = false

export default {
  metaInfo: {
    // if no subcomponents specify a metaInfo.title, this title will be used
    title: 'Home',
    // all titles will be injected into this template
    titleTemplate: '%s | Muntadas'
  },
  components: {
    Cube,
    Project,
    Ground,
    Content
  },
  mixins: [mixins],
  data () {
    // const envcolor = 0xffffff
    let renderer = new THREE.WebGLRenderer({ alpha: false, antialias: true })
    renderer.setClearColor(0x000000, 0)
    renderer.shadowMap.enabled = true

    // scene obj is well overwrited but background color not visible
    let scene = new THREE.Scene()
    scene.background = new THREE.Color(0x1a1a1a)
    // scene.fog = new THREE.Fog(new THREE.Color(0xffffff), 50, 200)
    // console.log('myScene', scene)

    // SKYDOME
    // https://github.com/mrdoob/three.js/blob/master/examples/webgl_lights_hemisphere.html
    // https://gamedevelopment.tutsplus.com/tutorials/a-beginners-guide-to-coding-graphics-shaders--cms-23313
    var uniforms = {
      'topColor': { value: new THREE.Color(0x0077ff) },
      'horizontColor': { value: new THREE.Color(0xffffff) },
      'waterColor': { value: new THREE.Color(0x13719a) },
      'bottomColor': { value: new THREE.Color(0x000000) },
      'offset': { value: 33 },
      'wateroffset': { value: 33 },
      'exponent': { value: 0.6 },
      'waterexponent': { value: 0.6 }
    }
    // SphereBufferGeometry(radius : Float, widthSegments : Integer, heightSegments : Integer, phiStart : Float, phiLength : Float, thetaStart : Float, thetaLength : Float)
    var bgGeo = new THREE.SphereBufferGeometry(900, 32, 15, 0, 2 * Math.PI, 0, 0.5 * Math.PI)
    var skyMat = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: BgVertex,
      fragmentShader: SkyFragment,
      side: THREE.BackSide
    })
    var sky = new THREE.Mesh(bgGeo, skyMat)
    scene.add(sky)
    var waterMat = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: BgVertex,
      fragmentShader: WaterFragment,
      side: THREE.BackSide
    })
    var water = new THREE.Mesh(bgGeo, waterMat)
    water.rotateZ(this.deg2rad(180))
    scene.add(water)

    // SEA

    // lights
    var sun = new THREE.PointLight(0xfcfcf9, 2)
    sun.position.set(-150, 150, 110)
    sun.castShadows = true
    // sun.target.position.set(0, 0, 0)
    scene.add(sun)
    // scene.add(sun.target)

    var sun2 = new THREE.PointLight(0xfcfcf9, 0.6)
    sun2.position.set(150, 150, 110)
    sun2.castShadows = true
    // sun2.target.position.set(0, 0, 0)
    scene.add(sun2)
    // scene.add(sun2.target)

    var sun3 = new THREE.PointLight(0xfcfcf9, 0.6)
    sun3.position.set(0, 150, -110)
    sun3.castShadows = true
    // sun3.target.position.set(0, 0, 0)
    scene.add(sun3)
    // scene.add(sun2.target)

    var sun4 = new THREE.PointLight(0xfcfcf9, 0.6)
    sun4.position.set(0, -150, -110)
    sun4.castShadows = true
    // sun4.target.position.set(0, 0, 0)
    scene.add(sun4)
    // scene.add(sun2.target)

    // RETURN DATA
    return {
      debug: _debug,
      myRenderer: renderer,
      myScene: scene,
      // mouse_start: new THREE.Vector2(),
      // mouse: new THREE.Vector2(),
      camera: null,
      mouse: new THREE.Vector2(),
      controls: {
        user_interact: false,
        is_dragging: false,
        mouse: new THREE.Vector2(),
        mouse_start: new THREE.Vector2(),
        lon: -91,
        lat: 0,
        lon_start: 0,
        lat_start: 0,
        phi: 0,
        theta: 0,
        cam_target: new THREE.Vector3(0, 0, 0)
      },
      // cam_controls: null,
      init_cam_pos: { x: 0, y: 10, z: 100 },
      raycaster: new THREE.Raycaster(),
      interactive_objects_names: ['Project', 'Content'],
      interactive_objects: [],
      light_opts: {
        castShadow: true
      },
      opened_vnode: null,
      content_data: null
    }
  },
  computed: {
    ...mapState({
      projects: state => state.Projects.projects
    }),
    size () {
      return {
        w: window.innerWidth,
        h: window.innerHeight
      }
    }
  },
  created () {
    if (!this.projects.length) {
      this.getProjects()
    }
    this.$env3d.scene = this.myScene
    this.$env3d.renderer = this.myRenderer
  },
  mounted () {
    console.log('App mounted', this)
    console.log('App mounted $env3d', this.$env3d)

    this.camera = this.$env3d.camera = this.$refs.camera.curObj
    // this.cam_controls = this.$refs.cam_controls.controls
    // console.log('cam_controls', this.cam_controls)

    // this.cam_controls = new THREE.PointerLockControls(this.camera)
    // console.log('this.cam_controls', this.cam_controls)

    this.updatedInteractiveObjects()
    // CONTROLS
    // https://blogs.perficient.com/2020/05/21/3d-camera-movement-in-three-js-i-learned-the-hard-way-so-you-dont-have-to/
    document.addEventListener('mousedown', this.onDocMouseDown, false)
    document.addEventListener('mousemove', this.onDocMouseMove, false)
    document.addEventListener('mouseup', this.onDocMouseup, false)
  },
  updated () {
    this.updatedInteractiveObjects()
  },
  methods: {
    ...mapActions({
      getProjects: 'Projects/getProjects'
    }),
    handleScene (scene) {
      console.log('handlescene', scene)
    },
    updatedInteractiveObjects () {
      console.log('updatedInteractiveObjects', this.myScene.children)
      this.interactive_objects = []
      for (var i = 0; i < this.myScene.children.length; i++) {
        if (this.interactive_objects_names.indexOf(this.myScene.children[i].name) !== -1) {
          this.interactive_objects.push(this.myScene.children[i])
        }
      }
      // console.log('this.interactive_objects', this.interactive_objects)
    },
    onDocMouseDown (e) {
      // CONTROLS
      this.controls.mouse_start.x = e.clientX
      this.controls.mouse_start.y = e.clientY
      this.controls.lon_start = this.controls.lon
      this.controls.lat_start = this.controls.lat
      this.controls.user_interact = true
    },
    onDocMouseMove (e) {
      // RAY CASTING : update the mouse variable
      this.mouse.x = (e.clientX / window.innerWidth) * 2 - 1
      this.mouse.y = -(e.clientY / window.innerHeight) * 2 + 1

      // CONTROLS
      if (this.controls.user_interact) {
        this.controls.lon = (this.controls.mouse_start.x - e.clientX) * 0.1 + this.controls.lon_start
        this.controls.lat = (e.clientY - this.controls.mouse_start.y) * 0.1 + this.controls.lat_start
      }
    },
    onDocMouseup (e) {
      console.log('onDocumentMouseup', e)
      // CONTROLS
      this.controls.user_interact = false

      // check if event is not a classic html link
      if (e.target.className === 'close-btn') {
        // console.log('close-btn: vnode', this.opened_vnode)
        // this.opened_vnode.isOpened = false
        return
      }

      // INTERACTIONS
      this.updatedInteractiveObjects()

      // check if draging
      if (Math.hypot(e.clientX - this.controls.mouse_start.x, e.clientY - this.controls.mouse_start.y) < 5) {
        // update the picking ray with the camera and mouse position
        this.raycaster.setFromCamera(this.mouse, this.camera)
        // calculate objects intersecting the picking ray
        var intersects = this.raycaster.intersectObjects(this.interactive_objects)
        // console.log('intersects', intersects)

        let object, vnode, toPos
        let cam = this.camera
        let camPos = { ...this.camera.position }
        if (intersects.length) {
          // console.log('onDocMouseUp intersects', intersects)
          // for (var i = 0; i < intersects.length; i++) {
          object = intersects[0].object
          vnode = object.userData.vnode
          if (object.name === 'Content' && vnode.isOpened) {
            this.content_data = vnode.data
          }

          // close precedent vnode
          if (this.opened_vnode && this.opened_vnode.$options._componentTag === 'ContentBlock') {
            // console.log('opened_vnode', this.opened_vnode)
            this.opened_vnode.isOpened = false
          }
          // opene current vnode
          vnode.isOpened = true
          // recorde vnode as precedent vnode
          this.opened_vnode = vnode

          toPos = { ...object.position }
          if (object.name === 'Content') {
            // toPos.z += 1
            switch (vnode.face) {
              case 'left':
                toPos.x += 4
                break
              case 'back':
                toPos.z += 4
                break
              case 'right':
                toPos.x -= 4
                break
              case 'front':
                toPos.z -= 4
                break
            }
            // TODO: we need to update lon and lat accordingly when chaging camera orientation
            // new TWEEN.Tween(this.controls.cam_target)
            //   .to(object.position, 2000)
            //   .start()
          }
          // toPos.y = 5
          new TWEEN.Tween(camPos)
            .easing(TWEEN.Easing.Quadratic.InOut)
            .to(toPos, 3000)
            .onUpdate(function () {
              // console.log('tween update', this._object)
              cam.position.set(this._object.x, this._object.y, this._object.z)
            })
            .start()

          // console.log('tween', tween)
          //   break
          // }
        } else {
          // new TWEEN.Tween(this.camera.position)
          //   .to(this.init_cam_pos, 1000)
          //   .start()
        }
      } // end if dragging
    },
    animate (tt) {
      // CONTROLS
      this.controls.lat = Math.max(-85, Math.min(85, this.controls.lat))
      this.controls.phi = this.deg2rad(90 - this.controls.lat)
      this.controls.theta = this.deg2rad(this.controls.lon)
      // if (this.controls.user_interact === false) {
      //   this.controls.lon += 0.1
      // }
      // if (this.controls.user_interact) {
      this.controls.cam_target.x = 500 * Math.sin(this.controls.phi) * Math.cos(this.controls.theta)
      this.controls.cam_target.y = 500 * Math.cos(this.controls.phi)
      this.controls.cam_target.z = 500 * Math.sin(this.controls.phi) * Math.sin(this.controls.theta)
      // }

      if (this.camera) {
        this.camera.lookAt(this.controls.cam_target)
      }

      // TWEENS
      TWEEN.update()
    },
    onCloseContent () {
      this.content_data = null
    }
  }
}
</script>

<style lang="scss" scoped>

.container{
  // font-family:'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
  max-width: 1200px;

}

</style>
