
let texLoader = new THREE.TextureLoader;



let camera, scene, renderer, stats, gui;
const mouse = new THREE.Vector2();
const raycaster = new THREE.Raycaster(); raycaster.linePrecision = 0;//不检测boxHelper
const Transitions = {
    doubleClick: 0,
    helperOpa: 1,
}
let labelIndex = 0




var Viewer = function (index, dom) {
    THREE.EventDispatcher.call(this)
    this.index = index;
    this.dom = dom
    this.camera = new THREE.PerspectiveCamera();
    this.camera.position.set(0, 0, 0.78);
    this.control = new THREE.OrbitControls(this.camera, this.dom)
    this.control.enableDamping = true;
    this.control.dampingFactor = 0.4;
    this.control.minDistance = 0.3;
    this.control.maxDistance = 2;

    this.control.enablePan = true;
    this.control.enableZoom = true;

    this.setRenderer()
    this.scene = new THREE.Scene;

    this.pointerDownPos
    this.textures = [];
    this.labels = []
    this.active = false;
    this.antialias = true;
    this.clickTime = new Date().getTime();

    this.updateClock = new THREE.Clock;
    this.init()
}

Viewer.prototype = Object.create(THREE.EventDispatcher.prototype)
Viewer.constructor = Viewer


Viewer.prototype.bindEvents = function () {
    this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this), false);
    this.renderer.domElement.addEventListener('pointerup', this.onPointerUp.bind(this), false);
}

Viewer.prototype.setRenderer = function () {
    try {

        this.renderer = new THREE.WebGLRenderer(
            {
                canvas: $(this.dom).find("canvas")[0],
                antialias: true,
                alpha: true
            }
        ),//许钟文 添加个抗锯齿，否则添加的线条锯齿严重,

            this.renderer.setClearAlpha(0);
        //this.renderer.autoClear = !0,
        this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1)
        // this.renderer.autoClear = false
        //this.emit(Events.ContextCreated) 


    } catch (e) {
        console.error("Unable to create a WebGL rendering context")
    }
}

Viewer.prototype.update = function (deltaTime) {//绘制的时候同时更新 
    //if(!this.active)return;
    this.setSize()
    this.control.update(deltaTime)
    transitions.update(deltaTime)
    var needsUpdate = 1;

    if (needsUpdate) {
        //this.renderer.autoClear = true
        this.renderer.render(this.scene, this.camera)

    }
}



Viewer.prototype.hasChanged = function () {//判断画面是否改变了，改变后需要更新一些东西
    var copy = function () {
        this.previousState = {
            projectionMatrix: this.camera.projectionMatrix.clone(),//worldMatrix在control时归零了所以不用了吧，用position和qua也一样
            position: this.camera.position.clone(),
            quaternion: this.camera.quaternion.clone(),
            //mouse: this.mouse.clone(), 
            fov: this.camera.fov
        };
    }.bind(this)


    if (!this.previousState) {
        copy()
        return { cameraChanged: !0, changeSlightly: !1 };
    }
    var cameraChanged =
        !this.camera.projectionMatrix.equals(this.previousState.projectionMatrix) ||
        !this.camera.position.equals(this.previousState.position) ||
        !this.camera.quaternion.equals(this.previousState.quaternion)


    //var changed = cameraChanged //|| !this.mouse.equals(this.previousState.mouse)  
    let changeSlightly
    if(cameraChanged){
        changeSlightly = math.closeTo(this.camera.position,this.previousState.position, 1e-3) &&
            math.closeTo(this.camera.quaternion,this.previousState.quaternion, 1e-4) 
    }
    copy()

    return { cameraChanged, changeSlightly };
}

Viewer.prototype.setSize = function () {
    var w, h, pixelRatio;
    return function () {
        if (w != this.dom.clientWidth || h != this.dom.clientHeight || pixelRatio != window.devicePixelRatio) {
            w = this.dom.clientWidth;
            h = this.dom.clientHeight;

            pixelRatio = window.devicePixelRatio;

            this.camera.aspect = w / h;
            this.camera.updateProjectionMatrix();
            this.renderer.setSize(w, h, false, pixelRatio);
        }
    }
}()

Viewer.prototype.init = function () {
    this.meshGroup = new THREE.Object3D();
    this.scene.add(this.meshGroup);
    this.meshGroup.name = "viewerMeshGroup";

    var buildScene = () => {
        this.animate()
    }
    this.loadOBJ(() => {
        this.bindEvents()
        //this.loadLabels()
    })


    buildScene()
}



