/*
 * 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 window, THREE*/

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

/**
 * Light & shadow page.
 *
 * @module light
 * @requires lib/threejs
 * @requires {@link app}
 * @namespace app.pages.light
 * @memberof app
 */

(function lightPage(app) {

    'use strict';

    /**
     * Floor edge size.
     *
     * @memberof light
     * @const {number}
     */
    var FLOOR_SIZE = 2000,

        /**
         * Floor color
         *
         * @memberof light
         * @private
         * @const {number}
         */
        FLOOR_COLOR = 0x076100,

        /**
         * Cube color.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        CUBE_COLOR = 0xff0000,

        /**
         * Cube edge length.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        CUBE_EDGE_LENGTH = 100,

        /**
         * Light bulb color.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        LIGHT_BULB_COLOR = 0xf3f0a0,

        /**
         * Ambient light color.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        AMBIENT_LIGHT_COLOR = 0x5,

        /**
         * Directional light color.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        DIRECTIONAL_LIGHT_COLOR = 0xdfebff,

        /**
         * Directional light intensity.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        DIRECTIONAL_LIGHT_INTENSITY = 1.75,

        /**
         * Angular velocity of the light.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        ANGULAR_VELOCITY = 0.05,

        /**
         * Radius of the light path's curve.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        RADIUS = 200,

        /**
         * Minimal height of the light.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        MIN_HEIGHT = 300,

        /**
         * Amplitude of light's height level.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        HEIGHT_AMPLITUDE = 200,

        /**
         * Shadow map width and height.
         *
         * @memberof light
         * @private
         * @const {number}
         */
        SHADOW_MAP = 256,

        /**
         * Reference to the scene.
         *
         * @memberof light
         * @public
         * @type {THREE.Scene}
         */
        scene = null,

        /**
         * Reference to the camera.
         *
         * @memberof light
         * @public
         * @type {THREE.PerspectiveCamera}
         */
        camera = null,

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

        /**
         * Reference to the light bulb.
         *
         * @memberof light
         * @private
         * @type {THREE.Mesh}
         */
        lightBulb = null,

        /**
         * Reference to the directional light.
         *
         * @memberof light
         * @private
         * @type {THREE.DirectionalLight}
         */
        directionalLight = null,

        /**
         * Progress of light move.
         *
         * @memberof light
         * @private
         * @type {number}
         */
        progress = 0;

    /**
     * Creates the scene.
     *
     * @memberof light
     * @private
     */
    function createScene() {
        scene = new THREE.Scene();
    }

    /**
     * Creates the perspective camera.
     *
     * @memberof light
     * @private
     */
    function createCamera() {
        camera = new THREE.PerspectiveCamera(70,
            app.DEVICE_RADIUS / app.DEVICE_RADIUS, 1, 1000);
        camera.position.set(200, 400, -600);
        camera.lookAt({x: 0, y: 0, z: 0});
    }

    /**
     * Creates the floor.
     * Floor is a square plane with length of edge defined by the FLOOR_SIZE
     * constant and color defined by the FLOOR_COLOR constant. Moreover it
     * receives shadow but not casts shadow.
     *
     * @memberof light
     * @private
     */
    function createFloor() {
        var geometry = new THREE.PlaneGeometry(FLOOR_SIZE, FLOOR_SIZE),
            material = new THREE.MeshLambertMaterial({
                color: FLOOR_COLOR,
                side: THREE.DoubleSide
            }),
            floor = new THREE.Mesh(geometry, material);

        floor.position.set(0, 0, 0);
        floor.rotation.x = Math.PI / 2;
        floor.receiveShadow = true;
        floor.castShadow = false;

        scene.add(floor);
    }

    /**
     * Creates the cube.
     * It is a cubical mesh having edge length defined by the CUBE_EDGE_LENGTH
     * constant value and color defined by the CUBE_COLOR constant value.
     * It casts but not receives shadow. It is placed in the scene directly on
     * the floor.
     *
     * @memberof light
     * @private
     */
    function createCube() {
        cube = new THREE.Mesh(new THREE.CubeGeometry(CUBE_EDGE_LENGTH,
                CUBE_EDGE_LENGTH, CUBE_EDGE_LENGTH),
            new THREE.MeshPhongMaterial({color: CUBE_COLOR}));
        cube.castShadow = true;
        cube.receiveShadow = false;
        cube.position.y = 50;
        scene.add(cube);
    }

    /**
     * Creates the light bulb.
     *
     * @memberof light
     * @private
     */
    function createLightBulb() {
        var geometry = new THREE.SphereGeometry(15, 8, 8),
            material = new THREE.MeshBasicMaterial({color: LIGHT_BULB_COLOR});

        lightBulb = new THREE.Mesh(geometry, material);
        scene.add(lightBulb);
    }

    /**
     * Creates ambient and directional light.
     * Ambient light has color defined by the AMBIENT_LIGHT_COLOR constant.
     * Directional light has color and intensity defined by the following
     * constants: DIRECTIONAL_LIGHT_COLOR, DIRECTIONAL_LIGHT_INTENSITY.
     * The directional light casts shadow.
     * This light casts shadow and this shadow has the following settings:
     * Shadow map width and height is defined by the SHADOW_MAP constant.
     * Shadow darkness is defined by the SHADOW_DARKNESS constant.
     *
     * @memberof light
     * @private
     */
    function createLights() {
        scene.add(new THREE.AmbientLight(AMBIENT_LIGHT_COLOR));

        directionalLight = new THREE.DirectionalLight(DIRECTIONAL_LIGHT_COLOR,
            DIRECTIONAL_LIGHT_INTENSITY);

        directionalLight.castShadow = true;

        directionalLight.shadow.mapSize.width = SHADOW_MAP;
        directionalLight.shadow.mapSize.height = SHADOW_MAP;
        directionalLight.shadow.camera.far = 1500;
        directionalLight.shadow.camera.left = -1000;
        directionalLight.shadow.camera.right = 1000;
        directionalLight.shadow.camera.top = 1000;
        directionalLight.shadow.camera.bottom = -1000;
        directionalLight.castShadow = true;

        scene.add(directionalLight);
    }

    /**
     * Initializes scene 3D.
     *
     * @memberof light
     * @private
     */
    function init() {
        createScene();
        createLights();
        createCamera();
        createFloor();
        createCube();
        createLightBulb();
    }

    /**
     * Recalculates position of light and light bulb.
     * The light path in the XZ plane is circular. Horizontally the light moves
     * sinusoidal.
     *
     * @memberof light
     * @public
     */
    function recalculatePositions() {
        progress += ANGULAR_VELOCITY;

        var x = RADIUS * Math.sin(progress),
            y = MIN_HEIGHT + HEIGHT_AMPLITUDE * Math.sin(progress / 2),
            z = RADIUS * Math.cos(progress);

        lightBulb.position.set(x, y, z);
        directionalLight.position.set(x, y, z);
    }

    init();

    app.pages.light = {
        number: 3,
        title: 'Light & shadow',
        scene: scene,
        camera: camera,
        animate: recalculatePositions
    };

}(window.app));
