/**
 * Created by Idear Learning S.A.S.
 * @Mauro García
 * 2019/11/05
 */
/* cmi.core.exit ("tiempo de espera", "suspender", "cerrar sesión", "" , WO) Indica cómo o por qué el alumno dejó el SCO
cmi.core.session_time (CMITimespan , WO) Cantidad de tiempo que el alumno ha pasado en la sesión de alumno actual para este SCO */
function base_sco() { // Clase que se encarga de administrar el scorm para esta plantilla.

    var startTimeStamp = null;
    // record the time that the learner started the SCO so that we can report the total time
    startTimeStamp = new Date();

    this.estructura = {};
    this.orden = {};
    this.leccion = "";
    this.datos = {};
    this.ok = false;

    this.comandos = { //Lista los comandos que se usan habitualmente en las transcciones SCORM
        'id': "cmi.core.student_id",
        'estudiante': "cmi.core.student_name",
        'estado': "cmi.core.lesson_status",
        'ultima': "cmi.core.lesson_location",
        'puntaje': "cmi.core.score.raw",
        'pmax': "cmi.core.score.max",
        'pmin': "cmi.core.score.min",
        'datos': "cmi.suspend_data",
        'tiempo': "cmi.core.total_time",
        'tiempose': "cmi.core.session_time",
    }

    /* cmi.core.session_time: Esta es la cantidad de tiempo en horas, minutos y segundos que el estudiante ha pasado en el SCO en el momento en que lo deja. Es decir, esto representa el tiempo desde el comienzo de la sesión hasta el final de un solo uso del SCO. Esto se utiliza para realizar un seguimiento del tiempo que pasa en un SCO durante una sesión. El LMS usará este tiempo para determinar el valor de cmi.core.total_time. */

    /* cmi.core.student_name: nombre del estudiante establecido por el LMS en la variable studentID */

    function set_cadena(cad) { // Restablece los valores que recibe de la plataforma
        if (cad == null || cad == "") return;
        var parts = cad.split('|');
        for (var i = 0; i < parts.length; i++) {
            console.log(parts[i]);
            this.datos[parts[i]] = {};
            var vals = parts[i + 1].split('~');
            for (var h in vals) {
                var pat = vals[h].split(':');
                this.datos[parts[i]][pat[0]] = pat[1];
            }
            i++;
        }
        for (var i in this.datos) {
            this.calificar(i);
        }
    }

    function get_cadena() { //Genera la cadena con la información SCORM para ser guardada en la base de datos
        var cad = ""
        for (var i in this.datos) {
            cad = cad + i + "|";
            for (var h in this.datos[i]) {
                if (this.datos[i][h] != null && this.datos[i][h] !== "") {
                    cad = cad + h + ":" + this.datos[i][h] + '~';
                }
            }
            cad = cad.substr(0, cad.length - 1);
            cad = cad + "|";
        }
        cad = cad.substr(0, cad.length - 1);
        return cad;
    }

    this.add = function (leccion, tipo, id) { //Método para adicionar elementos a la estructura SCORM
        if (this.estructura[leccion] == null) {
            this.estructura[leccion] = {};
        }
        if (this.estructura[leccion][tipo] == null) {
            this.estructura[leccion][tipo] = [];
        }
        this.orden[leccion + "_" + id] = this.length;
        this.estructura[leccion][tipo].push(id);
    }

    this.inicializar = function () { //Acondiciona la plantilla para funcionar con SCORM


        this.insignias();
        var destino = this;
        document.body.addEventListener('unload', function () {
            destino.fin();
        });
        if (this.api == null) return;
        this.api.LMSInitialize("");
        this._id = this.api.LMSGetValue(this.comandos.id);
        this._estudiante = this.api.LMSGetValue(this.comandos.estudiante);
        this._estado = this.api.LMSGetValue(this.comandos.estado);
        this._ultima = this.api.LMSGetValue(this.comandos.ultima);
        this._puntaje = this.api.LMSGetValue(this.comandos.puntaje);
        this._datos = this.api.LMSGetValue(this.comandos.datos);
        this._tiempo = this.api.LMSGetValue(this.comandos.tiempo);
        this._tiempose = this.api.LMSGetValue(this.comandos.tiempose);
        this.cadena = this._datos;

        this.usuario();

    }

    this.usuario = function () {
        // var studentName = this.api.LMSGetValue("cmi.core.student_name");
        var studentName = this._estudiante;
        var nameS = studentName.split(","); // separate the last and first names
        var lastName = nameS[0];
        var firstName = nameS[1];
        nameS = firstName.split(" "); // separa el primer y segundo nombre de la mitad inicial first name
        // firstName = nameS[0];
        firstName = nameS[1] + " " + nameS[2];

        function getFirstName() {
            return firstName;
        }

        function getLastName() {
            return lastName;
        }

        this.nom_usuario = document.getElementById('nombre_usuario');
        // this.nom_usuario.innerHTML = "Usuario: " + this._estudiante;
        this.nom_usuario.innerHTML = "Usuario: " + firstName + " " + lastName;
    }

    this.insignias = function () { // Busca y procesa las insignias
        this.insignias_vistas = {
            '_van_': 0
        };
        this.ic_insignias = document.getElementsByClassName('insignia');
        this.ct_insignias = document.getElementById('contador_insignias');
        this.ct_insignias.innerHTML = "0/" + this.length;
        for (var i in this.ic_insignias) {
            if (this.ic_insignias[i] != null) {
                if (i >= this.length) {
                    this.ic_insignias[i].classList.add('oculta'); //Oculta las insignias sobrantes
                }
            }
        }
    }

    this.encender_insignia = function (lec, id) { //Enciende la insignia que corresponde, recibe la lección y el id
        this.ic_insignias[this.orden[lec + "_" + id]].classList.add('on'); //Adiciona clase on a la insignia
        this.ct_insignias.innerHTML = document.getElementsByClassName('insignia on').length + "/" + this.length; //Cambia el texto del contador de insignias, actualizado 18/03/2020
        //console.log(document.getElementsByClassName('insignia on').length);
        //console.log(this.length);

    }

    this.preparar = function (lec) { //Este método se llama cada vez que una lección se carga.
        if (lec == null) lec = this.leccion; //Establece la lección.
        var elems = this.estructura[lec]; //Identifica los elementos
        if (elems == null) return; //Valida que los elementos no sean nulos

        scop_vid(this, elems.video); //Busca y adiciona eventos a los videos
        scop_aud(this, elems.audio); //Busca y adiciona eventos a los audios
        scop_bot(this, elems.botones); //Busca y adiciona eventos a los botones
        scop_act(this, elems.actividades); //Busca y adiciona a las actividades
        this.calificar(lec);
    }

    this.guardar = function () {
        if (this.api == null) return; //Verifica que si haya conexión con SCORM
        this.api.LMSSetValue(this.comandos.datos, this.cadena); //Guarda la cadena SCORM
        this.api.LMSSetValue(this.comandos.puntaje, this.puntaje); //Guarda el puntaje actual

        // inicio grabar registro de duracion sesion scorm
        var endTimeStamp = new Date();
        var totalMilliseconds = (endTimeStamp.getTime() - startTimeStamp.getTime());
        var scormTime = ConvertMilliSecondsToSCORMTime(totalMilliseconds, false);

        // ScormProcessSetValue("cmi.core.session_time", scormTime);
        this.api.LMSSetValue(this.comandos.tiempose, scormTime); // Guarda el tiempo en la sesion de sco

        // fin grabar registro de duracion sesion scorm

        this.commit(); //Asienta los datos guardados en plataforma
    }

    this.commit = function () {
        var res = this.api.LMSCommit("").toString(); // Commando para asentar datos SCORM en plataforma
        if (res == "true") return true;
        else {
            this.log_scorm.ultimo = "Commit";
            return false;
        }
    }

    this.fin = function () { //Método que se llama cuando el material SCORM se cierra.
        this.commit();
        var res = this.api.LMSFinish("").toString(); //Le indica a la plataforma que terminó la sesión.
        if (res == "true") {
            return true;
        } else if (scormGetLastError() == "301") {
            return true;
        } else {
            this.log_scorm.ultimo = "Finish";
            return false;
        }
    }

    function scop_vid(destino, lista) {
        for (var i in lista) {
            var vid = document.getElementById(lista[i]); //Busca el video por id

            console.log("llego a la llamada a video: " + lista[i]);

            if (vid == null) {
                destino.log_elems.ultimo = "En la leccion " + destino.leccion + " No se encontró el vídeo con id " + lista[i];
                continue;
            }
            var vtp = new YT.Player(vid); //Instancia el API de Youtube
            function estado(e) {
                if (e.data == YT.PlayerState.PLAYING) { //Cuando al video le dan play
                    console.log("Cuando al video le dan play");
                    console.log(e.target); // con este codigo se sabe que letra actualizo la API de Youtube
                    //console.log(e.target.h);
                    //console.log(e.target.h.id);
                    // destino.estado(e.target.a.id, '3'); //Guarda el estado 3 en el id del video. vieja codigo
                    //destino.estado(e.target.f.id, '3'); //Guarda el estado 3 en el id del video. , modificado 17/03/2020 cambios API YT
                    destino.estado(e.target.h.id, '3'); //Guarda el estado 3 en el id del video. , modificado 03/02/2021 cambios en API YT

                }
            }
            vtp.addEventListener('onStateChange', estado); //Enlaza el evento cambio de estado del video.
        }
    }

    function scop_aud(destino, lista) {
        for (var i in lista) {
            var aud = document.getElementById(lista[i]); //Busca los audios por id
            if (aud == null) {
                destino.log_elems.ultimo = "En la leccion " + destino.leccion + " No se encontró el audio con id " + lista[i];
                continue;
            }

            function estado(e) {
                destino.estado(e.target.id, '3'); //Guarda el estado 3 cuando el video se reproduce
            }
            aud.addEventListener('playing', estado); //Enlaza el evento reproduciendo.
        }
    }

    function scop_bot(destino, lista) {
        for (var i in lista) {
            var bot = document.getElementById(lista[i]); //Busca los botones por id
            if (bot == null) {
                destino.log_elems.ultimo = "En la leccion " + destino.leccion + " No se encontró el botón con id " + lista[i];
                continue;
            }

            function estado(e) {
                var tar = e.target; // Verifica el botón
                while (tar.scorm != true && tar != null) { // Lo busca entre los padres
                    tar = tar.parentElement;
                }
                if (tar != null) {
                    destino.estado(tar.id, '3'); //Guarda el estado SCORM
                }
            }
            bot.scorm = true; //Crea un flag en el botón para poder identificarlo luego.
            bot.addEventListener('click', estado); //Enlaza el evento click
        }
    }

    function scop_act(destino, lista) {
        for (var i in lista) {
            var act = document.getElementById(lista[i]); // Busca las actividades por ID
            if (act == null) {
                destino.log_elems.ultimo = "En la leccion " + destino.leccion + " No se encontró la actividad con id " + lista[i];
                continue;
            }

            function aprueba(e) {
                destino.estado(e.target.id, '3'); //Guarda estado SCORM 1ra vez realizado act
            }

            function reprueba(e) {
                destino.estado(e.target.id, '1'); //Guarda estado SCORM
            }
            act.addEventListener('aprueba', aprueba); //Enlaza el evento aprueba
            act.addEventListener('reprueba', reprueba); //Enlaza el evento reprueba
        }
    }

    function fin_leccion(lec) { //Cuando una lección completa todos los elementos asociados
        var elem = document.getElementById(lec);
        if (elem != null) {
            var res = elem.getElementsByClassName('iconoCheck'); //Busca el check
            if (res[0] != null) {
                res[0].classList.add('checkActive'); //Activa el check
            }
        }
    }
    this.estado = function (id, val) { //Almacena el estado de un ID
        if (this.datos[this.leccion] == null) {
            this.datos[this.leccion] = {};
        }
        if (this.datos[this.leccion][id] != '3') {
            this.datos[this.leccion][id] = val;
        }
        this.calificar(this.leccion); //Califica el avance de la lección
    }

    this.calificar = function (lec) {
        var bola = document.getElementById('bola_progreso'); //captura la bola de progreso
        var tx = document.getElementById('tx_progreso'); //Captura el tx de progreso
        if (this.datos[lec] == null) {
            this.datos[lec] = {};
        }
        var leccion = this.estructura[lec];
        var tod = [];
        for (var i in leccion) {
            tod = tod.concat(leccion[i]);
        }
        var frac = 100 / tod.length; //Calcula el peso de calificación de cada elemento
        var listos = 0;
        for (var i in tod) {
            if (this.datos[lec][tod[i]] == '3') {
                this.encender_insignia(lec, tod[i]); //Enciende la insignia que corresponde a la lec y el id
                listos++;
            }
        }
        if (frac == Infinity) {
            var ntema = 100;
        } else {
            var ntema = Math.round(listos * frac);
        }
        if (ntema > 100) {
            ntema = 100;
        }
        this.datos[lec]['_pun_'] = ntema;
        if (ntema == 100) {
            fin_leccion(lec);
        }
        var tems = 0;
        var nota = 0;
        for (var i in this.estructura) {
            tems++;
            if (this.datos[i] != null) {
                if (this.datos[i]['_pun_'] != null) {
                    nota += parseInt(this.datos[i]['_pun_']);
                }
            }
        }
        nota = Math.round(nota / tems);
        var pos = (nota * (85 / 100)) + 4;
        bola.style.setProperty('left', pos + "%");
        tx.style.setProperty('margin-left', pos + "%");
        tx.innerHTML = nota + "%";
        this.puntaje = nota;
        this.guardar();
    }

    // inicio funcion guardar duracion en SCO 
    //SCORM requires time to be formatted in a specific way

    function ConvertMilliSecondsToSCORMTime(intTotalMilliseconds, blnIncludeFraction) {

        var intHours;
        var intintMinutes;
        var intSeconds;
        var intMilliseconds;
        var intHundredths;
        var strCMITimeSpan;

        if (blnIncludeFraction == null || blnIncludeFraction == undefined) {
            blnIncludeFraction = true;
        }

        //extract time parts
        intMilliseconds = intTotalMilliseconds % 1000;

        intSeconds = ((intTotalMilliseconds - intMilliseconds) / 1000) % 60;

        intMinutes = ((intTotalMilliseconds - intMilliseconds - (intSeconds * 1000)) / 60000) % 60;

        intHours = (intTotalMilliseconds - intMilliseconds - (intSeconds * 1000) - (intMinutes * 60000)) / 3600000;

        /*
        deal with exceptional case when content used a huge amount of time and interpreted CMITimstamp 
        to allow a number of intMinutes and seconds greater than 60 i.e. 9999:99:99.99 instead of 9999:60:60:99
        note - this case is permissable under SCORM, but will be exceptionally rare
        */

        if (intHours == 10000) {
            intHours = 9999;

            intMinutes = (intTotalMilliseconds - (intHours * 3600000)) / 60000;
            if (intMinutes == 100) {
                intMinutes = 99;
            }
            intMinutes = Math.floor(intMinutes);

            intSeconds = (intTotalMilliseconds - (intHours * 3600000) - (intMinutes * 60000)) / 1000;
            if (intSeconds == 100) {
                intSeconds = 99;
            }
            intSeconds = Math.floor(intSeconds);

            intMilliseconds = (intTotalMilliseconds - (intHours * 3600000) - (intMinutes * 60000) - (intSeconds * 1000));
        }

        //drop the extra precision from the milliseconds
        intHundredths = Math.floor(intMilliseconds / 10);

        //put in padding 0's and concatinate to get the proper format
        strCMITimeSpan = ZeroPad(intHours, 4) + ":" + ZeroPad(intMinutes, 2) + ":" + ZeroPad(intSeconds, 2);

        if (blnIncludeFraction) {
            strCMITimeSpan += "." + intHundredths;
        }

        //check for case where total milliseconds is greater than max supported by strCMITimeSpan
        if (intHours > 9999) {
            strCMITimeSpan = "9999:99:99";

            if (blnIncludeFraction) {
                strCMITimeSpan += ".99";
            }
        }

        return strCMITimeSpan;

    }

    function ZeroPad(intNum, intNumDigits) {

        var strTemp;
        var intLen;
        var i;

        strTemp = new String(intNum);
        intLen = strTemp.length;

        if (intLen > intNumDigits) {
            strTemp = strTemp.substr(0, intNumDigits);
        } else {
            for (i = intLen; i < intNumDigits; i++) {
                strTemp = "0" + strTemp;
            }
        }

        return strTemp;
    }

    // fin funcion guardar duracion en SCO






    function logger() { //Admin del log, sirve para ratrear errores durante las pruebas y revisiones.
        this._ultimo = "";
        this.msg_tx = "";
        this.toString = function () {
            return this.msg_tx;
        }

        function get_ultimo() {
            return this._ultimo;
        }

        function set_ultimo(val) {
            this._ultimo = val;
            if (this.msg_tx.length > 0) {
                this.msg_tx = val + "\n" + this.msg_tx;
            } else {
                this.msg_tx = val;
            }
        }
        //console.log(Object.defineProperty(this, 'ultimo', {get:get_ultimo, set:set_ultimo}));
        Object.defineProperty(this, 'ultimo', {
            get: get_ultimo,
            set: set_ultimo
        });
    }

    function get_api(destino) { //Lanzador de la búsqueda del API
        var api = buscar_api(window.parent, destino);
        if (api == null) api = buscar_api(window, this, true);
        if (api != null) {
            destino.log_scorm.ultimo = "API SCORM encontrado";
            destino.ok = true;
        } else {
            destino.log_scorm.ultimo = "No se encontró el API SCORM";
        }
        return api;
    }

    function buscar_api(ventana, destino, lanzador) { //Buscador del API SCORM de la plataforma
        if (ventana == null) return null;
        var rec = 0;
        while (ventana.API == null && ventana.parent != null && ventana.parent != ventana) {
            rec++;
            if (rec >= 100) {
                destino.log_scorm.ultimo = "ERROR: Múltiple recursión buscando el API";
                break;
            }
            ventana = ventana.parent;
        }
        var api = ventana.API;
        if (lanzador == true && ventana == null) {
            api = buscar_api(ventana.opener);
        }
        return api;
    }

    function get_error() {
        if (this.api != null) {
            this.api.LMSGetLastError();
        } else {
            this.log.ultimo = "No está presente el API scorm"
        }
    }

    function get_log() {
        return this.log_scorm + "";
    }

    function get_puntaje() {
        return this._puntaje;
    }

    function set_puntaje(val) {
        this._puntaje = val;
    }

    function get_length() { //Devuelve la cantidad de elementos de la estructura
        var lon = 0;
        if (this.estructura != null) {
            for (var i in this.estructura) {
                for (var k in this.estructura[i]) {
                    lon += this.estructura[i][k].length;
                }
            }
        }
        return lon;
    }

    this.log_scorm = new logger();
    this.log_elems = new logger();
    this.api = get_api(this);

    /***
     * Devuelve la cadena de log en orden inverso;
     */
    Object.defineProperty(this, 'log', {
        get: get_log
    });
    /***
     * Devuelve el último error del API scorm;
     */
    Object.defineProperty(this, 'error', {
        get: get_error
    });
    Object.defineProperty(this, 'puntaje', {
        get: get_puntaje,
        set: set_puntaje
    });
    Object.defineProperty(this, 'cadena', {
        get: get_cadena,
        set: set_cadena
    });
    Object.defineProperty(this, 'length', {
        get: get_length
    });
}

_sco = new base_sco(); //instancia la función como una clase
cargar_structura_scorm(); //Carga la estructura SCORM
scormLogEvent = null;