"use strict";
/*
The MIT License (MIT)
Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)

Virtual World Framework Apache 2.0 license  (https://github.com/NikolaySuslov/livecodingspace/blob/master/licenses/LICENSE_VWF.md)
*/

// VWF & A-Frame view driver

define(["module", "vwf/view"], function (module, view) {
    var self;

    return view.load(module, {

        // == Module Definition ====================================================================

        initialize: function (options) {
            self = this;
            this.nodes = {};

            // this.tickTime = 0;
            // this.realTickDif = 50;
            // this.lastrealTickDif = 50;
            // this.lastRealTick = performance.now();

            this.state.appInitialized = false;

            if (options === undefined) { options = {}; }

            if (typeof options == "object") {

                this.rootSelector = options["application-root"];
            }
            else {
                this.rootSelector = options;
            }

            this.gearvr = options.gearvr !== undefined ? options.gearvr : false;
            this.wmrright = options.wmrright !== undefined ? options.wmrright : false;
            this.wmrleft = options.wmrleft !== undefined ? options.wmrleft : false;
        },

        createdNode: function (nodeID, childID, childExtendsID, childImplementsIDs,
            childSource, childType, childIndex, childName, callback /* ( ready ) */) {

            var node = this.state.nodes[childID];

            // If the "nodes" object does not have this object in it, it must not be one that
            // this driver cares about
            if (!node) {
                return;
            }

            if (this.state.scenes[childID]) {
                let scene = this.state.scenes[childID];
                createAvatarControl(scene);
                createAvatar.call(this, childID);

                // this.state.appInitialized  = true;

                if (this.gearvr == true) {
                    console.log("CREATE GEARVR HERE!!");
                    if (AFRAME.utils.device.isGearVR()) {
                        let nodeName = 'gearvr-' + self.kernel.moniker();
                        createGearVRControls();
                        createGearVRController.call(this, childID, nodeName);
                    }
                }

                if (this.wmrright == true) {
                    console.log("CREATE WMR RIGHT HERE!!");
                    if (AFRAME.utils.device.checkHasPositionalTracking()) {
                        let nodeName = 'wmrvr-right-' + self.kernel.moniker();
                        createWMRVRControls('right');
                        createWMRVR.call(this, childID, nodeName);
                    }
                }

                if (this.wmrleft == true) {
                    console.log("CREATE WMR LEFT HERE!!");
                    if (AFRAME.utils.device.checkHasPositionalTracking()) {
                        let nodeName = 'wmrvr-left-' + self.kernel.moniker();
                        createWMRVRControls('left');
                        createWMRVR.call(this, childID, nodeName);
                    }
                }

                document.body.appendChild(scene); //append is not working in Edge browser

            }

            if (this.state.nodes[childID] && this.state.nodes[childID].aframeObj) {
                this.nodes[childID] = {
                    id: childID,
                    extends: childExtendsID,
                    // lastTransformStep: 0,
                    // lastAnimationStep: 0 
                };
            }


            // if(this.state.nodes[childID]) {
            //     this.nodes[childID] = {id:childID,extends:childExtendsID};
            // } 
            // else if (this.state.nodes[childID] && this.state.nodes[childID].aframeObj.object3D instanceof THREE.Object3D) {
            //     this.nodes[childID] = {id:childID,extends:childExtendsID};
            // }

        },


        initializedNode: function (nodeID, childID) {

            var node = this.state.nodes[childID];
            if (!node) {
                return;
            }

            if (node.extendsID == "http://vwf.example.com/aframe/gearvrcontroller.vwf") {
                console.log("gearVR controller initialized")
            }


        },

        createdProperty: function (nodeId, propertyName, propertyValue) {
            return this.satProperty(nodeId, propertyName, propertyValue);
        },

        initializedProperty: function (nodeId, propertyName, propertyValue) {
            return this.satProperty(nodeId, propertyName, propertyValue);
        },

        gotProperty: function (nodeId, propertyName, propertyValue) {
            var selfs = this;

            var node = this.state.nodes[nodeId];

            if (!(node && node.aframeObj)) {
                return;
            }




        },

        satProperty: function (nodeId, propertyName, propertyValue) {
            //var selfs = this;

            var node = this.state.nodes[nodeId];

            if (!(node && node.aframeObj)) {
                return;
            }

            if (propertyName == 'position')
               {
                receiveModelTransformChanges( nodeId, propertyValue );
               }

            // if (node.aframeObj.nodeName == "AUDIO" && propertyName == 'itemSrc') {

            //     //console.log("sat new item");
            //     let elID = '#' + node.aframeObj.getAttribute('id');
            //     Object.entries(this.state.nodes).forEach(el => {
            //         let src = el[1].aframeObj.getAttribute('src');
            //         if (src){
            //            // console.log("my: " + src);
            //             if (src == elID)
            //             self.kernel.callMethod(el[0], "updateSrc", [elID])
            //         }
            //     })

            // }

            // if ( propertyName == "position" ) {
            //     receiveModelTransformChanges( nodeId, propertyValue );
            // }


            if (node.aframeObj.nodeName == "AUDIO" && propertyName == 'itemSrc') {

                //console.log("sat new item");
                let elID = '#' + node.aframeObj.getAttribute('id');
                Object.entries(this.state.nodes).forEach(el => {
                    let sound = el[1].aframeObj.getAttribute('sound');
                    if (sound) {

                        let soundID = vwf.find(el[0], 'sound');
                        self.kernel.callMethod(soundID, "refreshSrc", [elID])

                        // if (sound.src !== ""){
                        //     let src = '#' + sound.src.id;
                        //     if (src == elID){
                        //         let soundID = vwf.find(el[0], 'sound');
                        //         self.kernel.callMethod(soundID, "updateSrc", [elID])
                        //     }

                        // }

                    }

                })
            }


            if (node.aframeObj.nodeName == "IMG" && propertyName == 'itemSrc') {
                updateMaterial(node);
            }

            if (node.aframeObj.nodeName == "VIDEO" && propertyName == 'itemSrc') {
                updateMaterial(node);
            }

            if (node.aframeObj.nodeName == "A-ASSET-ITEM" && propertyName == 'itemSrc') {

                //console.log("sat new item");
                let elID = '#' + node.aframeObj.getAttribute('id');
                Object.entries(this.state.nodes).forEach(el => {
                    let src = el[1].aframeObj.getAttribute('src');
                    let mtl = el[1].aframeObj.getAttribute('mtl');
                    if (src) {
                        // console.log("my: " + src);
                        if (src == elID)
                            self.kernel.callMethod(el[0], "updateModel", [elID])
                    }
                    if (mtl) {
                        // console.log("my: " + mtl);
                        if (mtl == elID)
                            self.kernel.callMethod(el[0], "updateModelMtl", [elID])
                    }
                })

            }

            //  if (node.aframeObj.nodeName == "A-BOX" && propertyName == 'color') {

            //     console.log("sat color");
            //     let materialName = '/' + node.name + '/material';
            //     let materialID = vwf.find('', materialName)[0];
            //     if (materialID) {

            //         vwf.setProperty(materialID, propertyName, propertyValue);

            //     }  

            // }

            // if (propertyName == 'position') {
            //     this.nodes[nodeId].lastTransformStep = vwf.time();
            // }


            // var aframeObject = node.aframeObj;
            // switch (propertyName) {
            //     case "clickable":
            //         if (propertyValue) {
            //             aframeObject.addEventListener('click', function (evt) {
            //                 let cursorID = 'cursor-avatar-'+selfs.kernel.moniker();
            //                if (evt.detail.cursorEl.id == cursorID) {
            //                     vwf_view.kernel.fireEvent(nodeId, "clickEvent")
            //                }
            //             });
            //         }
            //         break;
            // }
        },

        deletedNode: function (childID) {
            delete this.nodes[childID];
        },

        firedEvent: function (nodeID, eventName, eventParameters) {

            if ( eventName == "changingTransformFromView" ) {
                var clientThatSatProperty = self.kernel.client();
                var me = self.kernel.moniker();

                // If the transform property was initially updated by this view....
                if ( clientThatSatProperty == me ) {
                    var node = this.state.nodes[ nodeID ];
                    node.ignoreNextTransformUpdate = true;
                }
            }

            //var avatarID = vwf_view.kernel.find("", avatarName)

            var avatarName = 'avatar-' + self.kernel.moniker();

            if (eventName == "createAvatar") {
                console.log("creating avatar...");

                // let avatarID = self.kernel.moniker();
                // var nodeName = 'avatar-' + avatarID;

                var newNode = {
                    "id": avatarName,
                    "uri": avatarName,
                    "extends": "http://vwf.example.com/aframe/avatar.vwf",
                    "properties": {
                        "localUrl": '',
                        "remoteUrl": '',
                        "displayName": 'Avatar ' + randId(),
                        "sharing": { audio: true, video: true },
                        "selectMode": false,
                        "position": [0, 1.6, 0]
                    }
                }

                if (!self.state.nodes[avatarName]) {

                    if (_LCSUSER.is) {
                        _LCSUSER.get('profile').once(res => {
                            var myNode = null;
                            if (res.avatarNode) {
                                myNode = JSON.parse(res.avatarNode);
                            }
                            newNode.properties.displayName = res.alias;

                            vwf_view.kernel.createChild(nodeID, avatarName, newNode);
                            vwf_view.kernel.callMethod(avatarName, "createAvatarBody", [myNode, null]);
                            //"/../assets/avatars/male/avatar1.gltf"


                            //vwf_view.kernel.callMethod(avatarName, 'setUserAvatar', [res] );
                        })
                    } else {

                        vwf_view.kernel.createChild(nodeID, avatarName, newNode);
                        vwf_view.kernel.callMethod(avatarName, "createAvatarBody", []);
                        //"/../assets/avatars/male/avatar1.gltf"
                    }
                }


                // if(_LCSUSER.is){
                //     _LCSUSER.get('profile').get('alias').once(res => {
                //         vwf_view.kernel.callMethod(avatarName, 'setUserAvatar', [res] );
                //     })
                // }




            }

            // if (eventName == "setAvatarRotation") {
            //     vwf_view.kernel.setProperty(avatarName, "rotation", [eventParameters.x, eventParameters.y, eventParameters.z]);
            // }

            //  if (eventName == "setAvatarPosition") {
            //     vwf_view.kernel.setProperty(avatarName, "position", [eventParameters.x, eventParameters.y, eventParameters.z]);
            // }

            if (eventName == "clickEvent") {

                if (self.kernel.moniker() == eventParameters[0]) {

                    let avatar = self.nodes[avatarName];
                    let mode = vwf.getProperty(avatarName, 'selectMode');

                    if (mode) {
                        console.log("allow to click!!!")
                        vwf_view.kernel.setProperty(avatarName, 'selectMode', false);

                        let editorDriver = vwf.views["vwf/view/editor-new"];
                        if (editorDriver) {
                            let selectSwitch = document.querySelector('#selectNodeSwitch');
                            const selectSwitchComp = new mdc.iconToggle.MDCIconToggle(selectSwitch); //new mdc.select.MDCIconToggle
                            selectSwitchComp.on = false;

                            let currentNodeDIV = document.querySelector('#currentNode');
                            if (currentNodeDIV) currentNodeDIV._setNode(nodeID);


                        }
                    }
                }


            }
        },

        ticked: function (vwfTime) {

            updateAvatarPosition();

            //update vr controllers
            if (this.gearvr == true) {
                updateHandControllerVR('gearvr-', '#gearvrcontrol');
            }
            if (this.wmrright == true) {
                updateHandControllerVR('wmrvr-right-', '#wmrvrcontrolright');
            }
            if (this.wmrleft == true) {
                updateHandControllerVR('wmrvr-left-', '#wmrvrcontrolleft');
            }

            // console.log(vwfTime);

            //lerpTick ();
        },

        calledMethod: function (nodeID, methodName, methodParameters, methodValue) {

            var node = this.state.nodes[nodeID];

            if (!(node && node.aframeObj)) {
                return;
            }


            switch(methodName) {
                case "translateBy":
                case "translateTo":
                // No need for rotateBy or rotateTo because they call the quaternion methods
                case "quaterniateBy":
                case "quaterniateTo":
                case "scaleBy":
                case "scaleTo":
                // No need for transformBy or worldTransformBy because they call transformTo and worldTransformTo
                case "transformTo":
                case "worldTransformTo":
                    // If the duration of the transform is 0, set the transforms to their final value so it doesn't interpolate
                    if(methodParameters.length < 2 || methodParameters[1] == 0) {

                        // let compDriver = vwf.views["vwf/view/aframeComponent"];
                        // if (compDriver) {


                        //     compDriver.nodes[nodeID].interpolate.position.lastTick = compDriver.getPosition(nodeID);
                        //     compDriver.nodes[nodeID].interpolate.position.selfTick = goog.vec.Vec3.clone(compDriver.nodes[nodeID].lastTickTransform);

                     
                    //}
                    break;
            }

        }

            if (this.nodes[nodeID].extends == "http://vwf.example.com/aframe/acamera.vwf") {
                if (methodName == "setCameraToActive") {
                    if (methodParameters[0] == vwf.moniker_) {
                        console.log("set active");
                        let offsetComp = node.aframeObj.getAttribute('viewoffset');
                        if (offsetComp) {
                            let offsetCompID = vwf.find(nodeID, 'viewoffset');
                            self.kernel.callMethod(offsetCompID, "setParams", []);
                        }
                        node.aframeObj.setAttribute('camera', 'active', true);
                    }
                }
            }

            if (methodName == "createGooglePoly") {


            }


        }


    });


      // Receive Model Transform Changes algorithm 
    // 1.0 If (own view changes) then IGNORE (only if no external changes have occurred since the user’s view 
    //       requested this change – otherwise, will need to treat like 1.1 or 1.2)
    // 1.1 Elseif (other external changes and no outstanding own view changes) then ADOPT
    // 1.2 Else Interpolate to the model’s transform (conflict b/w own view and external sourced model changes)

    function receiveModelTransformChanges(nodeID, propertyValue ) {

        var node = self.state.nodes[ nodeID ];

        // If the node does not exist in the state's list of nodes, then this update is from a prototype and we
        // should ignore it
        if ( !node ) {
            return;
        }

         // If the transform property was initially updated by this view....
         if ( node.ignoreNextTransformUpdate ) {
            node.outstandingTransformRequests.shift();
            node.ignoreNextTransformUpdate = false;
        } else {
           // adoptTransform( node, transformMatrix );
           var pos = goog.vec.Vec3.clone( propertyValue );
           node.aframeObj.object3D.position.set(pos[0], pos[1], pos[2]);
           
           //setAFrameProperty ('position', propertyValue, node.aframeObj)
        }




    }


    function setAFrameProperty (propertyName, propertyValue, aframeObject) {
        //console.log(propertyValue);
                if (propertyValue.hasOwnProperty('x')) {
                    aframeObject.setAttribute(propertyName, propertyValue)
                } else
                    if (Array.isArray(propertyValue) || propertyValue instanceof Float32Array ) {
                        aframeObject.setAttribute(propertyName, { x: propertyValue[0], y: propertyValue[1], z: propertyValue[2] })
                    } 
                    
                    else if (typeof propertyValue === 'string') {
                        aframeObject.setAttribute(propertyName, AFRAME.utils.coordinates.parse(propertyValue))
                    }
        
            }


    function compareCoordinates(a, b, delta) {
        return Math.abs(a.x - b.x) > delta || Math.abs(a.y - b.y) > delta || Math.abs(a.z - b.z) > delta

    }

    function getWorldRotation(el) {


        var worldQuat = new THREE.Quaternion();
        el.object3D.getWorldQuaternion(worldQuat);

        //console.log(worldQuat);
        let angle = (new THREE.Euler()).setFromQuaternion(worldQuat, 'YXZ');
        let rotation = (new THREE.Vector3(THREE.Math.radToDeg(angle.x),
            THREE.Math.radToDeg(angle.y), THREE.Math.radToDeg(angle.z)));

        return rotation
    }


    function updateAvatarPosition() {

        let delta = 0.0001;

        let avatarName = 'avatar-' + self.kernel.moniker();
        var node = self.state.nodes[avatarName];
        if (!node) return;
        if (!node.aframeObj) return;

        let el = document.querySelector('#avatarControl');
        if (el) {
            //let position = el.object3D.getWorldPosition(); //el.getAttribute('position');

            let position = new THREE.Vector3();
            el.object3D.getWorldPosition(position);

            let rotation = getWorldRotation(el);

            // console.log(rotation);
            //let rotation = el.getAttribute('rotation');

            let lastRotation = self.nodes[avatarName].selfTickRotation;
            let lastPosition = self.nodes[avatarName].selfTickPosition;

            // let currentPosition = node.aframeObj.getAttribute('position');
            // let currentRotation = node.aframeObj.getAttribute('rotation');

            if (position && rotation && lastPosition && lastRotation) {
                if (compareCoordinates(position, lastPosition, delta) || Math.abs(rotation.y - lastRotation.y) > delta) {
                    console.log("not equal!!")
                    vwf_view.kernel.callMethod(avatarName, "followAvatarControl", [position, rotation]);
                }
            }
            self.nodes[avatarName].selfTickRotation = Object.assign({}, rotation);
            self.nodes[avatarName].selfTickPosition = Object.assign({}, position);
        }

    }


    function updateHandControllerVR(aName, aSelector) {
        //let avatarName = 'avatar-' + self.kernel.moniker();

        let delta = 0.0001

        let avatarName = aName + self.kernel.moniker();
        var node = self.state.nodes[avatarName];
        if (!node) return;
        if (!node.aframeObj) return;

        let el = document.querySelector(aSelector);
        if (el) {
            //let position = el.object3D.getWorldPosition() //el.getAttribute('position');

            let position = new THREE.Vector3();
            el.object3D.getWorldPosition(position);

            let rotation = getWorldRotation(el);

            //let rotation = el.getAttribute('rotation');

            let lastRotation = self.nodes[avatarName].selfTickRotation;
            let lastPosition = self.nodes[avatarName].selfTickPosition;

            // let currentPosition = node.aframeObj.getAttribute('position');
            //let currentRotation = node.aframeObj.getAttribute('rotation');

            if (position && rotation && lastRotation && lastPosition) {
                if (compareCoordinates(position, lastPosition, delta) || compareCoordinates(rotation, lastRotation, delta)) {
                    console.log("not equal!!");
                    vwf_view.kernel.callMethod(avatarName, "updateVRControl", [position, rotation]);
                }
            }


            //vwf_view.kernel.callMethod(avatarName, "updateVRControl", [position, rotation]);

            self.nodes[avatarName].selfTickPosition = Object.assign({}, position);
            self.nodes[avatarName].selfTickRotation = Object.assign({}, rotation);

        }
    }


    function createAvatarControl(aScene) {

        let avatarName = 'avatar-' + self.kernel.moniker();

        let avatarEl = document.createElement('a-entity');
        avatarEl.setAttribute('id', 'avatarControlParent');


        if (AFRAME.utils.device.isGearVR()) {
            avatarEl.setAttribute('movement-controls', {});//{'controls': 'gamepad'});
            //avatarEl.setAttribute('position', '0 0 0');
        }

        //avatarEl.setAttribute('position', '0 1.6 0');

        let controlEl = document.createElement('a-camera');

        controlEl.setAttribute('id', 'avatarControl');
        //controlEl.setAttribute('wasd-controls', {});
        controlEl.setAttribute('look-controls', { pointerLockEnabled: false });
        //controlEl.setAttribute('gamepad-controls', {'controller': 0});



        //controlEl.setAttribute('gearvr-controls',{});

        // controlEl.setAttribute('camera', 'near', 0.51);


        //controlEl.setAttribute('position', '0 0 0');


        let cursorEl = document.createElement('a-cursor');
        cursorEl.setAttribute('id', 'cursor-' + avatarName);
        cursorEl.setAttribute('raycaster', {});
        cursorEl.setAttribute('raycaster', 'objects', '.intersectable');
        cursorEl.setAttribute('raycaster', 'showLine', false);

        if (AFRAME.utils.device.isGearVR()) { }

        // cursorEl.setAttribute('raycaster', {objects: '.intersectable', showLine: true, far: 100});
        // cursorEl.setAttribute('raycaster', 'showLine', true);
        controlEl.appendChild(cursorEl);

        avatarEl.appendChild(controlEl);
        aScene.appendChild(avatarEl);

        controlEl.setAttribute('camera', 'active', true);

        // let gearVRControlsEl = document.createElement('a-entity');
        // gearVRControlsEl.setAttribute('id', 'gearvr-'+avatarName);
        // gearVRControlsEl.setAttribute('gearvr-controls', {});
        // aScene.appendChild(gearVRControlsEl);



        //     controlEl.addEventListener('componentchanged', function (evt) {
        //     if (evt.detail.name === 'position') {
        //         var eventParameters = evt.detail.newData;
        //          vwf_view.kernel.setProperty(avatarName, "position", [eventParameters.x, eventParameters.y, eventParameters.z]);
        //     }

        //      if (evt.detail.name === 'rotation') {
        //         var eventParameters = evt.detail.newData;
        //            vwf_view.kernel.setProperty(avatarName, "rotation", [eventParameters.x, eventParameters.y, eventParameters.z]);
        //     }

        // });

    }

    function createWMRVR(nodeID, nodeName) {

        var newNode = {
            "id": nodeName,
            "uri": nodeName,
            "extends": "http://vwf.example.com/aframe/wmrvrcontroller.vwf",
            "properties": {
            }
        }

        if (!self.state.nodes[nodeName]) {

            vwf_view.kernel.createChild(nodeID, nodeName, newNode);
            vwf_view.kernel.callMethod(nodeName, "createController", []);
            //"/../assets/controller/wmrvr.gltf"
        }
    }


    function createGearVRController(nodeID, nodeName) {

        var newNode = {
            "id": nodeName,
            "uri": nodeName,
            "extends": "http://vwf.example.com/aframe/gearvrcontroller.vwf",
            "properties": {
            }
        }

        if (!self.state.nodes[nodeName]) {

            vwf_view.kernel.createChild(nodeID, nodeName, newNode);
            vwf_view.kernel.callMethod(nodeName, "createController", []);
            //"/../assets/controller/gearvr.gltf"
        }
    }

    function createAvatar(nodeID) {

        vwf_view.kernel.fireEvent(nodeID, "createAvatar")

        // let avatarID = self.kernel.moniker();
        // var nodeName = 'avatar-' + avatarID;

        // var newNode = {
        //     "id": nodeName,
        //     "uri": nodeName,
        //     "extends": "http://vwf.example.com/aframe/avatar.vwf"
        // }


        // vwf_view.kernel.createChild(nodeID, nodeName, newNode);
        // vwf_view.kernel.callMethod(nodeName, "createAvatarBody");
    }

    function randId() {
        return '_' + Math.random().toString(36).substr(2, 9);
    }

    function createGearVRControls() {

        let sceneEl = document.querySelector('a-scene');

        let avatarControl = document.querySelector('#avatarControlParent');

        let gearvr = document.createElement('a-entity');
        gearvr.setAttribute('id', 'gearvrcontrol');
        gearvr.setAttribute('gearvr-controls', {
            'hand': 'right',
            'model': true
        });
        // gearvr.setAttribute('gearvr-controls', 'hand', 'right');

        gearvr.setAttribute('gearvrcontrol', {});
        avatarControl.appendChild(gearvr);

    }

    function createWMRVRControls(hand) {

        let sceneEl = document.querySelector('a-scene');

        let avatarControl = document.querySelector('#avatarControlParent');

        let wmrvr = document.createElement('a-entity');
        wmrvr.setAttribute('id', 'wmrvrcontrol' + hand);
        wmrvr.setAttribute('windows-motion-controls', '');
        wmrvr.setAttribute('windows-motion-controls', 'hand', hand);
        wmrvr.setAttribute('wmrvrcontrol', { 'hand': hand });
        avatarControl.appendChild(wmrvr);
    }


    function updateMaterial(node){

        let elID = '#' + node.aframeObj.getAttribute('id');
        Object.entries(self.state.nodes).forEach(el => {
            let material = el[1].aframeObj.getAttribute('material');
            if (material) {
                if (!material.src) {
                    let materialID = vwf.find(el[0], 'material');
                    self.kernel.callMethod(materialID, "refreshSrc",[]);
                }
                else if (material.src) {
                    if (material.src !== "") {
                        let src = '#' + material.src.id;
                        if (src == elID) {
                            let materialID = vwf.find(el[0], 'material');
                            self.kernel.callMethod(materialID, "updateSrc", [elID])
                        }
                    }
                }
            }

        })

    }


});