/**
 * @author netro
 */
var
    x = 4,
    y = 0,
    effectPixelate,
    scale = 0.33,
    earthWireMaterial,
    earthWireObject,
    earthWire,
    cosmosMaterial,
    cosmosObject,
    cosmos,
    smokeParticles = [],
    smokeParticleHolder,
    cameraOrtho,
    sceneOrtho,
    astro,
    spriteAstroMap,
    spriteAstroMaterial,
    sprite,
    spriteEarthMap,
    spriteEarthMaterial,
    smoke,
    smokeGroup,
    smokeGroupexpo1,
    smokeGroupItem = [],
    smokeGroupexpo1Item = [],
    spriteSmokehMap,
    spriteSmokeMaterial,
    spriteSmokehMap2,
    spriteSmokeMaterial2,
    expo1,
    expo2,
    expo3,
    expo4,
    spriteexpo1hMap,
    spriteexpo1Material,
    spriteexpo2hMap,
    spriteexpo2Material,
    spriteexpo3hMap,
    spriteexpo3Material,
    spriteexpo4hMap,
    spriteexpo4Material,
    debugTime = 0,
    textGeom = [],
    textMesh = [],
    animRunned = [],
    fontMaterial = [],
    fontObject,
    fontTimer = [],
    fontLoadedObject,
    objectBoundingBox = [],
    nBar = 41,
    musicTimerActual = 0,
    musicTimerActualFIXED = 0,
    musicTimerStart = 0,
    musicTimerFlag = false,
    end = false,
    volume = 1,
    amplitude = 40,
    frequency = 20,
    controls,
    bloomComposer,
    renderPass,
    bloomPass,
    bokehPass,
    dotPass,
    rgbPass,
    effectCopy,
    pointLight,
    bloomLayer,
    finalPass,
    filmPass,
    finalComposer,
    width = window.innerWidth,
    winWidth = window.innerWidth,
    height = window.innerHeight,
    activeHeight = Math.round(winWidth * (266 / 640)),
    BLOOM_SCENE = 1,
    materials = {},
    objectEarth,
    postprocessing = {},
    shaderSettings = {
        rings: 3,
        samples: 4
    },
    darkMaterial = new THREE.MeshLambertMaterial({ambient: 0xffffff, color: 0x000000}),
    camera, scene, renderer;

