import * as Utils from "../../modules/HTMLUtils.js";
import WebGL from "../../modules/WebGLUtils.js";
import M3 from "../../modules/M3.js";
import Figure2D from "../../geometry/Figure2D.js";
import Grid from "../../geometry/Grid.js";

/**
 * Orden de transformaciones utilizando matriz de rotación y escalamiento.
 * @author Melissa Méndez Servín.
 */
window.addEventListener("load", main);

function main(){
    document.body.style.backgroundColor = "white"; 
    var canvas = document.getElementById("gl_canvas");
    var gl = WebGL.init(canvas);
    if(!gl) return;

    var srcTexture = '../../../images/capitulo_04/fave_c.png';
    
    var rparams = { sPoint: {x: 0, y: 0}, width: 1, height: 1};
    var model = new Figure2D(gl, WebGL, 'rect', 1, null, rparams, srcTexture);
    var grid = new Grid(gl, 33);

    let leftSequence = function*(){
        while(true){
            yield 'rotate';
            yield 'wait_after_rotate';
            yield 'scale';
            yield 'wait_after_scale';
            yield 'wait';
            yield 'start';
        }
    };
    let rightSequence = function*(){
        while(true){
            yield 'scale';
            yield 'wait_after_scale';
            yield 'rotate';
            yield 'wait_after_rotate';
            yield 'wait';
            yield 'start';
        }
    };                           

    let leftInfo = { sequence: leftSequence(), step: 'start', time: 0, angle: 0, 
                     sx: 1, sy: 1, matrix: M3.identity(), steps: []};
    let rightInfo = { sequence: rightSequence(), step: 'start', time: 0, angle: 0, 
                     sx: 1, sy: 1, matrix: M3.identity(), steps: []};

    const wait_time = 1.5;
    const max_angle = 40;
    const max_sx = 1;
    const max_sy = 0.5;
    const speed_angle = 13;
    const speed_scale = 0.15;
    
    let container = document.getElementById("container");

    let leftLabelPosition = {bottom: 5, left: 5};
    let rightLabelPosition = {bottom: 5, left: 55};
    leftInfo.label = Utils.createMathBox(container, leftLabelPosition);
    rightInfo.label = Utils.createMathBox(container, rightLabelPosition);
    
    Utils.resize(canvas);
    requestAnimationFrame(render);


    // Para la ejecución correcta de la animación al hacer un resize.
    window.addEventListener('resize', function(){
        if (!window.requestAnimationFrame){
            window.requestAnimationFrame(render);
        }
        if (!window.cancelAnimationFrame)
            window.cancelAnimationFrame = function() {
                time = 0;
        };
    });
    
    
    var then = 0;

    function render(current){
        Utils.resize(canvas);
        
        gl.enable(gl.CULL_FACE);
        gl.enable(gl.DEPTH_TEST);
        gl.enable(gl.SCISSOR_TEST);
        
        const {width, height} = gl.canvas;
        const leftWidth = width / 2 | 0;
        const rightWidth = width - leftWidth;
        
        current *= 0.001;
        var delta_time = current - then;
        then = current;
        
        let boundary = {width: leftWidth, height: height,
                    l: -leftWidth * (1/2), r: leftWidth * (1/2), 
                    t: height * (5/7), b: -height * (2/7)};
        
        var projectionMatrix =  M3.projection(boundary.l, boundary.r, boundary.t, boundary.b);
        
        //dibuja vista izquierda
        gl.scissor(0, 0, leftWidth, height);
        gl.viewport(0, 0, leftWidth, height);
        
        let leftMatrix = getMatrixAndUpdateLabel(delta_time, leftInfo);
        grid.draw(gl, boundary);
        model.draw(gl, leftMatrix, projectionMatrix);
        
        //dibuja vista derecha
        gl.viewport(leftWidth, 0, rightWidth, height);
        gl.scissor(leftWidth, 0, rightWidth, height);
        
        let rightMatrix = getMatrixAndUpdateLabel(delta_time, rightInfo);
        grid.draw(gl, boundary);
        model.draw(gl, rightMatrix, projectionMatrix);
        
        //sincronizar tiempo
        leftInfo.time += delta_time;
        rightInfo.time += delta_time;

        requestAnimationFrame(render);
    }
    /**
     * Devuelve
     * @param {*} delta_time 
     * @param {*} info 
     */
    function getMatrixAndUpdateLabel(delta_time, info){
        if(info.time >= wait_time && info.step != 'rotate' && info.step != 'scale'){ 
            info.time = 0;
            info.step = info.sequence.next().value;
        }
        //Esperamos
        if(info.step == 'rotate'){
            info.angle += speed_angle * delta_time;
        }
        if(info.step == 'scale'){
            info.sy -= speed_scale * delta_time;
        }
        let matrix = getCurrentMatrixAndSetLabel(info);
        //Actualiza etiqueta
        var steps = info.steps.join(' \\ ');
        Utils.setMathBox(info.label, steps);
        return matrix;
    }
    function getCurrentMatrixAndSetLabel(info){
        switch(info.step){
            case 'start':
                info.matrix = M3.scale(126, 148);
                info.steps = [""];
                return info.matrix; 
            case 'rotate':
                if( info.angle >= max_angle ){
                    var rotate = M3.rotate(max_angle);
                    info.angle = 0;
                    info.step = info.sequence.next().value;
                    info.matrix = M3.multiply(rotate, info.matrix);
                    pushRotation(info, max_angle);
                    info.steps.push(" \\ ");
                    return info.matrix;
                }
                pushRotation(info, info.angle);
                var rotate = M3.rotate(info.angle);
                return M3.multiply(rotate, info.matrix);
            case 'scale':
                if( info.sx == max_sx && info.sy <= max_sy){
                    var scale = M3.scale(max_sx, max_sy);
                    info.sy = 1;
                    info.step = info.sequence.next().value;
                    info.matrix = M3.multiply(scale, info.matrix);
                    pushScale(info, max_sy);
                    info.steps.push(" \\ ");
                    return info.matrix;
                }
                pushScale(info, info.sy);
                var scale = M3.scale(info.sx, info.sy);
                return M3.multiply(scale, info.matrix);
            default:
                return info.matrix;
        }
    }
    function pushScale(info, scale){
        info.steps.pop();
        info.steps.push("\\mathbf{S}(" + max_sx + ", " + round(scale) + ") "); 
    }
    function pushRotation(info, angle){
        info.steps.pop();
        info.steps.push("\\mathbf{R}(" +  Math.floor(angle) + " \\degree) "); 
    }
    function round(number){
        return Number(Math.round(number + 'e' + 2) + 'e-' + 2);
    }
}

