diff --git a/src/libs/routes/classroomCount.js b/src/libs/routes/classroomCount.js
index d78f850c7ea175bec88340e11584525d77625c3d..d0335094fff28b915e07c99a214896bef5cfa108 100644
--- a/src/libs/routes/classroomCount.js
+++ b/src/libs/routes/classroomCount.js
@@ -127,16 +127,6 @@ rqf.addField({
         type: 'integer',
         field: 'ano_censo'
     }
-}).addValue({
-    name: 'period',
-    table: '@',
-    tableField: 'turma_turno_id',
-    resultField: 'period_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'turma_turno_id'
-    }
 }).addValue({
     name: 'school_year',
     table: '@',
@@ -158,22 +148,34 @@ rqf.addField({
         field: 'localizacao_id'
     }
 }).addValue({
-    name: 'education_level_short',
+    name: 'period',
     table: '@',
-    tableField: 'etapa_resumida',
-    resultField: 'education_level_short_id',
+    tableField: 'turma_turno_id',
+    resultField: 'period_id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'etapa_resumida'
+        field: 'turma_turno_id'
     }
 });
 
 classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
+    let classSize = JSON.parse(req.body.class_size) || null;
+    let integralTime = JSON.parse(req.body.integral_time) || null;
+
+    console.log(classSize, integralTime);
+
+    if(classSize == null || integralTime == null) {
+        res.statusCode = 400;
+        return res.json({err: {message: "There was an error processing class_size or integral_time. Check your JSON sintax and be sure you're sending both paramenters."}});
+    }
+    req.classSize = classSize;
+    req.integralTime = integralTime;
+
     req.dims.state = true;
     req.dims.city = true;
     req.dims.period = true;
-    req.dims.education_level_short = true;
+    req.dims.school_year = true;
     req.dims.location = true;
 
     req.sql.field('COUNT(*)', 'total')
