(function () {
    'use strict';

    angular
        .module('editorRotas')
        .controller('indexController', indexController);

    indexController.$inject = ['$scope', 'FuncionalidadesMapa', 'ferramentaExibirClassificacao', 'ListaFiltro', 'filtroService', 'gridService', 'pacmansService',
        'setoresService', 'rotasService', '__env', 'FormValidation', 'ngNotify', 'ferramentaInicioFimColeta', 'configuracoesGerais', '$timeout', 'toaster', 'utils', '$q'];

    function indexController($scope, FuncionalidadesMapa, ferramentaExibirClassificacao, ListaFiltro, filtroService, gridService, pacmansService,
        setoresService, rotasService, __env, FormValidation, ngNotify, ferramentaInicioFimColeta, configuracoesGerais, $timeout, toaster, utils, $q) {

        let vm = this;

        angular.extend(vm, {
            abrirModalCriarRota: abrirModalCriarRota,
            fecharModalCriarRota: fecharModalCriarRota,
            abrirModalCriarRotaManualmente: abrirModalCriarRotaManualmente,
            fecharModalCriarRotaManualmente: fecharModalCriarRotaManualmente,
            abrirModalImportarShapeFile: abrirModalImportarShapeFile,
            fecharModalImportarShapeFile: fecharModalImportarShapeFile,
            abrirModalAlterarAtivacaoRota: abrirModalAlterarAtivacaoRota,
            fecharModalAlterarAtivacaoRota: fecharModalAlterarAtivacaoRota,
            abrirModalExcluirRota: abrirModalExcluirRota,
            fecharModalExcluirRota: fecharModalExcluirRota,
            retornarCriacaoRota: retornarCriacaoRota,
            criarRota: criarRota,
            alterarAtivacaoRota: alterarAtivacaoRota,
            excluirRota: excluirRota,
            abrirDetalhes: abrirDetalhes,
            consultaSetor: consultaSetor,
            resetFiltro: resetFiltro,
            filtrarGrid: filtrarGrid,
            consultaGrid: consultaGrid,
            TratarEvento: TratarEvento,
            onChangeRotaTipoCodigo: onChangeRotaTipoCodigo
        });

        // ----------------- Variáveis do escopo
        vm.configuracoesGerais = configuracoesGerais.data;
        vm.permissions = ['camadaVisualizacao', 'filtro'];
        vm.Etapa = "Index";
        vm.appPath = __env.appPath;
        vm.hasFieldRequiredFilter = false;
        vm.usarGeoGoogle = false;
        vm.colunaOrdenada = "Setor";
        vm.takeGrid = 0;
        vm.skipGrid = 0;
        vm.objTakeSkipGrid = { Take: vm.takeGrid, Skip: vm.skipGrid };
        vm.resultGridPendente = [];
        vm.chave = null;
        // ----------------- Variáveis do escopo

        // ----------------- Variáveis locais
        let _doubleClickGrid = false;
        let _skipOriginal = 0;
        let _takeOriginal = 0;
        // ----------------- Variáveis locais

        //------------- Chamada ao inicializar a controller
        vm.objTakeSkipGrid = {
            Take: vm.takeGrid,
            Skip: vm.skipGrid,
            ColOrdenada: vm.colunaOrdenada,
            RevertSort: vm.revertSort
        };

        ferramentaExibirClassificacao.SetScope($scope);

        vm.SignalR = new PersistentConnectionSR({
            mapeamento: urlGlobal + 'TunelEditorRotas',
            onConnected: function () {
                console.log('SignalR Conectado');
            }
        });

        vm.SignalR.SetHandlers(vm);

        function TratarEvento(evento, mensagem) {
            console.log(evento);
            console.log(mensagem);

            var listaGrid = Object.assign([], vm.dataGrid);
            var listaGridPendente = Object.assign([], vm.resultGridPendente);

            listaGrid.map((item) => {
                if (evento.Objeto !== undefined)
                    if (item.CodigoRota === evento.Objeto.CodigoRota)
                        for (var key in item)
                            item[key] = evento.Objeto[key];
            });

            listaGridPendente.map((item) => {
                if (evento.Objeto !== undefined)
                    if (item.CodigoRota === evento.Objeto.CodigoRota)
                        for (var key in item)
                            item[key] = evento.Objeto[key];
            });

            vm.dataGrid = listaGrid;
            vm.resultGridPendente = listaGridPendente;

            if (!$scope.$$phase)
                $scope.$apply();

            if (evento.Tipo == 'ImportarShapeFile') {
                toaster.pop('error', 'Editor de Rotas', mensagem, 0, 'trustedHtml', function (toasterInstance) {
                    if (event.target.classList.contains("toast-close-button")) {
                        toaster.clear(toasterInstance);
                        return;
                    }
                    return true;
                });
            }
            if (evento.Tipo == 'ImportadoShapeFile') {
                if (evento.Objeto !== undefined) {
                    let urlRota = `${env.baseUrl}${evento.ParametrosQueryString}`;
                    toaster.pop('success', 'Editor de Rotas', `A rota <strong>${evento.Objeto.Descricao}</strong> foi criada com sucesso`, 0, 'trustedHtml', function (toasterInstance) {
                        if (event.target.classList.contains("toast-close-button")) {
                            toaster.clear(toasterInstance);
                            return;
                        }
                        window.open(urlRota);
                        return true;
                    });
                    atualizarDadosGrid();
                }
            }
        }

        resetFiltro();

        //------------- Chamada ao inicializar a controller

        /**
         * Redireciona a para o detalhes de alguma rota
         */
        function abrirDetalhes(setor) {
            window.open(urlGlobal + "EditorRotas/Etapa" + setor.Etapa + "?CodigoSetor=" + setor.CodigoSetor + "&CodigoRota=" + setor.CodigoRota + "&rt=" + setor.RotasTemp);
        }

        /**
         * Evento de click da grid, carrega as informações do setor selecionado
         * @param {*} setor
         * @param {*} doubleClick //se doubleClick = true será realizado o fitBounds
         */
        function consultaSetor(setor, doubleClick) {
            _doubleClickGrid = doubleClick;
            vm.Setor = setor;

            setoresService.CarregarSetorArea(setor.CodigoSetor, setor.CodigoRota, setor.RotasTemp)
                .then(successCarregarSetor)
                .catch(errorRequest);
        }

        /**
         * Após carregado o setor, a rota do setor é carregada
         * @param {*} result
         */
        function successCarregarSetor(result) {
            if (result.data) {
                FuncionalidadesMapa.AreaCordenadas($scope.Mapa.map, result.data);
                vm.chave = vm.Setor.chave;

            }

            rotasService.CarregarRota({ CodigoRota: vm.Setor.CodigoRota, RotasTemp: vm.Setor.RotasTemp })
                .then(successCarregarRota)
                .catch(errorRequest);
        }

        /**
         * Plota as informações da rota selecionada e carrega as outras rotas do setor
         * @param {*} result
         */
        function successCarregarRota(result) {
            FuncionalidadesMapa.RemoverPacmansPlotados();
            FuncionalidadesMapa.LimparTodosMarkers();
            FuncionalidadesMapa.RemoverInicioFim();
            FuncionalidadesMapa.RemoverTodasRotasPlotadas();
            ferramentaExibirClassificacao.RemoverTodas();

            if (result.data) {
                let rotaPlotada = FuncionalidadesMapa.PlotarRota(result.data, vm.Setor.CodigoRota, false, null, true, vm.Setor.Etapa, _doubleClickGrid);
                rotaPlotada.Selecionado = true;
                FuncionalidadesMapa.PlotarIniciofim(vm.Setor.CodigoRota, ferramentaInicioFimColeta, vm.permissions);

                if (vm.Setor.Etapa >= 2) ferramentaExibirClassificacao.SelecaoPolylineClassificada(FuncionalidadesMapa.GetRota(vm.Setor.CodigoRota), true);

                rotasService
                    .CarregarRotasSetor(vm.Setor.CodigoSetor)
                    .then(successCarregarRotasSetor)
                    .catch(errorRequest);
            }
        }

        /**
         * Atualiza as informações das outras rotas do setor
         * @param {*} result
         */
        function successCarregarRotasSetor(result) {
            result.data.forEach(rota => {
                if (rota.Status != 4) {
                    if (rota.Codigo != vm.Setor.CodigoRota) {
                        let rotaPlotada = FuncionalidadesMapa.PlotarRota(null, rota.Codigo, false, null, false, vm.Setor.Etapa, _doubleClickGrid);
                        rotaPlotada.Selecionado = false;
                        rotaPlotada.Descricao = rota.Descricao;
                        rotaPlotada.Apelido = rota.Apelido;
                        rotaPlotada.RotasTemp = rota.RotasTemp;
                        rotaPlotada.Etapa = rota.Etapa;
                    }
                }
            });

            pacmansService
                .CarregarPacmans({ CodigoRota: vm.Setor.CodigoRota, RotasTemp: vm.Setor.RotasTemp })
                .then(successCarregarPacmans)
                .catch(errorRequest);
        }

        /**
         * Plota os pacmans carregados e carrega as configurações de visualização
         * @param {*} result
         */
        function successCarregarPacmans(result) {
            if (result.data && result.data != "null" && vm.Setor.Etapa == 3) FuncionalidadesMapa.PlotarPacman(result.data, vm.Setor.CodigoRota);
            vm.CarregarConfiguracoesVisualizacao(vm.Etapa);
        }

        // --------------- Criação da Rota
        /**
         * Abre modal para seleção do método de criação da rota
         */
        function abrirModalCriarRota() {
            if (event) {
                event.stopPropagation();
            }
            vm.shapeFiles = [];

            vm.openModal('Criar rota', __env.appPath + 'scripts/Index/modais/criar-rota-partial.html', fecharModalCriarRota)
                .then(() => {
                    vm.validationFormCriarRota = new FormValidation(vm.formCriarRota);
                    vm.validationFormCriarRota.setControlsValidation(vm.checkShapeFile);

                    vm.modalOpen = true;
                })
        }

        /**
         * Fecha a modal de criação de uma rota
         */
        async function fecharModalCriarRota() {
            vm.modalOpen = false;
            return await vm.closeModal()
                .then(() => {
                    vm.validationFormCriarRota.resetControlsValidation();
                    vm.novaRota = {};
                    vm.Setor = null;
                })
        }

        /**
         * Abre a modal de criação de uma rota manualmente
         */
        function abrirModalCriarRotaManualmente() {
            fecharModalCriarRota().then(() => {
                vm.novaRota = { RotasTemp: true };
                if (vm.Setor) vm.novaRota.CodigoSetor = vm.Setor.CodigoSetor;

                vm.showLoading();
                setoresService.CarregarSetores()
                    .then(result => {
                        vm.listaSetores = result.data;
                        $timeout(() => {
                            vm.openModal('Nova Rota', __env.appPath + 'scripts/Index/modais/criar-rota-manualmente.partial.html', fecharModalCriarRotaManualmente)
                                .then(() => {
                                    vm.hideLoading();
                                    vm.validationFormCriarRotaManualmente = new FormValidation(vm.formCriarRotaManualmente);
                                    vm.validationFormCriarRotaManualmente.setControlsValidation();
                                    vm.modalOpen = true;
                                });
                        }, 400);
                    })
                    .catch(errorRequest);
            });
        }

        /**
         * Fecha a modal de criação de uma rota manualmente
         */
        function fecharModalCriarRotaManualmente() {
            vm.modalOpen = false;
            vm.closeModal()
                .then(() => {
                    vm.validationFormCriarRotaManualmente.resetControlsValidation();
                    vm.novaRota = {};
                    vm.Setor = null;
                })
        }

        /**
         * Abre a modal de importação de shapeFile
         */
        function abrirModalImportarShapeFile(shapeFiles) {
            fecharModalCriarRota().then(() => {

                lerShapeFile(shapeFiles).then(data => {

                    vm.novaRota = { RotasTemp: true, FromShape: true };

                    setoresService.CarregarSetores()
                        .then(result => {
                            vm.listaSetores = result.data;
                            $timeout(() => {
                                vm.openModal('Importar ShapeFile', __env.appPath + 'scripts/Index/modais/importar-shape-file.partial.html', fecharModalImportarShapeFile)
                                    .then(() => {
                                        vm.validationFormImportarShapeFile = new FormValidation(vm.formImportarShapeFile);
                                        vm.validationFormImportarShapeFile.setControlsValidation(vm.validateSequencia);
                                        vm.modalOpen = true;
                                    })
                            }, 400);
                        })
                        .catch(errorRequest);
                });
            });
        }

        /**
         * Fecha a modal de importação de shapeFile
         */
        async function fecharModalImportarShapeFile() {
            vm.modalOpen = false;
            return await vm.closeModal()
                .then(() => {
                    vm.validationFormImportarShapeFile.resetControlsValidation();
                    vm.novaRota = {};
                    vm.Setor = null;
                })
        }

        /**
         * Retorna para modal de criação de rota
         */
        function retornarCriacaoRota() {
            fecharModalImportarShapeFile().then(() => {
                $timeout(abrirModalCriarRota, 400)
            });
        }
        function distinct(value, index, self) {
            return self.indexOf(value) === index;
        }

        /**
         * Realiza extração dos dados dos arquivos shp e dbf
         */
        function lerShapeFile(files) {

            const getExtension = (file) => file.name.toLowerCase().substr(file.name.lastIndexOf('.'), file.name.length);

            const shpFile = files.find(file => getExtension(file) === '.shp');
            const dbfFile = files.find(file => getExtension(file) === '.dbf');

            let deferred = $q.defer();

            new Shapefile({
                shp: shpFile,
                dbf: dbfFile,
            }, (data) => {
                var filterFunction = (value, index, self) => distinct(value, index, self) && value;
                vm.dadosArquivo = data;
                vm.dbfValores = new Map(data.dbf.fields.map(x => [x.name, data.dbf.records.map(z => z[x.name].trim()).filter(filterFunction)]));

                deferred.resolve(data);
            });

            return deferred.promise;
        }

        /**
         * Previne que os valores dos campos de classificação de trecho se repitam
         */
        function onChangeRotaTipoCodigo(property) {
            const attrs = ['CodigoColeta', 'CodigoColetaManual', 'CodigoDeslocamento'];
            attrs.filter(x => x != property).forEach(attr => {
                if (vm.novaRota[attr] === vm.novaRota[property]) {
                    vm.novaRota[attr] = null;
                }
            });
        }

        /**
        * Extrai o tipo do treho
        * @param {object} record
        */
        function getTipo(record) {
            if (!vm.novaRota.Tipo)
                return null;

            const value = record[vm.novaRota.Tipo];

            switch (value) {
                case vm.novaRota.CodigoColeta:
                    return 'c';
                case vm.novaRota.CodigoColetaManual:
                    return 'm';
                case vm.novaRota.CodigoDeslocamento:
                    return 'd';
                default:
                    return null;
            }
        }

        /**
        * Extrai ponos e trechos dos dados do shapefile
        * @param {object} areaSetor
        */
        function getTrechos(areasSetor) {
            let trechos = vm.dadosArquivo.geojson.features.sort((record, record2) =>
                parseInt(record.properties[vm.novaRota.Sequencia]) - parseInt(record2.properties[vm.novaRota.Sequencia]));

            let pontosTotais = getTodosPontos(trechos, areasSetor);
            let pontosUnicos = getPontosUnicos(pontosTotais
                .sort((a, b) => a.Trecho - b.Trecho || a.SequenciaPonto - b.SequenciaPonto));

            atualizaInstrucaoDistanciaESequencia(pontosUnicos);
            trechos = trechos.map(record => {
                const sequencia = parseInt(record.properties[vm.novaRota.Sequencia]);
                return {
                    Sequencia: sequencia,
                    Tipo: getTipo(record.properties),
                    Pontos: pontosUnicos.filter(x => x.Trecho === sequencia).sort((x, z) => x.SequenciaPonto - z.SequenciaPonto)
                }
            });

            return trechos;
        }

        function getTodosPontos(trechos, areasSetor) {
            const setorCoordenadas = areasSetor.flatMap(x => x.Coordenadas).map(item => {
                item.Latitude = item.Latitude / 1000000;
                item.Longitude = item.Longitude / 1000000;
                return item;
            });

            return trechos.flatMap(record => {
                const trecho = parseInt(record.properties[vm.novaRota.Sequencia]);
                const isReversePath = vm.novaRota.Sentido && vm.novaRota.SentidoInverso
                    && record.properties[vm.novaRota.Sentido] === vm.novaRota.SentidoInverso;
                const recordCoordinates = isReversePath ? record.geometry.coordinates.clone().reverse() : record.geometry.coordinates;

                return recordCoordinates.map((x, z) => {
                    return {
                        Longitude: x[0] * 1000000,
                        Latitude: x[1] * 1000000,
                        Lat: x[0],
                        Lng: x[1],
                        Trecho: trecho,
                        SequenciaPonto: z,
                        PacmanAtivo: false,
                        PossuiGeoCode: false,
                        DentroSetor: utils.IsInsideArea(setorCoordenadas, { Longitude: x[0], Latitude: x[1] })
                    }
                });
            });
        }

        function getPontosUnicos(pontos) {
            let pontosUnicos = [];

            pontos.forEach((item, index) => {
                let posicaoAnterior = index > 0 ? pontos[index - 1] : null;
                const latAnterior = posicaoAnterior ? posicaoAnterior.Lat : 0;
                const lngAnterior = posicaoAnterior ? posicaoAnterior.Lng : 0;

                if (latAnterior == item.Lat && lngAnterior == item.Lng)
                    return;

                pontosUnicos.push(item);
            });

            return pontosUnicos;
        }

        function atualizaInstrucaoDistanciaESequencia(pontos) {
            pontos.map((x, z) => {
                let posicaoAtual = { lat: x.Lat, lng: x.Lng };
                let posicaoAnterior = z > 0 ? pontos[z - 1] : null;
                let proximaPosicao = z + 1 <= pontos.length ? pontos[z + 1] : null;

                posicaoAnterior = posicaoAnterior ? { lat: posicaoAnterior.Lat, lng: posicaoAnterior.Lng } : null;
                proximaPosicao = proximaPosicao ? { lat: proximaPosicao.Lat, lng: proximaPosicao.Lng } : null;

                x.Instrucao = utils.GetDiretions(posicaoAnterior, posicaoAtual, proximaPosicao);
                x.Distancia = posicaoAnterior ? getDistanceFromLatLong(posicaoAnterior, posicaoAtual) : 0;
                x.Sequencia = z + 1;
                return x;
            });
        }

        function getDistanceFromLatLong(position1, position2) {
            var deg2rad = (deg) => deg * (Math.PI / 180),
                R = 6371,
                dLat = deg2rad(position2.lat - position1.lat),
                dLng = deg2rad(position2.lng - position1.lng),
                a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
                    + Math.cos(deg2rad(position1.lat))
                    * Math.cos(deg2rad(position1.lat))
                    * Math.sin(dLng / 2) * Math.sin(dLng / 2),
                c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
            return (R * c * 1000);
        }

        /**
         * Cria rota através dos dados do Shapefile
         * @param {object} form
         * @param {function} callback
         */
        function criarRotaFromShapeFile(form, callback) {
            return setoresService.CarregarSetorArea(form.codigoSetor.$$element.attr('data-value'))
                .then((result) => {

                    const rota = {
                        Descricao: vm.novaRota.Descricao,
                        Apelido: vm.novaRota.Apelido,
                        CodigoSetor: form.codigoSetor.$$element.attr('data-value'),
                        RotasTemp: true,
                        Trechos: vm.novaRota.Sequencia ? getTrechos(result.data) : null
                    };

                    rotasService.CriarRotaCompleta(rota).then(result => {
                        if (result.data.Sucesso) {
                            vm.setFormStatus(form, 'SEND_SUCCESS').then(() => {
                                if (callback)
                                    callback();
                                ngNotify.set(result.data.MensagemSucesso, { type: 'info', sticky: true });
                            });
                        } else {
                            sendFormError(form, result.data.MensagemErro);
                        }
                    }).catch(error => {
                        sendFormError(form, error.data.MensagemErro);
                    });
                })
                .catch((error) => {
                    errorRequest(error);
                    sendFormError(form, error.data.MensagemErro);
                });
        }

        /**
         * Mostra mensagem de erro no formulário
         * @param {object} form
         * @param {string} message
         */
        function sendFormError(form, message) {
            vm.setFormStatus(form, 'SEND_FAIL').then(() => {
                ngNotify.set(message, {
                    target: '#modalNotificationOuter',
                    type: 'error',
                    sticky: true,
                    button: true
                });
                $timeout(() => {
                    vm.setFormStatus(form, null);
                }, 1000);
            });
        }

        /**
         * Envia a requisição de criação de uma nova rota
         * @param {object} form
         * @param {function} callback
         */
        function criarRota(form, callback) {
            vm.setFormStatus(form, 'SENDING').then(() => {
                if (vm.novaRota.FromShape) {
                    criarRotaFromShapeFile(form, callback);
                } else {
                    criarRotaManual(form, callback);
                }
            });
        }

        /**
        * Cria rota manual
        * @param {object} form
        * @param {function} callback
        */
        function criarRotaManual(form, callback) {
            rotasService.CriarRota(vm.novaRota)
                .then(result => {
                    if (result.data.Sucesso) {
                        vm.setFormStatus(form, 'SEND_SUCCESS').then(() => {
                            if (callback)
                                callback();
                            rotasService.CarregarNovaRota(result.data.Objeto.Codigo)
                                .then(successCarregarNovaRota)
                                .catch(errorRequest);
                        });

                        atualizarDadosGrid();

                    } else {
                        vm.setFormStatus(form, 'SEND_FAIL').then(() => {
                            ngNotify.set(result.data.MensagemErro, {
                                target: '#modalNotificationOuter',
                                type: 'error',
                                sticky: true,
                                button: true
                            });
                            $timeout(() => {
                                vm.setFormStatus(form, null);
                            }, 1000);
                        });
                    }
                })
                .catch(error => {
                    vm.setFormStatus(form, 'SEND_FAIL').then(() => {
                        callback();
                        ngNotify.set(error.statusText, { type: 'error', sticky: true, button: true });
                    });
                });
        }

        /**
         * Abre em nova aba a nova rota criada
         * @param {object} result
         */
        function successCarregarNovaRota(result) {
            if (result.data) {
                vm.resultGrid.push(result.data);
                window.open(urlGlobal + `EditorRotas/Etapa${result.data.Etapa}?CodigoSetor=${result.data.CodigoSetor}&CodigoRota=${result.data.CodigoRota}&rt=${result.data.RotasTemp}`);
            }
        }

        // ---------- Alteração da data de ativação da rota
        /**
         * Abre a modal para alterar ou cancelar a data de ativação da rota
         * @param {*} rota
         */
        function abrirModalAlterarAtivacaoRota(rota) {
            event.stopPropagation();

            vm.rotaAtivacao = {
                codigoRota: rota.CodigoRota,
                rotaTemp: rota.RotasTemp,
                dataAgendamento: rota.DataAtivacaoDescricao
            };

            vm.openModal('Alterar Ativação da Rota', __env.appPath + 'scripts/partials/alterar-ativacao-rota.partial.html', fecharModalAlterarAtivacaoRota)
                .then(() => {
                    vm.validationFormAlterarAtivacaoRota = new FormValidation(vm.formAlterarAtivacaoRota);
                    vm.validationFormAlterarAtivacaoRota.setControlsValidation();
                    vm.modalOpen = true;
                })
        }

        /**
         * Fecha a modal de alterar ou cancelar a data de ativação da rota
         */
        function fecharModalAlterarAtivacaoRota() {
            vm.modalOpen = false;
            vm.closeModal().then(() => {
                vm.validationFormAlterarAtivacaoRota.resetControlsValidation();
                vm.rotaAtivacao = {};
            })
        }

        /**
         * Envia as alterações da ativação da rota para o servidor
         */
        function alterarAtivacaoRota() {
            vm.setFormStatus(vm.formAlterarAtivacaoRota, 'SENDING').then(() => {
                if (vm.rotaAtivacao.cancelarAgendamento) vm.rotaAtivacao.dataAgendamento = null;

                rotasService
                    .AtivarRota(vm.rotaAtivacao)
                    .then(successAlterarAtivacaoRota)
                    .catch(errorAlterarAtivacaoRota);
            });
        }

        /**
         * Se a requisição retornar sucesso é as informações da rota na grid são atualizadas
         * @param {*} result
         */
        function successAlterarAtivacaoRota(result) {
            if (result.data.Sucesso) {
                vm.setFormStatus(vm.formAlterarAtivacaoRota, 'SEND_SUCCESS').then(() => {
                    fecharModalAlterarAtivacaoRota();
                    ngNotify.set(result.data.MensagemSucesso, { type: 'success', sticky: false });

                    let itemGrid = vm.dataGrid.filter(item => item.CodigoRota == vm.rotaAtivacao.codigoRota);
                    let dataAtivacao = moment(vm.rotaAtivacao.dataAgendamento, "DD/MM/YYYY");

                    itemGrid[0].DataAtivacao = dataAtivacao.toISOString();
                    itemGrid[0].DataAtivacaoDescricao = !vm.rotaAtivacao.cancelarAgendamento ? dataAtivacao.format("DD/MM/YYYY") : '---';
                });
            } else {
                vm.setFormStatus(vm.formAlterarAtivacaoRota, 'SEND_FAIL').then(() => {
                    ngNotify.set(result.data.MensagemErro, {
                        target: '#modalNotificationOuter',
                        type: 'error',
                        sticky: true,
                        button: true
                    });
                    $timeout(() => {
                        vm.setFormStatus(vm.formAlterarAtivacaoRota, null);
                    }, 1000);
                });
            }
        }

        /**
         * Se der erro ao realizar a requisição de alterar a data de ativação da rota é apresentada a mensagem de erro e fechada a modal
         * @param {*} error
         */
        function errorAlterarAtivacaoRota(error) {
            vm.setFormStatus(vm.formAlterarAtivacaoRota, 'SEND_FAIL').then(() => {
                fecharModalAlterarAtivacaoRota();
                ngNotify.set(error.statusText, { type: 'error', sticky: true, button: true });
            });
        }

        // ------------- Exclusão da rota
        /**
         * Abre a modal de confirmação para exclusão da rota
         * @param {*} rota
         */
        function abrirModalExcluirRota(rota) {
            event.stopPropagation();

            vm.rotaExclusao = {
                codigoSetorRota: rota.CodigoRota,
                rotasTemp: rota.RotasTemp
            };

            vm.openModal('Confirmar Exclusão da Rota', __env.appPath + 'scripts/partials/confirmar-exclusao.partial.html', fecharModalExcluirRota)
                .then(vm.modalOpen = true);
        }

        /**
         * Fecha a modal de confirmação para a exclusão da rota
         */
        function fecharModalExcluirRota() {
            vm.modalOpen = false;
            vm.closeModal().then(vm.rotaExclusao = {})
        }

        /**
         * Envia para o servidor a requisição para exclusão da rota
         */
        function excluirRota() {
            vm.setFormStatus(vm.formExcluirRota, 'SENDING').then(() => {
                rotasService.ExcluirRota(vm.rotaExclusao)
                    .then(successExcluirRota)
                    .catch(errorExcluirRota);
            });
        }

        /**
         * Limpa a visualização exibida no mapa e recarrega as informações da grid para não apresentar a rota excluida
         * @param {*} result
         */
        function successExcluirRota(result) {
            if (result.data.Sucesso) {
                vm.setFormStatus(vm.formExcluirRota, 'SEND_SUCCESS').then(() => {
                    fecharModalExcluirRota();
                    ngNotify.set(result.data.MensagemSucesso, { type: 'success', sticky: false });
                });

                let del = vm.dataGrid.arrayFirst(item => ((item.CodigoRota == vm.rotaExclusao.codigoSetorRota) && (item.RotasTemp == vm.rotaExclusao.rotasTemp)));
                vm.dataGrid.remove(del);

                FuncionalidadesMapa.RemoverPacmansPlotados();
                FuncionalidadesMapa.LimparTodosMarkers();
                FuncionalidadesMapa.RemoverInicioFim();
                FuncionalidadesMapa.RemoverTodasRotasPlotadas();
                FuncionalidadesMapa.EsconderSetor();
                ferramentaExibirClassificacao.RemoverTodas();

                _skipOriginal = vm.skipGrid;
                _takeOriginal = vm.takeGrid;

                vm.takeGrid = (_skipOriginal > 0 ? _skipOriginal : vm.configuracoesGerais.PaginacaoGrid) + vm.configuracoesGerais.PaginacaoGrid;

                vm.skipGrid = 0;
                vm.objTakeSkipGrid = {
                    Take: vm.takeGrid,
                    Skip: vm.skipGrid,
                    ColOrdenada: vm.colunaOrdenada,
                    RevertSort: vm.revertSort
                };

                gridService.ConsultaGrid(vm.objTakeSkipGrid)
                    .then(successConsultaGridExcluirRota)
                    .catch(errorRequest);
            } else {
                vm.setFormStatus(vm.formExcluirRota, 'SEND_FAIL').then(() => {
                    ngNotify.set(result.data.MensagemErro, {
                        target: '#modalNotificationOuter',
                        type: 'error',
                        sticky: true,
                        button: true
                    });
                    $timeout(() => {
                        vm.setFormStatus(vm.formExcluirRota, null);
                    }, 1000);
                });
            }
        }

        /**
         * Se der erro ao realizar a requisição de excluir a rota é apresentada a mensagem de erro e fechada a modal
         * @param {*} error
         */
        function errorExcluirRota(error) {
            vm.setFormStatus(vm.formExcluirRota, 'SEND_FAIL').then(() => {
                fecharModalExcluirRota();
                ngNotify.set(error.statusText, { type: 'error', sticky: true, button: true });
            });
        }

        /**
         * Consulta novamente a grid para apresentar a nova rota criada e chama função para abrir a nova rota em outra aba
         */
        function atualizarDadosGrid() {
            vm.dataGrid = [];
            vm.takeGrid = vm.configuracoesGerais.PaginacaoGrid * 2;

            vm.objTakeSkipGrid = {
                Take: vm.takeGrid,
                Skip: vm.skipGrid,
                ColOrdenada: vm.colunaOrdenada,
                RevertSort: vm.revertSort
            };

            $("footer:visible .loading-inlog-modulo").fadeIn();
            gridService.ConsultaGrid(vm.objTakeSkipGrid)
                .then(successConsultarGrid)
                .catch(errorRequest);
        }

        /**
         * Se trouxer algum resultado, atualiza as novas informações da grid
         * @param {*} result
         */
        function successConsultarGrid(result) {
            if (result.data.Grid.Data.length > 0) {
                vm.data = result.data.Grid.Data;
                vm.colunas = result.data.Grid.columns;
                vm.itensFiltro = result.data.Filtro.ItemsFiltro;

                vm.arrayFilter = [];
                vm.currentFilter = [];
                vm.currentPathTemplate = '';

                vm.dataGrid = result.data.Grid.Data.slice(0, vm.takeGrid / 2);
                vm.resultGridPendente = result.data.Grid.Data.slice(vm.takeGrid / 2, vm.takeGrid);
                vm.quantidadeTotal = result.data.QuantidadeTotal;

                $("footer:visible .loading-inlog-modulo").fadeOut();
            }
        }

        /**
         * Atualiza as informações da grid aprensentando a quantidade do take e guardando as proximas para serem apresentadas posteriormente
         * @param {*} result
         */
        function successConsultaGridExcluirRota(result) {
            vm.dataGrid = result.data.Grid.Data.slice(0, (_skipOriginal > 0 ? _skipOriginal : vm.configuracoesGerais.PaginacaoGrid));
            vm.resultGridPendente = result.data.Grid.Data.slice((_skipOriginal > 0 ? _skipOriginal : vm.configuracoesGerais.PaginacaoGrid), vm.takeGrid);

            vm.takeGrid = _takeOriginal;
            vm.skipGrid = (_skipOriginal > 0 ? _skipOriginal : vm.configuracoesGerais.PaginacaoGrid);

        }

        /**
         * Limpa o fitlro e faz uma nova requisição de consulta da grid
         */
        function resetFiltro() {
            $("footer:visible .loading-inlog-modulo").fadeIn();
            vm.listaFiltroAtiva = new ListaFiltro();
            vm.takeGrid = vm.configuracoesGerais.PaginacaoGrid * 2;
            vm.skipGrid = 0;
            $(".scroll-table").animate({ scrollTop: 0 }, "slow");

            let obj = {
                take: vm.takeGrid,
                typeOrder: vm.colunaOrdenada
            };

            filtroService
                .LimparFiltro(obj)
                .then(successLimparFiltro)
                .catch(errorRequest);
        }

        function successLimparFiltro(result) {
            vm.data = result.data.Grid.Data;
            vm.colunas = result.data.Grid.columns;
            vm.listaFiltroInativa = result.data.Filtro.ItensFiltro;

            vm.arrayFilter = [];
            vm.currentFilter = [];
            vm.currentPathTemplate = '';
            vm.dataGrid = vm.data.slice(0, vm.takeGrid / 2);
            vm.resultGridPendente = vm.data.slice(vm.takeGrid / 2, vm.takeGrid);
            vm.quantidadeTotal = result.data.QuantidadeTotal;

            for (const key in vm.listaFiltroInativa) {
                if (vm.listaFiltroInativa.hasOwnProperty(key)) {
                    const element = vm.listaFiltroInativa[key];
                    element.ItensFiltro = result.data.Filtro.ItensFiltro[key].ItensFiltro;
                }
            }

            // Se nao tiver um digest em excecucao, execute o bind das informacoes.
            if (!$scope.$$phase)
                $scope.$apply();

            $("footer:visible .loading-inlog-modulo").fadeOut();
        }

        /**
         * Aplica a pesquisa do filtro e envia a nova requisição de consulta da grid
         * @param {*} $event
         */
        function filtrarGrid($event) {
            vm.hasFieldRequiredFilter = false;
            if (vm.listaFiltroAtiva.itemsFiltro.length === 0) return;

            vm.arrayFilter = [];
            vm.listaFiltroAtiva.itemsFiltro.forEach((item) => {
                if (item.properties.Pesquisa.length == 0 || item.invalid) vm.hasFieldRequiredFilter = true;
                vm.arrayFilter.push(item.properties)
            });

            if (vm.hasFieldRequiredFilter) return;
            $("footer:visible .loading-inlog-modulo").fadeIn();

            let obj = {
                Take: vm.takeGrid,
                TypeOrder: vm.colunaOrdenada,
                ItensFiltro: vm.arrayFilter
            };

            filtroService
                .AplicarFiltro(obj)
                .then(successFiltrarGrid)
                .catch(errorRequest);

            // depois de filtrar, esconde o componente de grid
            $event.stopPropagation();
            let trigger = $event.target;
            $(trigger).closest('.tool').removeClass('open');
        }

        /**
         * Após filtrar a grid atualiza as informações a serem exibidas na grid
         * @param {*} result
         */
        function successFiltrarGrid(result) {
            vm.dataGrid = result.data.Grid.Data.slice(0, vm.takeGrid / 2);
            vm.resultGridPendente = result.data.Grid.Data.slice(vm.takeGrid / 2, vm.takeGrid);
            vm.quantidadeTotal = result.data.QuantidadeTotal;


            for (const key in vm.listaFiltroInativa) {
                if (vm.listaFiltroInativa.hasOwnProperty(key)) {
                    const element = vm.listaFiltroInativa[key];
                    element.ItensFiltro = result.data.Filtro.ItensFiltro[key].ItensFiltro;
                }
            }

            if (vm.listaFiltroAtiva.itemsFiltro.length > 0) {
                vm.listaFiltroAtiva.itemsFiltro.forEach((item) => {
                    item.properties.ItensFiltro = result.data.Filtro.ItensFiltro[item.name].ItensFiltro
                });
            }
            vm.skipGrid = 0;

            // Se nao tiver um digest em excecucao, execute o bind das informacoes.
            if (!$scope.$$phase)
                $scope.$apply();

            $("footer:visible .loading-inlog-modulo").fadeOut();
            $('.grid').floatThead('reflow');
        }

        /**
         * Envia a requisição de consulta da grid
         */
        function consultaGrid() {
            if (vm.skipGrid < vm.quantidadeTotal) {
                vm.objTakeSkipGrid = {
                    Take: vm.takeGrid,
                    Skip: vm.skipGrid,
                    ColOrdenada: vm.colunaOrdenada,
                    RevertSort: vm.revertSort
                };

                gridService
                    .ConsultaGrid(vm.objTakeSkipGrid)
                    .then(successConsultaGridScroll)
                    .catch(errorRequest);
            } else {
                $("footer:visible .loading-inlog-modulo").fadeOut();
            }
        }

        /**
         * Atualiza as informações da grid após realizar o scroll
         * @param {*} result
         */
        function successConsultaGridScroll(result) {
            vm.resultGridPendente = result.data.Grid.Data;
            if (result.data.length === 0) vm.skipGrid = vm.skipGrid - vm.takeGrid;
            $("footer:visible .loading-inlog-modulo").fadeOut();
        }

        /**
         * Função de erro padrão
         * @param {*} error
         */
        function errorRequest(error) {
            if (error.statusText) ngNotify.set(error.statusText, { type: 'error', sticky: true, button: true });
            console.error(error);
        }
    }
})();
