"use strict";

import {addLabel, resize, scaleToFitContainer, Slider, TextBox} from "../../modules/HTMLUtils.js";
import WebGL from "../../modules/WebGLUtils.js";
import M4 from "../../modules/M4.js";
import V3 from "../../modules/V3.js";
import GridXZ from "../../geometry/GridXZ.js";
import DirectionalLight from "../../materials/DirectionalLight.js";
import GeometryPainter from "../../geometry/GeometryPainter.js";
import { Cone, Cube, QuadCube, RectangularPrism, Sphere} from "../../geometry/Figure.js";
import Vector3D from "../../geometry/Vector3D.js";
import {TrackballCamera, registerCameraEvents} from "../../modules/Trackballcamera.js";
import Diffuse from "../../materials/Diffuse.js";
import SolidColor from "../../materials/SolidColor.js";

/**
 * Transformación de vista. 
 * Se muestra el espacio Global, de Vista y el resultado de lo que la cámara ve,
 * permitiendo modificar la posición y dirección de la cámara en cada uno de 
 * los espacios mencionados en tiempo real.
 * @author Melissa Méndez Servín.
 */
window.addEventListener("load", main);

function main(){
    var canvas = document.getElementById("gl_canvas");
    var gl = WebGL.init(canvas);
    if(!gl) return;
    
    //Figuras
    let gridXZ = new GridXZ(gl, 68, 0, null);

    let figuresGeom = [new Cube(1), new Sphere(.5, 25, 25), new Cone(.5, 1, 4, false)];
     
    let lightDirection = [0, -4, -2, 0];
    let lightPosition = [0, 2, 0, 0];
    let colors = [[0.780392, 0,0], [0.25, 0.95,0.8], [1, 1,0]];
    let material = { ka: [0.3,.3,.3], kd: [0.780392, 0,0], ks: [0.992157, 0.941176, 0.807843], 
                    shininess: 0.21794872*128};
    let light = { direction: lightDirection, la: [.3, .3, .3], ld: [1,1,1], ls: [1.0, 1.0, 1.0]};
    let uniforms = { u_light: light, u_material: material};  
    let cameraUniforms = {u_color: [0.2,0.2,0.2,1], u_light_position: lightPosition};

    //Objetos a renderizar 
    let objects = [];
    for(var i= 0; i< figuresGeom.length; i++)
        objects.push(new GeometryPainter(gl, new DirectionalLight(gl, WebGL, figuresGeom[i], uniforms)));                                   
    
    let axis = [ new Vector3D(gl, WebGL, new V3(1,0,0), null, .15, [0,.2,1,1], lightPosition),
                 new Vector3D(gl, WebGL, new V3(0,1,0), null, .15, [0,1,0.2,1], lightPosition),
                 new Vector3D(gl, WebGL, new V3(0,0,1), null, .15, [1,.2,0,1], lightPosition)];
            
    //Transformaciones (multiplicación de izq a der)
    let transformations = [ M4.multiplyArray([M4.scale(2, 2, 2), M4.translate(0,0.5,0), M4.translate(-1.5,0,-1.5)]),//Cube
                            M4.multiplyArray([M4.scale(1, 1, 1), M4.translate(0,.5,0), M4.translate(1.5,0,1.5)]),//Esfera
                            M4.multiplyArray([M4.scale(3, 1, 3), M4.translate(0,0.5,0), M4.translate(-1,2,-1), M4.rotateY(45)])];//Cono
    //Cámera
    let rectPrismFigure = new RectangularPrism(.42, .5, .6); //Base para la cámara
    let coneFigure = new Cone(.23,.4, 25); //Lente de la cámara
    let camera1 = new TrackballCamera(new V3(0,1.5,0.7), new V3(-0.7,1.4,0), null, false);
    let cameraInOriginTransformations = [M4.identity(), M4.multiply(M4.translate(0,0,-.05), M4.rotateX(90))];

    let bodyCamera = new GeometryPainter(gl, new Diffuse(gl, WebGL, rectPrismFigure, cameraUniforms), M4.translate(0,0,.25));
    let coneCamera = new GeometryPainter(gl, new Diffuse(gl, WebGL, coneFigure, cameraUniforms));                                         
    let cameraDirVector = new Vector3D(gl, WebGL, camera1.target.normalize(), camera1.pos, .1, [.8,.8,0,1], lightPosition);
    
    //Frustrum 
    let frustrum = new GeometryPainter(gl, new SolidColor(gl, WebGL, new QuadCube(2), [0,0,0,1]));

    //HTML
    let controls = document.getElementById("ui-container");
    let globalLabel = new TextBox(container, {}, "Espacio Global");
    let viewLabel = new TextBox(container, {}, "Espacio de vista");
    let resultLabel = new TextBox(container, {}, "Resultado vista");
    let controls_pos = document.getElementById("ui-container-pos");
    let controls_dir = document.getElementById("ui-container-dir");
    //controls_pos.className = "bottom-left-box";
    addLabel(controls_pos, "Posición de la cámara");
    var pos_x = new Slider(controls_pos, "x", -5, 5, upadeteCPos(0), camera1.pos.x.toFixed(2), 0.01, ["#4994D0"]);
    var pos_y = new Slider(controls_pos, "y", -5, 5, upadeteCPos(1), camera1.pos.y.toFixed(2), 0.01, ["#4994D0"]);
    var pos_z = new Slider(controls_pos, "z", -5, 5, upadeteCPos(2), camera1.pos.z.toFixed(2), 0.01, ["#4994D0"]);
    
    addLabel(controls_dir, "Objetivo de la cámara");
    //controls_dir.className = "bottom-left-box";
    var target_x = new Slider(controls_dir, "x", -5, 5, upadeteCTarget(0), camera1.target.x.toFixed(2), 0.01, ["#4994D0"]);
    var target_y = new Slider(controls_dir, "y", -5, 5, upadeteCTarget(1), camera1.target.y.toFixed(2), 0.01, ["#4994D0"]);
    var target_z = new Slider(controls_dir, "z", -5, 5, upadeteCTarget(2), camera1.target.z.toFixed(2), 0.01, ["#4994D0"]);
   
    var modelViewMatrix;   
    let minusZ = new V3(0,0,-1);                     
    let origin = new V3(0,0,0);
    let zNear = 1;
    let zFar = 20;
    let pos = new V3(0,2,4.2);
    let camera = new TrackballCamera(pos);
    camera.setZoomConfig(10,3.2);

    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.SCISSOR_TEST);
    gl.clearColor(0, 0, 0, 0);
    
    resize(canvas);
    window.addEventListener('resize', draw);
    registerCameraEvents(camera, canvas, draw);
    draw();


    function draw(){
        resize(canvas);
        scaleToFitContainer(controls, {x:0, y:55})
        var {width, height} = gl.canvas;
        const smallWidth = width/2 -10| 0;
        const smallHeight = height/2 -20| 0; 
        const bottomHeight =  height- smallHeight;

        globalLabel.setPosition({top: 10, left: (smallWidth) * .35, type: "px"});
        viewLabel.setPosition({top: 10, left: (smallWidth +10 )* 1.35, type: "px"});
        resultLabel.setPosition({top: smallHeight *1.2, left: (smallWidth +10 )* 1.35, type: "px"});


        let aspect = smallWidth/smallHeight;
        var projectionMatrix = M4.perspective(60, aspect, zNear, zFar);
       
        var viewMatrix = camera.getMatrix();
        var lightDir = M4.multiplyVector(viewMatrix, lightDirection);
        var lightPos = M4.multiplyVector(viewMatrix, lightPosition);
        
        //Izquierda
        gl.viewport(0, smallHeight, smallWidth, smallHeight);
        gl.scissor(0, smallHeight, smallWidth, smallHeight);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        renderLocalScene(0, viewMatrix, projectionMatrix, lightDir, lightPos);
        //Derecha
        gl.viewport(smallWidth+10, smallHeight, smallWidth, smallHeight);
        gl.scissor(smallWidth+10, smallHeight, smallWidth, smallHeight);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        renderLocalScene(1, viewMatrix, projectionMatrix, lightDir, lightPos);
        //Abajo
        aspect = smallWidth/bottomHeight;
        gl.viewport(smallWidth+10, 0, smallWidth, smallHeight);
        gl.scissor(smallWidth+10, 0, smallWidth, smallHeight);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        viewMatrix = camera1.getMatrix();
        lightDir = M4.multiplyVector(viewMatrix, lightDirection);
        lightPos = M4.multiplyVector(viewMatrix, lightPosition);
        renderLocalScene(2, viewMatrix, projectionMatrix, lightDir, lightPos);

    }
    function renderLocalScene(id, viewMatrix, projectionMatrix, lightDir, lightPos){
        var viewMatrixForObj = (id == 1) ? M4.multiply(viewMatrix, camera1.getMatrix()) : viewMatrix;
        //var viewMatrixForSystem = (id == 2) ? M4.multiply(camera1.getMatrix(), M4.inverse(viewMatrixCamera1)) :viewMatrix;

        for(var i = 0; i < figuresGeom.length; i++){
            uniforms.u_light.direction = lightDir;
            uniforms.u_material.kd = colors[i];
            uniforms.u_material.ka = colors[i];
            
            modelViewMatrix = M4.multiply(viewMatrixForObj, transformations[i]);
            objects[i].draw(gl, modelViewMatrix, projectionMatrix, uniforms);
        }
        if(id < 2){
            gridXZ.draw(gl, viewMatrix, projectionMatrix);
        
            for(var i=0; i < 3; i++){
                axis[i].draw(gl, viewMatrix, projectionMatrix, lightPos);
            }
            //Matriz para alinear la cámara
            var camera_dir_vector = camera1.target.sub(camera1.pos);
            var normalize_dir = camera_dir_vector.normalize();
            let a_axis = minusZ.cross(camera_dir_vector).normalize();
            let theta = Math.acos(minusZ.dotProduct(normalize_dir));
            let rotateToDirVector = M4.rotate(a_axis, theta);

            var translateFrustrum;
            var coneTransformation;
            var bodyTransformation; 

            if(id == 0){
                coneTransformation = M4.multiplyArray([M4.translate(camera1.pos.x, camera1.pos.y, camera1.pos.z ), rotateToDirVector, M4.rotateX(90)]);
                bodyTransformation = M4.multiply(M4.translate(camera1.pos.x, camera1.pos.y, camera1.pos.z), rotateToDirVector);
                cameraDirVector.draw(gl, viewMatrix, projectionMatrix, lightPos, normalize_dir, camera1.pos);
                translateFrustrum = M4.multiply(bodyTransformation, M4.rotateX(-5));
            }else{
                bodyTransformation = cameraInOriginTransformations[0];
                coneTransformation = cameraInOriginTransformations[1];
                cameraDirVector.draw(gl, viewMatrix, projectionMatrix, lightPos, minusZ, origin);
                translateFrustrum = M4.identity()
            }
            bodyCamera.draw(gl, M4.multiply(viewMatrix, bodyTransformation), projectionMatrix);
            coneCamera.draw(gl, M4.multiply(viewMatrix, coneTransformation), projectionMatrix);
            var frustrumModelMatrix = M4.multiply(translateFrustrum,  M4.inverse(projectionMatrix));
            modelViewMatrix = M4.multiply(viewMatrix, frustrumModelMatrix);
            frustrum.drawWireframeQuad(gl, modelViewMatrix, projectionMatrix);
            
        }
    }
    function upadeteCPos(index){
        return function(value){
            switch(index){
                case 0:
                    camera1.pos.x = value;
                    break;
                case 1:
                    camera1.pos.y = value;
                    break;
                case 2:camera1.pos.z = value;
                    break;
            }
            draw();
        }
    }
    function upadeteCTarget(index){
        return function(value){
            switch(index){
                case 0:
                    camera1.target.x = value;
                    break;
                case 1:
                    camera1.target.y = value;
                    break;
                case 2:
                    camera1.target.z = value;
                    break;
            }
            draw();
        }
    }
}