@@ -187,6 +189,28 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     next();
 }, rqf.build(), query, id2str.transform(), (req, res, next) => {
     req.enrollment = req.result;
+
+    // Gera a relação etapa de ensino X ano escolar
+    req.educationSchoolYear = {};
+    for(let i = 10; i < 80; ++i) {
+        if(id2str.schoolYear(i) !== id2str.schoolYear(99)) {
+            let educationLevelId = Math.floor(i/10);
+
+            let classSize = req.classSize.find((el) => {return el.id === educationLevelId});
+            let integralTime = req.integralTime.find((el) => {return el.id === educationLevelId});
+
+            let numberStudentClass = (typeof classSize !== 'undefined') ? classSize.numberStudentClass : null;
+            let offerGoal = (typeof integralTime !== 'undefined') ? integralTime.offerGoal : null;
+
+            req.educationSchoolYear[i] = {
+                id: educationLevelId,
+                name: id2str.educationLevelShort(educationLevelId),
+                numberStudentClass,
+                offerGoal
+            };
+        }
+    }
+
     req.resetSql();
     next();
 }, rqf.parse(), (req, res, next) => {
@@ -205,128 +229,173 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
 
     next();
 }, rqf.build(), query, id2str.transform(), (req, res, next) => {
-    let classSize = JSON.parse(req.body.class_size) || null;
-    let integralTime = JSON.parse(req.body.integral_time) || null;
-
-    console.log(classSize, integralTime);
-
-    if(classSize == null || integralTime == null) {
-        res.statusCode = 400;
-        return res.json({err: {message: "There was an error processing class_size or integral_time. Check your JSON sintax and be sure you're sending both paramenters."}});
-    }
 
+    
     req.classroom = req.result;
     let classroom = [];
+    
+    // req.result = [{classroom: req.classroom, enrollment: req.enrollment}]; return next();
 
-    req.classroom.forEach((c) => {
-        let cityObjects = req.classroom.filter((value) => {
-            return (c.city_id == value.city_id && c.year == value.year);
-        });
-
+    // Cria estrutura de resposta requisitada:
+    let i = 0;
+    let j = 0;
+    let result = [];
+    let hashSet = new Set();
+    while (i < req.classroom.length) {
+        let classroom = req.classroom[i];
+        // Cria hash única para cada cidade, dado um ano
+        let hash = '' + classroom.year + classroom.city_id;
+        // Estrutura do objeto do resultado final
         let obj = {
-            state_id: c.state_id,
-            state_name: c.state_name,
-            city_id: c.city_id,
-            city_name: c.city_name,
-            year: c.year,
+            year: classroom.year,
+            name: classroom.name,
+            state_id: classroom.state_id,
+            state_name: classroom.state_name,
+            city_id: classroom.city_id,
+            city_name: classroom.city_name,
             locations: []
         };
 
-        cityObjects.forEach((co) => {
-            obj.locations.push({
-                location_id: co.location_id,
-                location_name: co.location_name,
-                total_classroom: co.total
-            });
-        });
-
-        if(classroom.findIndex((el) => {return (el.city_id === obj.city_id && el.year === obj.year)}) === -1) {
-            classroom.push(obj);
+        let currentClassroomObj = null;
+        if( !hashSet.has(hash) ) {
+            hashSet.add(hash);
+            result.push(obj);
+            currentClassroomObj = obj;
+        } else { // Se a hash já existe, já temos a cidade nos resultados. Como está ordenado, é o último valor nos resultados
+            currentClassroomObj = result[result.length - 1];
         }
-    });
 
-    let result = [];
-    // Para cada resultado
-    classroom.forEach((c) => {
-        let r = c;
-        // Para cada localização
-        c.locations.forEach((location, index) => {
-            function filterEnrollment(e) {
-                return (e.city_id == c.city_id && e.year == c.year && e.location_id == location.location_id);
+        // Inserimos a localidade no array de locations da sala
+        let location = {
+            location_id: classroom.location_id,
+            location_name: classroom.location_name,
+            total_classroom: parseInt(classroom.total, 10),
+            total_classroom_be_built: 0,
+            education_level: []
+        };
+        currentClassroomObj.locations.push(location);
+
+        // Partimos para as etapas de ensino/anos escolares
+
+        let enrollmentMatch = true;
+        j = 0;
+        let educationLevelSet = new Set();
+        while(enrollmentMatch && j < req.enrollment.length) {
+            let enrollment = req.enrollment[j];
+            if(typeof enrollment === 'undefined') {
+                enrollmentMatch = false;
+                continue;
+            }
+            if(enrollment.city_id != classroom.city_id) { // Se as cidades não são iguais, já passamos do range
+                enrollmentMatch = false;
+                continue;
             }
 
-            let fEnrollments = req.enrollment.filter(filterEnrollment);
-            let educationLevels = [];
-            fEnrollments.forEach((fe) => {
-                let obj = {education_level_short_id: fe.education_level_short_id, education_level_short_name: fe.education_level_short_name};
+            if(enrollment.year != classroom.year || enrollment.location_id != classroom.location_id) { // Se ano ou localização são diferentes, passa para o próximo
+                ++j;
+                continue;
+            }
 
-                if(educationLevels.findIndex((el) => {return el.education_level_short_id === obj.education_level_short_id}) === -1 ) {
-                    educationLevels.push(obj);
-                }
-            });
+            // Remove se o período é nulo (não dá pra usar no cálculo)
+            if(enrollment.period_id == null) {
+                req.enrollment.splice(j, 1);
+                continue;
+            }
 
-            let education_level = [];
-            // Para cada etapa de ensino
-            educationLevels.forEach((eduLevel) => {
-                function filterEducationLevel(e) {
-                    return (e.city_id == c.city_id && e.year == c.year && e.location_id == location.location_id && e.education_level_short_id == eduLevel.education_level_short_id);
+            // Temos uma matrícula com cidade, ano e localidades certos
+            // "Consome" a matrícula (remove do vetor de matrículas)
+            req.enrollment.splice(j, 1);
+
+            // Cria a etapa de ensino adequada
+            let enrollmentEducationLevel = req.educationSchoolYear[enrollment.school_year_id];
+            // Se não há um número de alunos por turna para a etapa de ensino, ignoramos a entrada
+            if(enrollmentEducationLevel.numberStudentClass == null) continue;
+            
+            let educationLevel = null;
+            if(!educationLevelSet.has(enrollmentEducationLevel.id)) {
+                educationLevelSet.add(enrollmentEducationLevel.id);
+
+                educationLevel = {
+                    education_level_short_id: enrollmentEducationLevel.id,
+                    education_level_short_name: enrollmentEducationLevel.name,
+                    enrollment: {
+                        total_enrollment_day: 0,
+                        total_enrollment_night: 0,
+                        full_period_classes: 0,
+                        day_classes: 0,
+                        night_classes: 0,
+                        total_classrooms_needed: 0
+                    }
+                };
+                
+                // location.education_level.push(educationLevel);
+                // Para manter a orde da etapa de ensino
+                if (location.education_level.length == 0) {
+                    location.education_level.push(educationLevel);
+                } else {
+                    let k = location.education_level.length - 1;
+                    let el = location.education_level[k];
+                    while (k >= 0) {
+                        if(educationLevel.education_level_short_id < el.education_level_short_id) {
+                            --k;
+                            if(k>=0) el = location.education_level[k];
+                        } else break;
+                    }
+                    k++;
+                    location.education_level.splice(k, 0, educationLevel);
                 }
+            } else {
+                let k = 0;
+                let el = location.education_level[k];
+                while(k < location.education_level.length) {
+                    if(el.education_level_short_id != enrollmentEducationLevel.id) {
+                        ++k;
+                        if(k<location.education_level.length) el = location.education_level[k];
+                    } else break;
+                }
+                if(k >= location.education_level.length) --k;
+                educationLevel = location.education_level[k];
+            }
 
-                let fEducationLevel = fEnrollments.filter(filterEducationLevel);
-
-                let dayEnrollments = 0;
-                let nightEnrollments = 0;
-                // Para cada matrícula na etapa de ensino
-                fEducationLevel.forEach((fEduLevel) => {
-                    if(fEduLevel.period_id < 3) dayEnrollments+= fEduLevel.total;
-                    else if(fEduLevel.period_id === 3) nightEnrollments+= fEduLevel.total;
-                });
-
-                let size = classSize.find((el) => {
-                    return el.id === eduLevel.education_level_short_id;
-                }) || undefined;
-                let fullPeriodTime = integralTime.find((el) => {
-                    return el.id === eduLevel.education_level_short_id;
-                }) || undefined;
-
-                if(typeof size === 'undefined' || typeof fullPeriodTime === 'undefined') return;
-                size = size.numberStudentClass;
-                fullPeriodTime = fullPeriodTime.offerGoal;
-
-                let fullPeriodClasses = Math.ceil((dayEnrollments * (fullPeriodTime / 100))/size);
-                let dayClasses = Math.ceil((dayEnrollments/size) - fullPeriodClasses);
-                let nightClasses = Math.ceil(nightEnrollments/size);
-                let totalClassroomsNeeded = fullPeriodClasses + dayClasses;
-
-                if(nightClasses > dayClasses) totalClassroomsNeeded+= (nightClasses - dayClasses);
-
-                let obj = {
-                    total_enrollment_day : dayEnrollments,
-                    total_enrollment_night: nightEnrollments,
-                    full_period_classes: fullPeriodClasses,
-                    day_classes: dayClasses,
-                    night_classes: nightClasses,
-                    total_classrooms_needed: totalClassroomsNeeded
-                };
+            // Soma os totais de matrícula da etapa de ensino
+            educationLevel.enrollment.total_enrollment_day += (enrollment.period_id < 3 && enrollment.period_id != null) ? enrollment.total : 0;
+            educationLevel.enrollment.total_enrollment_night += (enrollment.period_id == 3) ? enrollment.total : 0;
 
-                let educationLevelIndex = education_level.findIndex((el) => {return el.education_level_short_id === eduLevel.education_level_short_id});
+            // Calcula o número de turmas parcial
+            // Turmas de período integral
+            educationLevel.enrollment.full_period_classes = Math.ceil((educationLevel.enrollment.total_enrollment_day * (enrollmentEducationLevel.offerGoal/100)) / enrollmentEducationLevel.numberStudentClass);
 
-                if(educationLevelIndex === -1) {
-                    educationLevelIndex = education_level.length;
-                    education_level.push(eduLevel);
-                }
+            // Turmas diurnas
+            educationLevel.enrollment.day_classes = Math.ceil((educationLevel.enrollment.total_enrollment_day / enrollmentEducationLevel.numberStudentClass) - educationLevel.enrollment.full_period_classes);
 
-                education_level[educationLevelIndex].enrollment = obj;
-                // education_level[educationLevelIndex].enrollments = fEducationLevel;
-            });
+            // Turmas noturnas
+            educationLevel.enrollment.night_classes = Math.ceil((educationLevel.enrollment.total_enrollment_night / enrollmentEducationLevel.numberStudentClass));
 
-            r.locations[index].education_level = education_level;
-        });
+            // Total de salas
+            educationLevel.enrollment.total_classrooms_needed = (educationLevel.enrollment.full_period_classes + educationLevel.enrollment.day_classes);
+
+            if(educationLevel.enrollment.night_classes > educationLevel.enrollment.day_classes) educationLevel.enrollment.total_classrooms_needed += (educationLevel.enrollment.night_classes - educationLevel.enrollment.day_classes);
+
+        }
+
+        // Calculamos o total classroom be built para o município usando reduce
+        location.total_classroom_be_built = location.education_level.reduce((total, atual) => {
+            return total + atual.enrollment.total_classrooms_needed;
+        }, 0) - location.total_classroom;
+
+        if(location.total_classroom_be_built < 0) location.total_classroom_be_built = 0;
+
+        ++i;
+    }
+    // TODO: agregar por estado e brasil
+    if(req.query['state']) {
+        console.log('ESTADO!!!!');
+    } else if(!req.query['city']) {
+        console.log('BRASIL!!!');
+    }
 
-        result.push(r);
-    });
-    console.log('FEITO');
     req.result = result;
+
     next();
 }, response('classroom_count'));