Viewer.prototype.animate = function () {
    var deltaTime = Math.min(1, this.updateClock.getDelta());
    this.update(deltaTime)
    //bus.emit('player/position/change', {x:this.position.x, y:this.position.z, lon: this.cameraControls.controls.panorama.lon})

    let changed = this.hasChanged()
    if (changed.cameraChanged) {
        this.dispatchEvent({ type: 'view.changed', changeSlightly:changed.changeSlightly })

        let label_ = this.labels.filter(e => e.elem[0].style.display == 'block')
        label_.sort((a, b) => b.pos2d.z - a.pos2d.z)
        label_.forEach((e, index) => e.elem.css('z-index', index + 1000));

    }

    window.requestAnimationFrame(this.animate.bind(this));
},




    Viewer.prototype.loadOBJ = function (done) {

        var startTime = new Date().getTime();
        window.objs = [];
        var group = new THREE.Object3D;
        this.meshGroup.add(group);

        function onProgress(xhr) {
            if (xhr.lengthComputable) {
                var percentComplete = xhr.loaded / xhr.total * 100;
                console.log('model ' + Math.round(percentComplete, 2) + '% downloaded');
            }
        }

        function onError() { }

        var MTLLoader = new THREE.MTLLoader();
        var OBJLoader = new THREE.OBJLoader()


        var loadModel = () => {
            var info = {//凳子
                path: 'model/',
                mtl: 'wl48-he.mtl',
                obj: 'wl48-he.obj',
                position: [0, 0],
                rotation: 0,
                height: 1
            };

            if (!info) {
                console.log("加载持续时间：" + (new Date().getTime() - startTime))
                return;
            }


            MTLLoader.setPath(info.path).load(info.mtl, (materials) => {

                materials.preload();

                OBJLoader.setMaterials(materials)
                    .setPath(info.path)
                    .load(info.obj, (object) => {

                        group.add(object);
                        object.traverse(function (child) {
                            if (child.isMesh) {
                                console.log(child);
                                // if (child.name == "WL48_ping") {
                                //     let textrueLoader = new THREE.TextureLoader();
                                //     var emissiveTexture = textrueLoader.load("model/shadow.jpg");
                                //     // emissiveTexture.encoding = THREE.LinearEncoding;
                                //     child.material.emissiveMap = emissiveTexture;
                                //     child.material.emissiveIntensity = 0;
                                //     // let step = 1
                                //     // setInterval(() => {

                                //     //     if (child.material.emissiveIntensity > 0.3) {
                                //     //         step = -1

                                //     //     }

                                //     //     if (child.material.emissiveIntensity < 0) {
                                //     //         step = 1
                                //     //     }

                                //     //     child.material.emissiveIntensity += 0.01 * step;

                                //     // }, 50);
                                //     child.material.emissive = new THREE.Color(0xffffff);
                                // }
                                /* if(child.geometry){
                                    child.geometry.computeBoundingBox();
                                    bound.union(child.geometry.boundingBox) 
                                } */
                            }
                        });


                        this.model = object
                        let s = 0.010
                        object.scale.set(s, s, s)

                        done && done()
                        console.log("加载持续时间：" + (new Date().getTime() - startTime))
                        setTimeout(() => {
                            this.dispatchEvent({ type: 'hadLoaded' })
                        });
                    }, onProgress, onError);
            });
        }

        loadModel()

        var light1 = new THREE.AmbientLight(16777215);
        light1.intensity = 2.8;
        this.scene.add(light1)

        var light2 = new THREE.SpotLight(0xffffff, 1);
        light2.position.set(0, 0, 3)
        light2.intensity = 0.2;

        var light3 = new THREE.SpotLight(0xffffff, 1);
        light3.position.set(0, 0, -3)
        light3.intensity = 0.4;


        // let spotLightHelper = new THREE.SpotLightHelper(light2);
        // let spotLightHelper3 = new THREE.SpotLightHelper(light3);
        this.scene.add(light2)
        this.scene.add(light3)
        // this.scene.add( spotLightHelper );
        // this.scene.add( spotLightHelper3 );

    }




Viewer.prototype.onPointerMove = function (event) {

    if (event.isPrimary === false) return;

    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;

    if (!this.pointerDownPos) this.checkIntersection();

}
Viewer.prototype.onPointerDown = function (event) {
    if (event.isPrimary === false) return;
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
    this.pointerDownPos = mouse.clone()
}

