(function () {
    'use strict';

    angular
        .module('editorRotas')
        .factory('ferramentaAjustarTrecho', ferramentaAjustarTrecho);

    ferramentaAjustarTrecho.$inject = ['$location', 'Mapa', 'FuncionalidadesMapa', 'ferramentaApagarTrecho', 'selecionarTrecho', 'ferramentaInicioFimColeta'];

    /**
     * Factory para executar funcoes de ajuste do trecho da polyline seleiconada. Como:
     * Unificacao de trecho simples ou de duas polylines.
     * Segmentacao de um trecho selecionado com base no numero de trecho desejados.
     */
    function ferramentaAjustarTrecho($location, Mapa, FuncionalidadesMapa, ferramentaApagarTrecho, ferramentaSelecionarTrecho, ferramentaInicioFimColeta) {

        /**
         * Declaracao das veriaveis Globais.
         */
        var _queryString = $location.search();
        var _codigoSetor = _queryString ? _queryString.CodigoSetor : 0;
        var _codigoRota = _queryString ? _queryString.CodigoRota : 0;
        var _polylineUnificar = [];
        var _unificarPontos = false;
        var _habilitado = false;
        var _polyline = null;
        var _scope = null;
        //---------

        /**
         * Metodo para unificar trechos.
         */
        function unificarTrecho() {

            // Esconde o iniciofim e a rota.
            FuncionalidadesMapa.EsconderInicioFimPorCodigoRota(_codigoRota);
            FuncionalidadesMapa.EsconderRotaPorCodigo(_codigoRota);

            var polylinesSelecionadasUnificacao = FuncionalidadesMapa.GetPolylinesSelecionadasUnificacao().Copy();
            var arrayPathPolylineSelecionada = FuncionalidadesMapa.GetArrayPathPolylineSelecionada().Copy();
            var arrayPathPolylineHighlight = FuncionalidadesMapa.GetArrayPathPolylineHighlight().Copy();
            var novoPathPolyline = [];

            if (polylinesSelecionadasUnificacao.length === 0 && arrayPathPolylineHighlight.length > 2) {
                // Unificacao simples com base apenas em uma polyline.

                var pontosParaRemover = [];
                var indexInicialSelecionado = FuncionalidadesMapa.GetPolylineSelecionada().IndexInicialSelecionado;

                // Adicionar a 'pontosParaRemover' os pontos que vao ser removidos.
                for (var index = 1; index < arrayPathPolylineHighlight.length - 1; index++) 
                    pontosParaRemover.push(arrayPathPolylineHighlight[index]);

                // Itera para remover os pontos do array da polyline selecionada.
                pontosParaRemover.forEach(point => {
                    var indexPolylineSelecionada = arrayPathPolylineSelecionada.findIndex(item => item.lat() == point.lat() && item.lng() == point.lng());
                    arrayPathPolylineSelecionada.splice(indexPolylineSelecionada, 1);
                });
                
                // Cria as informacoes adicionais para atualizar a polyline.
                var informacoesAdicionaisPath = {
                    IndexPolylineClicada: FuncionalidadesMapa.GetPolylineSelecionada().IndexPolylineClicada,
                    IndexInicialSelecionado: indexInicialSelecionado,
                    IndexFinalSelecionado: indexInicialSelecionado + 1
                };

                // Remove classficiacao do trecho.
                arrayPathPolylineSelecionada[informacoesAdicionaisPath.IndexFinalSelecionado].Tipo = null;

                // Atualiza o path da polyline selecionada e as polylines do apagar trecho.
                FuncionalidadesMapa.AtualizarPathPolylineSelecionada(arrayPathPolylineSelecionada.Copy(), informacoesAdicionaisPath, false);
                ferramentaApagarTrecho.AtualizarPathPolylinesApagarTecho(informacoesAdicionaisPath.IndexPolylineClicada, arrayPathPolylineSelecionada.Copy(), -1, true);

                novoPathPolyline = arrayPathPolylineSelecionada;

            } else if (polylinesSelecionadasUnificacao.length === 2) {
                // Unificacao de duas polylines.

                var pathPolylineInicioUnificacao = polylinesSelecionadasUnificacao[0].getPath().getArray();
                var pathPolylineFinalUnificacao = polylinesSelecionadasUnificacao[1].getPath().getArray();

                // Adiciona no 'pathPolylineInicioUnificacao' os pontos do 'pathPolylineFinalUnificacao'.
                pathPolylineFinalUnificacao.forEach(ponto => pathPolylineInicioUnificacao.push(ponto));

                // Cria as informacoes adicionais para atualizar a polyline.
                var informacoesAdicionaisPath = {
                    IndexPolylineClicada: polylinesSelecionadasUnificacao[0].IndexPolylineClicada,
                    IndexInicialSelecionado: 0,
                    IndexFinalSelecionado: pathPolylineInicioUnificacao.length - 1
                };

                // Limpa as polyline da unificacao, limpa a selecao do mapa e atualiza as polylines do apagar trecho.
                FuncionalidadesMapa.LimpaPolylinesSelecionadasUnificacao();
                FuncionalidadesMapa.LimparSelecionarTrecho();
                ferramentaApagarTrecho.AtualizarPathPolylinesApagarTecho(informacoesAdicionaisPath.IndexPolylineClicada, pathPolylineInicioUnificacao, polylinesSelecionadasUnificacao[1].IndexPolylineClicada, true);

                novoPathPolyline = pathPolylineInicioUnificacao;

            }

            _scope.vm.isValid = false;
            _scope.vm.RotaObservable.notify('enable');

            // Valida se deve mostrar as informacoes da rota principal e o iniciofim.
            validaSeMostrarInformacoesInicioFimERota(novoPathPolyline);
        }

        /**
         * Metodo para segmentacao de trecho, como parametro recebe o numero de segmentos desejados.
         * @param {*} quantidadeSegmentos
         */
        function segmentarTrecho(quantidadeSegmentos) {

            var coordenadaX = 0;
            var projecaoDoMapa = Mapa.map.getProjection();
            var pathPolylinehighlight = ferramentaSelecionarTrecho.GetArrayPathPolylineHighlight();
            var polylineSelecionadaPath = ferramentaSelecionarTrecho.GetArrayPathPolylineSelecionada();
            var polylineSelecionada = ferramentaSelecionarTrecho.GetPolylineSelecionada();

            if (pathPolylinehighlight.length === 2) {
                // Executa a acao se tiver um polyline selecionada.

                // Converte o latlng para o tipo ponto da tela, para obter as coordenadas X e Y.
                var pontoInicial = projecaoDoMapa.fromLatLngToPoint(pathPolylinehighlight[0]);
                var pontoFinal = projecaoDoMapa.fromLatLngToPoint(pathPolylinehighlight[pathPolylinehighlight.length - 1]);

                // Verifica qual o index inicial.
                var indexInicialSelecionado = Mapa.verificarIdx(polylineSelecionada, pathPolylinehighlight[0], false);

                // Calcula qual vai ser o tamanho do espeçamento dos novos trechos.
                var tamanhoEspacamento = (pontoFinal.x - pontoInicial.x) / quantidadeSegmentos;

                // Executa o laco com base no numero de segmentos desejados.
                for (var index = 0; index < quantidadeSegmentos - 1; index++) {

                    // Soma a coordenada X do ponto inical com o 'tamanhoEspacamento'.
                    coordenadaX = pontoInicial.x + tamanhoEspacamento;

                    // Calcula qual sera as coordenadas do novo ponto com base na 'coordenadaX'.
                    pontoInicial = pontoInicial.CalculaPonto(coordenadaX, pontoInicial, pontoFinal);

                    // Converte o ponto para latlng e constroi o objeto.
                    var latlng = Mapa.buildObjectLatLng(projecaoDoMapa.fromPointToLatLng(pontoInicial));

                    // Adicionar o latlng apos o 'indexInicialSelecionado'.
                    polylineSelecionadaPath.splice(indexInicialSelecionado + index + 1, 0, latlng);
                }

                // Cria as informacoes adicionais para atualizar a polyline.
                var informacoesAdicionaisPath = {
                    IndexPolylineClicada: FuncionalidadesMapa.GetPolylineSelecionada().IndexPolylineClicada,
                    IndexInicialSelecionado: indexInicialSelecionado,
                    IndexFinalSelecionado: indexInicialSelecionado + quantidadeSegmentos
                };

                // Remove classficiacao do trecho.
                polylineSelecionadaPath[informacoesAdicionaisPath.IndexFinalSelecionado].Tipo = null;

                // Atualiza o path da polyline selecionada e as polylines do apagar trecho.
                FuncionalidadesMapa.AtualizarPathPolylineSelecionada(polylineSelecionadaPath.Copy(), informacoesAdicionaisPath, false);
                ferramentaApagarTrecho.AtualizarPathPolylinesApagarTecho(informacoesAdicionaisPath.IndexPolylineClicada, polylineSelecionadaPath.Copy(), -1, true);

                // Valida se deve mostrar as informacoes da rota principal e o iniciofim.
                validaSeMostrarInformacoesInicioFimERota(polylineSelecionadaPath);

                _scope.vm.isValid = false;
                _scope.vm.RotaObservable.notify('enable');
            }

        }

        /**
         * Metodo para verificar se deve mostrar a rota e o iniciofim. Passando como parametro o novo path.
         * @param {*} novoPathPolyline
         */
        function validaSeMostrarInformacoesInicioFimERota(novoPathPolyline) {
            if (ferramentaApagarTrecho.GetPolylinesApagarTecho().length <= 1) {
                // Se no apagar trecho estiver com Uma ou Nenhuma polyline.

                // Esconde a polyline selecionada, atualiza o Path da polyline principal, mostra o iniciofim e mostra a rota.
                FuncionalidadesMapa.EsconderPolylineSelecionada();
                FuncionalidadesMapa.AtualizaPathPolyline(_codigoRota, novoPathPolyline.Copy());

                _scope.vm.carregarCamadas();

                // Inicia o evento dos markers de inicio e fim.
                ferramentaInicioFimColeta.Init();
            }
        }

        /**
         * Metodo para habilitar a ferramenta de 'ajustarTrecho', passando o tipo da ferramenta como parametro.
         * @param {*} tipoFerramenta
         */
        function habilita(tipoFerramenta) {

            _habilitado = true;

            // Chama funcao para mostrar no HTML a janela da ferramenta.
            _scope.HabilitarAjustarTrecho(_habilitado, tipoFerramenta);
        }

        /**
         * Metodo para desabilitar a ferramenta de 'ajustarTrecho'.
         */
        function desabilita() {

            _habilitado = false;

            // Chama funcao para esconder no HTML a janela da ferramenta.
            _scope.HabilitarAjustarTrecho(_habilitado);
        }

        /**
         * Retorno da factory para expor as funcoes.
         */
        return {
            UnificarTrecho: unificarTrecho,
            SegmentarTrecho: segmentarTrecho,
            Habilita: habilita,
            Desabilita: desabilita,
            SetScope: (scope) => _scope = scope,
            IsHabilitado: () => _habilitado
        };
    }
})();
