Mapeate.Mapas.Selector = function(mapeate, entradas){

    const self = this;
    let iniciado = false, 
        hilos_de_proceso = null;

    const constructor = () => {

        mapeate.print("info", "mapeate_mapas_selector_construyendose");

        mapeate.print("ok", "mapeate_mapas_selector_construido");
    
    };

    this.iniciar = callback => {

        const terminar = estado => typeof callback == "function" && callback(estado);

        mapeate.print("info", "mapeate_mapas_selector_iniciando");

        if(iniciado){
            mapeate.print("warn", "mapeate_mapas_selector_ya_iniciado");
            terminar(false);
            return false;
        };
        iniciado = true;

        hilos_de_proceso = mapeate.anadir_hilo_de_proceso(autorecalcular);

        mapeate.print("ok", "mapeate_mapas_selector_iniciado");

        terminar(true);

        return true;
    };

    const autorecalcular = () => {

        mapeate.si_mismo.querySelectorAll("[data-recalculado=false]").forEach(elemento => {
            self.recalcular_descripcion(elemento.querySelector("[type=checkbox]"), null);
            elemento.setAttribute("data-recalculado", true);
        });
    };

    const crear_niveles = mapa => {

        let html = ``;

        mapa.elementos.forEach((bloque, i) => (
            bloque && 
            bloque.length && 
            (html += mapeate.string_variables(mapeate.vistas.coger("elemento_seleccion_mapa_nivel", {
                i : i, 
                hash : mapeate.hash(), 
                elementos : bloque.length, 
                seleccionado : i ? "" : " checked", 
                seleccionado_bool : !i
            })))
        ));

        return html;
    };

    this.crear_lista = () => Object.keys(mapeate.mapas.coger_todos()).map(clave => {

        const mapa = mapeate.mapas.coger(clave);

        return mapeate.vistas.coger("elemento_seleccion_mapa", {
            ...mapa, 
            nombre_objeto : mapeate.nombre_objeto, 
            nombre : clave, 
            nombre_texto : mapeate.i18n(clave), 
            objetivos : mapa.elementos[0].length, 
            niveles : crear_niveles(mapa)
        });
    }).join("");

    this.seleccionar = (elemento, evento) => {

        let clone = evento.target;

        while(clone && clone.hasAttribute && (clone = clone.parentNode) && clone.tagName)
            if(clone.tagName.toLowerCase() == "section")
                return;

        let error = (
            elemento === undefined ? 1 << 0 :
            elemento === null ? 1 << 1 : 
            typeof elemento != "object" ? 1 << 2 : 
            !(elemento.tagName || elemento.nodeName) ? 1 << 3 : 
            !elemento.hasAttribute("data-i18n") ? 1 << 4 : 
        0) << 1;

        if(error)
            return;

        mapeate.mapas.construir(elemento.getAttribute("data-i18n"), [...elemento.querySelectorAll(".niveles [data-seleccionado=true]")].map(nivel => Number(nivel.getAttribute("data-nivel"))));
        
    };

    this.recalcular_descripcion = (elemento, evento) => {

        const caja = mapeate.coger_padre(elemento, "section"), 
              elementos_seleccionados = [...caja.querySelectorAll("[type=checkbox]")].filter(elemento => elemento.checked), 
              seleccion = mapeate.coger_padre(caja, "li")

        if(!elementos_seleccionados.length){
            elemento.checked = true;
            if(seleccion.getAttribute("data-recalculado") == "true")
                return;
        };

        const mapa = mapeate.mapas.coger(seleccion.getAttribute("data-i18n")), 
              niveles = elementos_seleccionados.map(elemento => Number(elemento.value)), 
              puntos_maximos_dificultad = [30, 20, 10, 5].reduce((a, valor, i) => a + (valor * (i + 1))), 
              puntos_seleccionados = mapa.elementos.reduce((a, bloque, i) =>  a + (niveles.includes(i) ? bloque.length * (i + 1) : 0), 0);

        mapeate.coger_padre(elemento, "label").setAttribute("data-seleccionado", elemento.checked);
        seleccion.querySelector("[data-i18n=objetivos]>.valor").innerText = mapa.elementos.reduce((a, bloque, i) =>  a + (niveles.includes(i) ? bloque.length : 0), 0);
        seleccion.querySelector("[data-i18n=dificultad]>.valor").innerText = Math.round(
            10 * (puntos_seleccionados < puntos_maximos_dificultad ? puntos_seleccionados : puntos_maximos_dificultad) / puntos_maximos_dificultad
        );

    };
 
    constructor();

};