var APP = {

    Player: function () {

        var loader = new THREE.ObjectLoader();

        var stats = new Stats();
        // stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
        //
        // document.body.appendChild(stats.dom);

        var events = {};

        var dom = document.createElement('div');

        this.dom = dom;

        this.width = 500;
        this.height = 500;

        this.load = function (json) {

            renderer = new THREE.WebGLRenderer({antialias: true});
            renderer.gammaOutput = true;
            renderer.setClearColor(0x000000);
            renderer.setPixelRatio(window.devicePixelRatio);

            var project = json.project;

            if (project.shadows) renderer.shadowMap.enabled = true;
            if (project.vr) renderer.vr.enabled = true;

            renderer.domElement.id = "demo"

            dom.appendChild(renderer.domElement);
            this.setScene(loader.parse(json.scene));
            this.setCamera(loader.parse(json.camera));

            events = {
                init: [],
                start: [],
                stop: [],
                keydown: [],
                keyup: [],
                mousedown: [],
                mouseup: [],
                mousemove: [],
                touchstart: [],
                touchend: [],
                touchmove: [],
                update: []
            };

            var scriptWrapParams = 'player,renderer,scene,camera';
            var scriptWrapResultObj = {};

            for (var eventKey in events) {

                scriptWrapParams += ',' + eventKey;
                scriptWrapResultObj[eventKey] = eventKey;

            }

            var scriptWrapResult = JSON.stringify(scriptWrapResultObj).replace(/\"/g, '');

            for (var uuid in json.scripts) {

                var object = scene.getObjectByProperty('uuid', uuid, true);

                if (object === undefined) {

                    console.warn('APP.Player: Script without object.', uuid);
                    continue;

                }

                var scripts = json.scripts[uuid];

                for (var i = 0; i < scripts.length; i++) {

                    var script = scripts[i];

                    var functions = ( new Function(scriptWrapParams, script.source + '\nreturn ' + scriptWrapResult + ';').bind(object) )(this, renderer, scene, camera);

                    for (var name in functions) {

                        if (functions[name] === undefined) continue;

                        if (events[name] === undefined) {

                            console.warn('APP.Player: Event type not supported (', name, ')');
                            continue;

                        }

                        events[name].push(functions[name].bind(object));

                    }

                }

            }

            dispatch(events.init, arguments);

        };

        this.setCamera = function (value) {

            camera = value;
            camera.aspect = this.width / this.height;
            camera.updateProjectionMatrix();

            if (renderer.vr.enabled) {

                dom.appendChild(WEBVR.createButton(renderer));

            }

        };

        this.setScene = function (value) {

            scene = value;

        };

        this.setSize = function (width, height) {

            this.width = width;
            this.height = height;

            if (camera) {

                camera.aspect = this.width / this.height;
                camera.updateProjectionMatrix();

            }

            if (renderer) {

                renderer.setSize(width, height);

            }

        };

        function dispatch(array, event) {

            for (var i = 0, l = array.length; i < l; i++) {

                array[i](event);

            }

        }


        /* ----------------------------------
         -----------------------------------------
         -----------------------------------------
         defaults
         _________________________________________

         */
        var fadeIn = function () {

            var el = document.querySelector('.js-fade');
            el.classList.add('fade-in');
            el.classList.remove('fade-out');
            if (el.classList.contains('is-paused')) {
                el.classList.remove('is-paused');
            }
        }

        var fadeOut = function () {

            var el = document.querySelector('.js-fade');
            el.classList.add('fade-out');
            el.classList.remove('fade-in');

        }

        var fontLoader = function () {
            var
                self = this;

            var manager = new THREE.LoadingManager();
            manager.onLoad = function () { // when all resources are loaded
            }

            var font = null;
            fontObject = new THREE.FontLoader(manager);
            fontObject.load('fonts/font_seas.json', function (response) {
                fontLoadedObject = response;
            });

        }

        var absoluteStaticText = function (text, id, size, time, x, y, z) {
            var
                self = this;


            /*
             -----------------------------------
             init objects
             -----------------------------------------[
             */

            fontMaterial[id] = new THREE.LineBasicMaterial({
                color: 0xffffff,
                transparent: true,
                opacity: 0.4,
                side: THREE.DoubleSide
            });

            textGeom[id] = new THREE.TextBufferGeometry(text,
                {
                    size: (size !== undefined ? size : 3), height: 0, curveSegments: 1,
                    font: fontLoadedObject, weight: "normal", style: "normal",
                    bevelThickness: 0, bevelSize: 0, bevelEnabled: false,
                    material: 0, extrudeMaterial: 0
                });

            textMesh[id] = new THREE.Mesh(textGeom[id], fontMaterial[id]);
            textMesh[id].scale.x = textMesh[id].scale.y = textMesh[id].scale.z = 0.202;


            scene.add(textMesh[id]);
            obj = textMesh[id];

            textMesh[id].material.opacity = 1;
            textMesh[id].position.x = x;
            textMesh[id].position.y = y;
            textMesh[id].position.z = z;

            objectBoundingBox[id] = new THREE.Box3().setFromObject(textMesh[id]);

            textMesh[id].material.opacity = 1;

            /*
             ---------------------
             show and hide animation
             ---------------------------------
             */
            function animate() {

                textMesh[id].material.opacity += 0.01;
                requestAnimationFrame(animate);

            }

            requestAnimationFrame(animate);

            var opaOut = setTimeout(function () {

                function animateOut() {

                    textMesh[id].material.opacity -= 0.07;
                    requestAnimationFrame(animateOut);

                }

                requestAnimationFrame(animateOut);

            }, (time !== null ? time : 3000));

        }

        var staticText = function (text, id, object, speed, offset, y) {
            var
                self = this;

            /*
             -----------------------------------
             init objects
             -----------------------------------------[
             */
            fontMaterial[id] = new THREE.LineBasicMaterial({
                color: 0xffffff,
                transparent: true,
                opacity: 0.4,
                side: THREE.DoubleSide
            });

            textGeom[id] = new THREE.TextBufferGeometry(text,
                {
                    size: 3, height: 0, curveSegments: 1,
                    font: fontLoadedObject, weight: "normal", style: "normal",
                    bevelThickness: 0, bevelSize: 0, bevelEnabled: false,
                    material: 0, extrudeMaterial: 0
                });

            textMesh[id] = new THREE.Mesh(textGeom[id], fontMaterial[id]);
            textMesh[id].scale.x = textMesh[id].scale.y = textMesh[id].scale.z = 0.202;

            textMesh[id].position.set = (0, 0, 0)
            scene.add(textMesh[id]);

            textMesh[id].material.opacity = 0;


            /*
             ---------------------
             animation
             ---------------------------------
             */
            function animate() {

                requestAnimationFrame(animate);

            }

            requestAnimationFrame(animate);

            var opaOut = setTimeout(function () {

                function animateOut() {

                    textMesh[id].material.opacity -= 0.07;
                    requestAnimationFrame(animateOut);

                }

                requestAnimationFrame(animateOut);

            }, 13000);


        }

        var animateText = function (text, id, time, x, y, size) {

            /*
             -----------------------------------
             init objects
             -----------------------------------------[
             */
            fontMaterial[id] = new THREE.LineBasicMaterial({
                color: 0xffffff,
                transparent: true,
                opacity: 0.4,
                side: THREE.DoubleSide
            });

            textGeom[id] = new THREE.TextBufferGeometry(text,
                {
                    size: (size !== undefined ? size : 3), height: 0, curveSegments: 1,
                    font: fontLoadedObject, weight: "normal", style: "normal",
                    bevelThickness: 0, bevelSize: 0, bevelEnabled: false,
                    material: 0, extrudeMaterial: 0
                });

            textMesh[id] = new THREE.Mesh(textGeom[id], fontMaterial[id]);
            textMesh[id].scale.x = textMesh[id].scale.y = textMesh[id].scale.z = 0.202;

            textMesh[id].position.set((x !== null ? x : camera.position.x - 10 ), (y !== null ? y : 0), camera.position.z - 10);

            scene.add(textMesh[id]);
            console.log(textMesh[id])

            textMesh[id].material.opacity = 0;


            /*
             ---------------------
             show and hide animation
             ---------------------------------
             */
            function animate() {

                textMesh[id].material.opacity += 0.01;
                textMesh[id].scale.x += 0.0001;
                textMesh[id].scale.y += 0.0001;
                textMesh[id].scale.z += 0.0001;
                requestAnimationFrame(animate);

            }

            requestAnimationFrame(animate);
            console.log('TIME ', time)
            fontTimer[id] = setTimeout(function () {

                function animateOut() {

                    textMesh[id].material.opacity -= 0.07;
                    requestAnimationFrame(animateOut);

                }

                console.log(time, animateOut)
                requestAnimationFrame(animateOut);

            }, (time !== null ? time : 13000));


        }

        /* ----------------------------------
         -----------------------------------------
         -----------------------------------------
         animate
         _________________________________________

         */

        var time, prevTime, randomGravityRotation = [0.01, 0.004, 0.03, 0.0055, 0.044];

        function animate() {

            stats.begin();

            time = performance.now();


            /*
             zenei részek
             --------------------------------------
             */
            nBarHalf = Math.ceil(nBar / 2)
            histo = sound.makeHistogram(nBarHalf);

            var
                histo3 = (histo[2] / 20 < 0.0012 ? 0.0012 : (histo[2] / 20 > 6 ? 6 : histo[2] / 20)),
                histo6 = (histo[6] / 20 < 0.0012 ? 0.0012 : (histo[6] / 20 > 6 ? 6 : histo[6] / 20)),
                histo10 = (histo[10] / 20 < 0.0012 ? 0.0012 : (histo[10] / 20 > 6 ? 6 : histo[10] / 20)),
                histo14 = (histo[4] / 2920 < 0.022 ? 0.022 : histo[4] / 2920);

            if (histo[1] !== 0 && !musicTimerFlag) {
                musicTimerStart = time;
                musicTimerFlag = !musicTimerFlag;
            }
            if (musicTimerFlag) {
                musicTimerActual = time - musicTimerStart;
                musicTimerActualFIXED = ( (musicTimerActual + debugTime) / 1000).toFixed(0);
            }

            // musicTimerActualFIXED = parseFloat(( time / 1000).toFixed(0)) - 3;
            // musicTimerActualFIXED = parseFloat(( time / 1000).toFixed(0));
            console.log(musicTimerActualFIXED)


            if (musicTimerActualFIXED == 1) {

                if (animRunned[5] == undefined) {
                    bokehPass.uniforms["focus"].value = 1200;

                    camera.position.x = 0;
                    camera.position.y = 0;
                    camera.position.z = -70;

                    camera.rotation.x = 0;
                    camera.rotation.y = 0;
                    camera.rotation.z = 0;

                    // /* start */
                    fadeIn();
                    animRunned[5] = true;
                }

            }

            if (musicTimerActualFIXED == 4) {

                if (textGeom[1] == undefined) {

                    textGeom[1] = '';
                    animateText('So God created mankind in his own image,\nin the image of God he created them;\nmale and female he created them.', 1, 4000, null, 1, 2);

                }

            }

            if (musicTimerActualFIXED == 10) {

                if (textGeom[2] == undefined) {

                    textGeom[2] = '';
                    animateText('God blessed them and said to them,\n“Be fruitful and increase in number;\nfill the earth and subdue it.”', 2, 4000, null, 1, 2);

                }

            }

            if (musicTimerActualFIXED == 15) {

                if (textGeom[3] == undefined) {

                    textGeom[3] = '';
                    animateText('Rule over the fish in the sea and the birds\nin the sky and over every living\ncreature that moves on the ground.”', 3, 4000, null, 1, 2);

                }

            }

            if (musicTimerActualFIXED == 23) {

                if (textGeom[4] == undefined) {

                    textGeom[4] = '';
                    animateText('GENESIS', 4, 5000, null, -0.5, 10);

                }

            }

            if (musicTimerActualFIXED >  28) {

                if (animRunned[6] == undefined) {
                    fadeOut();
                    animRunned[6] = true;
                }

            }

            if (musicTimerActualFIXED > 31) {

                if (animRunned[4] == undefined) {
                    bokehPass.uniforms["focus"].value = 2500;
                    camera.rotation.x = 0;
                    camera.rotation.y = 0;
                    camera.rotation.z = 0;
                    camera.position.x = 0;
                    camera.position.y = 0;
                    camera.position.z = -10;

                    scene.children[4].position.z = -140;
                    scene.children[6].position.z = -140;
                    scene.children[5].position.z = -140;
                    scene.children[7].position.z = -140;
                    animRunned[4] = true;
                }

            }


            if (musicTimerActualFIXED > 31) {

                camera.position.z -= -0.023;
                sprite.scale.x += 0.05;
                sprite.scale.y += 0.05;
                sprite.scale.z += 0.05;

                earthWireMaterial.opacity += 0.000013;
                earthWire.scale.x += 0.069;
                earthWire.scale.y += 0.069;
                earthWire.scale.z += 0.069;

                earthWire.rotation.z += 0.001;

            }


            if (musicTimerActualFIXED > 32) {

                if (animRunned[3] == undefined) {

                    fadeIn();

                    scene.children[6].position.z = 0;
                    scene.children[5].position.z = -5;
                    scene.children[4].position.z = -5;
                    scene.children[7].position.z = -2;
                    animRunned[3] = true;
                }


            }

            if (musicTimerActualFIXED > 38) {

                if (animRunned[1] == undefined) {
                    tweenFocusFirst();
                    animRunned[1] = true;
                }
            }

            if (musicTimerActualFIXED == 42) {
                if (animRunned[2] == undefined) {

                    animRunned[2] = true;
                }
            }

            if (musicTimerActualFIXED > 112) {

                for (var i in smokeGroup.children) {

                    smokeGroup.children[i].material.rotation += 0.0001;
                    smokeGroup.children[i].material.opacity += 0.00010;

                    if (smokeGroup.children[i].material.opacity > 0.25)
                        smokeGroup.children[i].material.opacity = 0.25;

                }

            }

            if(musicTimerActualFIXED == 113){
                if (animRunned[2] == undefined) {
                    // tweenFocusHit();
                    animRunned[2] = true;
                }
            }

            if (musicTimerActualFIXED > 128) {

                for (var i in smokeGroup.children) {

                    smokeGroup.children[i].material.opacity -= 0.00018;

                }

            }

            if (musicTimerActualFIXED > 141) {

                for (var i in smokeGroupexpo1.children) {

                    smokeGroupexpo1.children[i].material.rotation += 0.0001;
                    smokeGroupexpo1.children[i].material.opacity += 0.00010;

                    if (smokeGroupexpo1.children[i].material.opacity > 0.22)
                        smokeGroupexpo1.children[i].material.opacity = 0.22;

                }

            }

            if (musicTimerActualFIXED == 192) {
                fadeOut();
            }

            if(musicTimerActualFIXED == 200){
                window.close();
            }

            postprocessing.composer.render(0.1);
            TWEEN.update();
            prevTime = time;

            try {

                dispatch(events.update, {time: time, delta: time - prevTime});

            } catch (e) {

                console.error(( e.message || e ), ( e.stack || "" ));

            }
            stats.end();


        }

        function initPostprocessing() {


        }


        /*
         * p l a y
         * ___________________________________
         */
        this.play = function () {

            console.log('SCENE', scene);
            /*
             Isten megteremtette az embert, saját képmására, az Isten képmására teremtette őt, férfinak és nőnek teremtette őket. 28Isten megáldotta őket, Isten szólt hozzájuk: „Legyetek termékenyek, szaporodjatok, töltsétek be a földet és vonjátok uralmatok alá. Uralkodjatok a tenger halai, az ég madarai és minden állat fölött, amely a földön mozog.”
             */
            /* ___________________________________ */
            /* earth
             /* ___________________________________ */
            spriteEarthMap = new THREE.TextureLoader().load("textures/earth_sphere.png");
            spriteEarthMaterial = new THREE.SpriteMaterial({map: spriteEarthMap, rotation: Math.PI / 4});
            sprite = new THREE.Sprite(spriteEarthMaterial);
            sprite.scale.set(20, 20, 1);
            scene.add(sprite);

            sprite.position.x = 0;
            sprite.position.y = 0;
            sprite.position.z = -30;

            earthWireMaterial = new THREE.MeshBasicMaterial({
                color: 0xffffff,
                wireframe: true,
                transparent: true,
                opacity: 1,
                side: THREE.DoubleSide
            });
            earthWireObject = new THREE.CircleGeometry(5, 64);
            earthWire = new THREE.Mesh(earthWireObject, earthWireMaterial);
            earthWire.position.set(0, 0, -40);
            earthWire.scale.set(20, 20, 20);
            earthWireMaterial.opacity = 0;
            scene.add(earthWire);

            /* ___________________________________ */
            /* expo1
             /* ___________________________________ */
            spriteexpo1Map = new THREE.TextureLoader().load("textures/expo.png");
            spriteexpo1Material = new THREE.SpriteMaterial({map: spriteexpo1Map, rotation: Math.PI / 4});

            spriteexpo2Map = new THREE.TextureLoader().load("textures/expo.png");
            spriteexpo2Material = new THREE.SpriteMaterial({map: spriteexpo2Map, rotation: Math.PI / 4});

            spriteexpo3Map = new THREE.TextureLoader().load("textures/expo.png");
            spriteexpo3Material = new THREE.SpriteMaterial({map: spriteexpo3Map, rotation: Math.PI / 4});

            spriteexpo4Map = new THREE.TextureLoader().load("textures/expo.png");
            spriteexpo4Material = new THREE.SpriteMaterial({map: spriteexpo4Map, rotation: Math.PI / 4});

            /* ________________________ */
            expo1 = new THREE.Sprite(spriteexpo1Material);
            expo1.scale.set(1, 1, 1)
            // scene.add(expo1);

            expo1.position.x = 0;
            expo1.position.y = 0;
            expo1.position.z = 139;

            /* ________________________ */
            expo2 = new THREE.Sprite(spriteexpo2Material);
            expo2.scale.set(1, 1, 1)
            // expo2.add(expo2);

            expo2.position.x = 5;
            expo2.position.y = 0;
            expo2.position.z = 139;

            /* ________________________ */
            expo3 = new THREE.Sprite(spriteexpo3Material);
            expo3.scale.set(1, 1, 1)
            // scene.add(expo3);

            expo3.position.x = -2;
            expo3.position.y = 0;
            expo3.position.z = 139;

            /* ________________________ */
            expo4 = new THREE.Sprite(spriteexpo4Material);
            expo4.scale.set(1, 1, 1)
            // scene.add(expo4);

            expo4.position.x = -1;
            expo4.position.y = 2;
            expo4.position.z = 139;

            /* ___________________________________ */
            /* smoke
             /* ___________________________________ */
            spriteSmokehMap = new THREE.TextureLoader().load("textures/smoke.png");
            spriteSmokeMaterial = new THREE.SpriteMaterial({map: spriteSmokehMap, rotation: Math.PI / 4});

            spriteSmokehMap2 = new THREE.TextureLoader().load("textures/smoke.png");
            spriteSmokeMaterial2 = new THREE.SpriteMaterial({map: spriteSmokehMap2, rotation: Math.PI / 4});

            smokeGroup = new THREE.Group();
            for (var i = 0; i < 6; i++) {

                var fixRandom = [4, 5, 7, 3, 10, 9, 12, 4, 4, 5, 7, 3, 10, 9, 12];
                var fixRandomRadiant = [40, 50, 107, 130, 100, 209, 200, 40, 140, 15, 47, 63, 100, 96, 120];
                var fixRandomPositionZ = [1, 2, 4, 5, 6, 9, 10, 11, 13.5, 14.2, 15.8, 16, 18, 18.5, 19.3];
                spriteSmokeMaterial.opacity = 0;
                spriteSmokeMaterial.rotation = fixRandomRadiant[i];

                smokeGroupItem[i] = new THREE.Sprite(spriteSmokeMaterial);
                smokeGroupItem[i].scale.set(fixRandom[i], fixRandom[i], 1);
                smokeGroupItem[i].position.z = 84 + fixRandomPositionZ[i];
                smokeGroup.add(smokeGroupItem[i])
            }
            scene.add(smokeGroup);

            /* ___________________________________ */
            /* smoke expo1
             /* ___________________________________ */
            smokeGroupexpo1 = new THREE.Group();
            for (var i = 0; i < 3; i++) {

                var fixRandom = [4, 5, 7, 3, 10, 9, 12, 4, 4, 5, 7, 3, 10, 9, 12];
                var fixRandomRadiant = [40, 50, 107, 130, 100, 209, 200, 40, 140, 15, 47, 63, 100, 96, 120];
                var fixRandomPositionZ = [1, 2, 4, 5, 6, 9, 10, 11, 13.5, 14.2, 15.8, 16, 18, 18.5, 19.3];
                spriteSmokeMaterial2.opacity = 0;
                spriteSmokeMaterial2.rotation = fixRandomRadiant[i];

                smokeGroupexpo1Item[i] = new THREE.Sprite(spriteSmokeMaterial2);
                smokeGroupexpo1Item[i].scale.set(fixRandom[i], fixRandom[i], 1);
                smokeGroupexpo1Item[i].position.z = 139 + fixRandomPositionZ[i];
                smokeGroupexpo1.add(smokeGroupexpo1Item[i])
            }
            scene.add(smokeGroupexpo1);

            /* ___________________________________ */
            /* intro plane
             /* ___________________________________ */
            var geometryPlane = new THREE.PlaneGeometry(100, 100, 4);
            var materialPlane = new THREE.MeshBasicMaterial({color: 0x000, side: THREE.DoubleSide});
            plane = new THREE.Mesh(geometryPlane, materialPlane);
            // scene.add(plane);

            /* ___________________________________ */
            /* font loader
             /* ___________________________________ */
            fontLoader();

            /* ___________________________________ */
            /* sphere - earth
             /* ___________________________________ */
            camera.position.x = -600;
            camera.position.y = 0;
            camera.position.z = 10;

            cosmosMaterial = new THREE.MeshBasicMaterial({
                color: 0x000000,
                wireframe: false,
                transparent: false,
                opacity: 1,
                side: THREE.DoubleSide
            });
            cosmosObject = new THREE.SphereGeometry(1, 99, 99);
            cosmos = new THREE.Mesh(cosmosObject, cosmosMaterial);
            cosmos.position.set(0, 0, 0);
            cosmos.scale.set(300.8, 300.8, 300.8);
            scene.add(cosmos);

            /* ___________________________________ */
            filmPass = new THREE.FilmPass(
                1.35,   // noise intensity
                0.025,  // scanline intensity
                10,    // scanline count
                false,  // grayscale
            );
            /* ___________________________________ */
            dotPass = new THREE.ShaderPass(THREE.DotScreenShader);
            dotPass.uniforms['scale'].value = 30;
            rgbPass = new THREE.ShaderPass(THREE.RGBShiftShader);
            rgbPass.uniforms['amount'].value = 0.001;
            /* ___________________________________ */
            renderPass = new THREE.RenderPass(scene, camera);
            /* ___________________________________ */
            bokehPass = new THREE.BokehPass(scene, camera, {
                focus: 2500,
                aperture: 0.2 * 0.00001,
                maxblur: 0.425,
                width: width,
                height: height
            });

            /* ___________________________________ */
            bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.8, 25, 1.499);
            /* ___________________________________ */

            /* ___________________________________ */
            bokehPass.renderToScreen = true;
            bloomPass.renderToScreen = true;
            filmPass.renderToScreen = true;
            dotPass.renderToScreen = true;
            rgbPass.renderToScreen = true;
            /* ___________________________________ */
            bloomComposer = new THREE.EffectComposer(renderer);

            bloomComposer.setSize(window.innerWidth * window.devicePixelRatio, window.innerHeight * window.devicePixelRatio);

            bloomComposer.addPass(renderPass);
            bloomComposer.addPass(filmPass);
            bloomComposer.addPass(dotPass);
            bloomComposer.addPass(rgbPass);
            // bloomComposer.addPass(bloomPass);
            bloomComposer.addPass(bokehPass);
            postprocessing.composer = bloomComposer;
            postprocessing.bokeh = bokehPass;

            /* ___________________________________ */
            /* debug
             /* ___________________________________ */
            function darkenNonBloomed(obj) {

                if (obj.isMesh && bloomLayer.test(obj.layers) === false) {

                    materials[obj.uuid] = obj.material;
                    obj.material = darkMaterial;

                }

            }

            function restoreMaterial(obj) {

                if (materials[obj.uuid]) {

                    obj.material = materials[obj.uuid];
                    delete materials[obj.uuid];

                }

            }

            dispatch(events.start, arguments);

            TWEEN.update();
            renderer.setAnimationLoop(animate);

        };

        this.stop = function () {

            dispatch(events.stop, arguments);

            renderer.setAnimationLoop(null);

        };

        this.dispose = function () {

            while (dom.children.length) {

                dom.removeChild(dom.firstChild);

            }

            renderer.dispose();

            camera = undefined;
            scene = undefined;
            renderer = undefined;

        };

        //

        function onDocumentKeyDown(event) {

            dispatch(events.keydown, event);

        }

        function onDocumentKeyUp(event) {

            dispatch(events.keyup, event);

        }

        function onDocumentMouseDown(event) {

            dispatch(events.mousedown, event);

        }

        function onDocumentMouseUp(event) {

            dispatch(events.mouseup, event);

        }

        function onDocumentMouseMove(event) {

            dispatch(events.mousemove, event);

        }

        function onDocumentTouchStart(event) {

            dispatch(events.touchstart, event);

        }

        function onDocumentTouchEnd(event) {

            dispatch(events.touchend, event);

        }

        function onDocumentTouchMove(event) {

            dispatch(events.touchmove, event);

        }

    }

};

