/*
 * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*global document, window, THREE*/

// create "app" namespace if not exists.
window.app = window.app || {
    pages: {}
};

/**
 * "3D Model" WebGL example page.
 * Creates data object describing page.
 *
 * @module model
 * @requires lib/threejs
 * @requires {@link app}
 * @namespace app.pages.model
 * @memberof app
 */

(function modelPage(app) {
    'use strict';

    /**
     * Easing fraction.
     *
     * @memberof model
     * @private
     * @const {number}
     */
    var EASING_FRACTION = 0.05,

        /**
         * Angular velocity around Y-axis.
         *
         * @memberof model
         * @private
         * @const {number}
         */
        TOUCH_SENSITIVITY = 0.03,

        /**
         * Scale of the phone model.
         *
         * @memberof model
         * @private
         * @const {number}
         */
        PHONE_SCALE = 0.8,

        /**
         * Background color of the scene for the phone model.
         *
         * @memberof model
         * @public
         * @const {string}
         */
        BACKGROUND_COLOR = '#000',

        /**
         * Model url.
         *
         * @memberof model
         * @private
         * @const {string}
         */
        MODEL_PATH = 'models-3d/simple-phone',

        /**
         * Reference to the scene.
         *
         * @memberof model
         * @public
         * @type {THREE.Scene}
         */
        scene = new THREE.Scene(),

        /**
         * Reference to the camera.
         *
         * @memberof model
         * @public
         * @type {THREE.PerspectiveCamera}
         */
        camera = new THREE.PerspectiveCamera(
            70,
            app.DEVICE_RADIUS / app.DEVICE_RADIUS,
            1,
            1000
        ),

        /**
         * Reference to the model.
         *
         * @memberof model
         * @private
         * @type {THREE.Mesh}
         */
        model3D = null,

        /**
         * The starting horizontal position of the touch event.
         *
         * @memberof model
         * @private
         * @type {number}
         */
        startX = 0,

        /**
         * The Y-axis rotation angle when the touchstart event is invoked.
         *
         * @memberof model
         * @private
         * @type {number}
         */
        startAngleY = 0,

        /**
         * The required Y-axis rotation angle.
         *
         * @memberof model
         * @private
         * @type {number}
         */
        angleY = 0;

    /**
     * Creates the perspective camera.
     *
     * @memberof model
     * @private
     */
    function setCamera() {
        camera.position.set(0, 400, 600);
        camera.lookAt({x: 0, y: 0, z: 0});
    }

    /**
     * Creates directional and ambient lights on the scene.
     *
     * @memberof model
     * @private
     */
    function createLight() {
        var directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
        directionalLight.position.set(400, 400, 500);
        scene.add(directionalLight);
        scene.add(new THREE.AmbientLight(0x332222));
    }

    /**
     * Loads model from the file. The loaded model is scaled by the specified
     * ratio and added to the scene.
     *
     * @memberof model
     * @private
     */
    function loadModel3D() {
        if (!model3D) {
            new THREE.JSONLoader().load(
                MODEL_PATH,
                function onLoaded(geometry, materials) {
                    model3D = new THREE.Mesh(geometry,
                        new THREE.MeshFaceMaterial(materials));
                    angleY = 0;
                    model3D.rotation.y = Math.PI / 4;
                    model3D.scale.set(PHONE_SCALE, PHONE_SCALE, PHONE_SCALE);
                    scene.add(model3D);
                }
            );
        }
    }

    /**
     * Initializes scene 3D.
     *
     * @memberof model
     * @private
     */
    function init() {
        setCamera();
        createLight();
    }

    /**
     * Recalculates the angle of the model. When user swipes the screen, model
     * is being rotated.
     *
     * @memberof model
     * @private
     */
    function recalculatePositions() {
        if (model3D) {
            model3D.rotation.y +=
                (angleY - model3D.rotation.y) * EASING_FRACTION;
        }
    }

    /**
     * Handles touch start event.
     *
     * @memberof model
     * @private
     * @param {Event} event
     */
    function onTouchStart(event) {
        var touch = event.touches[0];

        event.preventDefault();

        startX = touch.pageX;
        startAngleY = angleY;
    }

    /**
     * Handles touch move event.
     *
     * @memberof model
     * @private
     * @param {Event} event
     */
    function onTouchMove(event) {
        var touch = event.touches[0];

        event.preventDefault();
        angleY = startAngleY + (touch.pageX - startX) * TOUCH_SENSITIVITY;
    }

    /**
     * Registers event listeners.
     *
     * @memberof model
     * @private
     */
    function bindEvents() {
        document.addEventListener('touchstart', onTouchStart, false);
        document.addEventListener('touchmove', onTouchMove, false);
    }

    /**
     * Unregisters event listeners.
     *
     * @memberof model
     * @private
     */
    function unbindEvents() {
        document.removeEventListener('touchstart', onTouchStart, false);
        document.removeEventListener('touchmove', onTouchMove, false);
    }

    /**
     * Calls functions when page becomes current page.
     *
     * @memberof model
     * @public
     */
    function onShow() {
        bindEvents();
        loadModel3D();
    }

    /**
     * Calls function when page stops become current page.
     *
     * @memberof model
     * @public
     */
    function onHide() {
        unbindEvents();
        scene.remove(model3D);
        model3D = null;
    }

    init();

    app.pages.model = {
        number: 5,
        title: 'Model',
        onShow: onShow,
        onHide: onHide,
        scene: scene,
        camera: camera,
        animate: recalculatePositions,
        backgroundColor: BACKGROUND_COLOR
    };

}(window.app));