Viewer.prototype.onPointerUp = function (event) {

    if (event.isPrimary === false) return;
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
    if (mouse.distanceTo(this.pointerDownPos) < 0.006) {//click
        this.checkIntersection()

        // if (this.intersects.length) {
        //     console.log(this.intersects[0].point);
        //     this.addLabel({ position: this.intersects[0].point })
        // }

        var time = new Date().getTime();
        if (time - this.clickTime < 300) {
            if (this.intersects.length) {
                console.log('doubleClick');
                transitions.cancelById(0)
                transitions.start(lerp.vector(this.control.target, this.intersects[0].point), 600, null, 0/* Delay */, easing.easeInOutQuad, null, Transitions.doubleClick);
            }
        }
        this.clickTime = time;
    }
    this.pointerDownPos = null
}



Viewer.prototype.checkIntersection = function () {

    raycaster.setFromCamera(mouse, this.camera);

    const intersects = raycaster.intersectObject(this.model/* this.meshGroup */, true);
    this.intersects = intersects;
}

/* Viewer.prototype.adjustModelPos = function(){ //固定地板高度的情况下，调整模型的position.y
     
    this.meshGroup.updateMatrixWorld();
    this.model.updateMatrixWorld();
    var bound = this.model.bound.clone().applyMatrix4(this.model.matrixWorld)
    var center = bound.getCenter() 
    
    
    //为了让最低点在地面上：
    this.meshGroup.position.y +=  floorY - bound.min.y
    //居中：
    this.meshGroup.position.x +=  0 - center.x
    this.meshGroup.position.z +=  0 - center.z
   
     
}  */


Viewer.prototype.removeAllLabels = function (data) {
    this.labels.forEach(label=>{
        label.dispose()
        label = null
    })
    this.labels = []
}

Viewer.prototype.loadLabelsFromData = function (data) {
 
    data.forEach(info => {
        info.position = new THREE.Vector3().fromArray(info.posInModel).applyMatrix4(this.model.matrixWorld)
        this.addLabel(info)

    })
}

Viewer.prototype.addLabel = function (o) {

    labelIndex++
    o.title = o.title || ('default' + labelIndex)
    o.shelterByModel = true
    let label = new Label2D(o)
    this.labels.push(label)

}

Viewer.prototype.removeLabel = function (label) {
    label.dispose()
    let index = this.labels.indexOf(label)
    index > -1 && this.labels.splice(index)
    label.li && label.li.remove()
}


Viewer.prototype.setAddLabelState = function (state) {
    this.addingLabel = !!state
    $('#addLabel').text(state ? '停止加标签' : '添加标签')
}



Viewer.prototype.exportLabelData = function () {
    let data = this.labels.map(label => {
        let inv = new THREE.Matrix4().getInverse(this.model.matrixWorld)
        let posInModel = label.position.clone().applyMatrix4(inv)

        let info = {
            title: label.title,
            posInModel: convertTool.toPrecision(posInModel.toArray(), 4),

        }
        return info
    })

    $('textarea').css('display', 'block').text(JSON.stringify(data))
    console.log(data)
    return data
}


Viewer.prototype.getCLabel = function () { //获取最接近中心的label

    let disSquairs = this.labels.filter(e => e.elem[0].style.display == 'block').map((label) => {
        return {
            label,
            disSquair: label.pos2d.x * label.pos2d.x + label.pos2d.y * label.pos2d.y
        }
    })
    disSquairs.sort((e1, e2) => { return e1.disSquair - e2.disSquair })
    return disSquairs[0] && disSquairs[0].label
}



//============

var startTime = new Date().getTime();

function dataURLtoBlob(dataurl) {//将base64转换blob
    var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], { type: mime });
}

/* 

    MeshStandardMaterial（pbr）代替Phong
    优点 ： 能量守恒、更容易调节出真实感。  metalnessMap只用到一个通道，颜色信息整合到albedo贴图里，省数据。
    缺点：可能，mtl里的值只能用到一部分， specular用不到。 （会降低对specular的控制）
        另外不知道大部分模型用的是哪种模式，是否使用metalnessMap。
    
    aoMap r
    roughnessMap alphaMap g
    metalnessMap b
    
    normalMap  ?
    bumpMap : 黑白？

主要是光滑度or粗糙度（可贴图）  还有 金属性（可贴图）  还有颜色（可贴图），透明度（in颜色贴图），法线贴图or凹凸贴图  
    
    
    
    贴图的属性 rotation  offset  repeat (当wrapS = THREE.RepeatWrapping，wrapT = THREE.RepeatWrapping)
    
    
 */