function audioPlayer() {

    webaudio = new WebAudio();

    sound = webaudio.createSound().load('../../music/function2019-3.mp3', function (sound) {
        sound.loop(false).play();
        sound.volume(1);
    });

}


function visibleHeightAtZDepth(depth, camera) {
    // compensate for cameras not positioned at z=0
    const cameraOffset = camera.position.z;
    if (depth < cameraOffset) depth -= cameraOffset;
    else depth += cameraOffset;

    // vertical fov in radians
    const vFOV = camera.fov * Math.PI / 180;

    // Math.abs to ensure the result is always positive
    return 2 * Math.tan(vFOV / 2) * Math.abs(depth);
};

function visibleWidthAtZDepth(depth, camera) {
    const height = visibleHeightAtZDepth(depth, camera);
    return height * camera.aspect;
};

function tweenCameraShake() {
    var from = {
        x: camera.position.x,
        y: camera.position.y,
    };

}

function tweenFocusFirst() {
    var from = {
        x: bokehPass.uniforms["focus"].value,
    };

    var to = {
        x: 1500,
    };
    var tween = new TWEEN.Tween(from)
        .to(to, 4000)
        .easing(TWEEN.Easing.Linear.None)
        .onUpdate(function () {

            bokehPass.uniforms["focus"].value = this.x;

        })
        .onComplete(function () {

        })
        .start();

}

function tweenFocusHit() {
    var from = {
        x: bokehPass.uniforms["focus"].value,
    };

    var to = {
        x: 2600
    };
    var tween = new TWEEN.Tween(from)
        .to(to, 5000)
        .repeat(1)
        .yoyo(true)
        .easing(TWEEN.Easing.Linear.None)
        .onUpdate(function () {

            bokehPass.uniforms["focus"].value = this.x;

        })
        .onComplete(function () {

        })
        .start();

}
