function TerminaisModel() {
    "use strict";
    var mSelf = this,
        mMarcarTodos = null,
        terminaisFiltrados = [];

    function getMarcarTodos() {
        mMarcarTodos = checkValue(mMarcarTodos, document.getElementById("chkMarcarTodos"));
    }
    mSelf.isTerminal = true;
    mSelf.Todos = ko.observableArray([]);
    mSelf.Selected = ko.observable(null);

    mSelf.Filtro = ko.observable("");
    mSelf.limparFiltro = function () {
        mSelf.Filtro("");
    };

    mSelf.ListaFiltrada = ko.observableArray();
    mSelf.ListaSelecionada = ko.observableArray();

    mSelf.Show = ko.observable(true);

    mSelf.permiteAcaoAdicionar = ko.observable(true);

    mSelf.Visible = ko.observable(false);

    mSelf.TodosVisible = ko.computed(function () {
        if (mSelf.Filtro() === '') {
            terminaisFiltrados = mSelf.Todos().slice(0, 10);
        } else {
            terminaisFiltrados = mSelf.Todos();
        }

        return ko.utils.arrayFilter(terminaisFiltrados, function (item) {
            var filtro = mSelf.Filtro().toUpperCase(),
                desc = item.Descricao().toUpperCase(),
                ret = false;

            try {
                var regex = new RegExp(filtro, "gi");
                ret = regex.test(desc);
            } catch (e) {
                ret = false;
            }
            return ret;
        });
    });

    mSelf.LimpaFiltroTerminais = function () {
        mSelf.Filtro("");
    };

    //mSelf.FiltrarTerminais = function () {

    //    mSelf.ListaFiltrada([]);

    //    if(mSelf.Filtro() != "" && mSelf.Filtro().length > 2){
    //        mSelf.ListaFiltrada([]);

    //        var param = {
    //            filtro: mSelf.Filtro()
    //        };

    //        $.ajax({
    //            url: urlGlobal + "api/GestaoLinhaConsulta/FiltrarTerminaisCache",
    //            contentType: 'application/json; charset=utf-8',
    //            type: "GET",
    //            data: param,
    //            success: function (data) {
    //                for (var i in data.$values) {
    //                    var terminal = new TerminalModel(data.$values[i].Terminal);
    //                    terminal.Codigo(0);
    //                    if (Object.keys(vm.TodosTerminaisSelecionados).indexOf(data.$values[i].CodigoGrupoEntidade.toString()) > -1) {
    //                        terminal.Selected(true);
    //                    }
    //                    mSelf.ListaFiltrada.push(terminal);
    //                }
    //            },
    //            error: function(erro){
    //                alert(erro);
    //            }
    //        });
    //    }    
    //};

    mSelf.Lista = ko.observableArray([]);

    mSelf.MarcarTodos = ko.computed({
        read: function () {
            var visiveis = mSelf.TodosVisible(),
                firstUnchecked = null,
                selecionados = null,
                indeterminado = false;

            firstUnchecked = ko.utils.arrayFirst(visiveis, function (item) {
                return item.Selected() == false;
            });

            selecionados = ko.utils.arrayFilter(visiveis, function (item) {
                return item.Selected();
            });

            if (selecionados.length > 0) {
                indeterminado = visiveis.length != selecionados.length;
            }

            getMarcarTodos();

            if (mMarcarTodos) {
                mMarcarTodos.indeterminate = indeterminado;
            }

            return firstUnchecked == null;
        },
        write: function (value) {
            ko.utils.arrayForEach(mSelf.TodosVisible(), function (item) {
                if (item.verificaTerminalEnlace()) {
                    item.Selected(value);
                }
            });
        },
        owner: mSelf
    });

    mSelf.ListaVisivel = ko.observable(true);

    mSelf.AddVisivel = ko.observable(false);
    mSelf.EditarVisivel = ko.observable(false);

    mSelf.mostrar = function () {
        mSelf.Visible(true);
        mSelf.ListaVisivel(true);
        mSelf.AddVisivel(false);
        mSelf.EditarVisivel(false);

        mSelf.clickTerminais(true);
    };

    mSelf.esconder = function () {
        if (mSelf.AddVisivel()) {
            mSelf.toggleAdicionar();
        }

        mSelf.Visible(false);
        mSelf.ListaVisivel(false);
        mSelf.AddVisivel(false);
        mSelf.EditarVisivel(false);
        mSelf.editarTerminal();

        mSelf.clickTerminais(false);
    };

    mSelf.toggleAdicionar = function () {
        ko.utils.arrayForEach(mSelf.Todos(), function (item) {
            var todos = ko.utils.arrayFirst(mSelf.Lista(), function (i) {
                return i.Codigo() === item.Codigo();
            });
            item.Selected(todos != null);
        });

        var aux = mSelf.AddVisivel();
        mSelf.ListaVisivel(aux);
        mSelf.AddVisivel(!aux);
        ajusteTamanhos();

        mSelf.Filtro("");
    };

    mSelf.Add = ko.computed(function () {
        return mSelf.AddVisivel() && !mSelf.ListaVisivel();
    }, mSelf);

    mSelf.Edit = ko.computed(function () {
        return mSelf.Visible() && mSelf.EditarVisivel();
    }, mSelf);

    mSelf.mostrarEditar = function () {
        var aux = mSelf.EditarVisivel();
        mSelf.ListaVisivel(aux);
        mSelf.EditarVisivel(!aux);
        ajusteTamanhos();
    };

    mSelf.remover = function (terminal) {
        if (listaIsOpen.removerTerminalToggleEnabled()) {
            for (var count in vm.Enlaces.Lista()) {
                var enlace = vm.Enlaces.Lista()[count];
                if (!enlace.Apagado()) {
                    if (mSelf.isTerminal) {
                        if (enlace.TerminalInicial() != null && enlace.TerminalFinal() != null) {
                            if (terminal.Codigo() == enlace.TerminalInicial().Codigo() || terminal.Codigo() == enlace.TerminalFinal().Codigo()) {
                                abrirModalMensagem("Excluir terminal", "Não é permitido  excluir um terminal que esteja vinculado em um enlace.");
                                return;
                            }
                        }
                    } else {
                        if (enlace.PontosOnibus && enlace.PontosOnibus.Lista().length > 0) {
                            var hasPtOnibus = ko.utils.arrayFirst(enlace.PontosOnibus.Lista(), function (pt) {
                                return pt.CodigoGrupoEntidade() === terminal.CodigoGrupoEntidade();
                            });
                            if (hasPtOnibus != null) {
                                abrirModalMensagem("Excluir ponto de ônibus", "Não é permitido  excluir um ponto de ônibus que esteja vinculado em um enlace.");
                                return;
                            }
                        }
                    }
                }
            }

            abrirModalMensagem("Confirmação", "Deseja remover " + terminal.Descricao() + "?",
                {
                    resizable: false,
                    height: 190,
                    modal: true,
                    buttons: {
                        "Remover": function () {
                            if (terminal.IsPontoOnibus()) {
                                delete vm.PontosOnibusSelecionados[terminal.CodigoGrupoEntidade()];
                            }
                            terminal.Selected(false);
                            terminal.setMap(null);

                            terminal.Apagado(true);
                            // remover ponto de controle

                            if (terminal.PontoControle) {
                                terminal.PontoControle.setMap(null);
                                terminal.PontoControle.IsApagado = true;
                            }
                            terminal.IsEditandoPontoControle(false);

                            mSelf.listaToggle(terminal);
                            vm.Terminais.Lista.remove(terminal);

                            var todos = ko.utils.arrayFirst(mSelf.Todos(), function (i) {
                                return i.Codigo() === terminal.Codigo();
                            });

                            if (todos)
                                todos.Selected(false);
                            mSelf.focarTodosTerminais();

                            $(this).dialog("close");
                        },
                        "Cancelar": function () {
                            $(this).dialog("close");
                        }
                    }
                });
        }
    };

    mSelf.editarTerminal = function (terminal, goto) {
        var array = mSelf.Lista();
        var rightClickHandler = new Handler({ eventName: "rightclick" });
        var clickHandler = new Handler({ eventName: "click" });
        for (var i = 0, j = array.length; i < j; i++) {
            if (array[i].IsEditando()) {
                array[i].editar();
                array[i].setFormaEvent([
                    rightClickHandler
                ]);
                array[i].setPontoControleEvent([
                    clickHandler
                ]);
                if (array[i] === terminal) {
                    return;
                }
                break;
            }
        }
        if (terminal) {
            terminal.editar(goto);
            rightClickHandler.callback = function (e, terminal) {
                var arrayItens = [],
                    pcItem = {
                        Texto: "Ponto de Controle",
                        Click: terminal.editarPontoControle
                    };

                if (terminal.PontoControle && terminal.PontoControle.IsApagado === true) {
                    pcItem.Click = terminal.togglePontoControle;
                }

                arrayItens.push(pcItem);
                arrayItens.push({ Texto: "Remover Terminal", Click: function () { mSelf.remover(terminal); } });

                vm.MenuContexto.show(arrayItens, e);
            };

            clickHandler.callback = function () {
                terminal.editarPontoControle();
            };

            terminal.setFormaEvent([
                rightClickHandler
            ]);

            terminal.setPontoControleEvent([
                clickHandler
            ]);
        }
    };

    mSelf.deselecionarPontosControle = function (array) {
        for (var i in array) {
            if (array[i].PontoControle != null && array[i].PontoControle.getEditable()) {
                array[i].PontoControle.setEditable(false);
                array[i].IsEditandoPontoControle(false);
            }
        }
    };

    mSelf.removerPontoControle = function (array) {
        for (var i in array) {
            if (array[i].PontoControle != null && array[i].PontoControle.getEditable()) {
                array[i].PontoControle.setMap(null);
                array[i].PontoControle = null;
            }
        }
    };

    mSelf.clickTerminais = function (setar) {
        var clickHandler = new Handler({
            eventName: "click"
        });

        if (setar) {
            clickHandler.callback = function (e, terminal) {
                mSelf.listaToggle(terminal);
            };
        }

        mSelf.setFormaEvents([
            clickHandler
        ]);
    };


    mSelf.setFormaEvents = function (handlers) {
        ko.utils.arrayForEach(mSelf.Lista(), function (item) {
            item.setFormaEvent(handlers);
        });
    }

    mSelf.keypressMapa = function (evt) {
        var array = this.Lista();
        // Letra D ou Delete
        if (evt.keyCode == 68 || evt.keyCode == 46) {
            this.removerPontoControle(array);
        } else if (evt.keyCode == 38) {
            mSelf.pontoControleAndar(array, 0);
        } else if (evt.keyCode == 39) {
            mSelf.pontoControleAndar(array, 90);
        } else if (evt.keyCode == 40) {
            mSelf.pontoControleAndar(array, 180);
        } else if (evt.keyCode == 37) {
            mSelf.pontoControleAndar(array, 270);
        }
    };

    mSelf.pontoControleAndar = function (array, direcao) {
        for (var i in array) {
            if (array[i].PontoControle != null && array[i].PontoControle.getEditable()) {
                var nvLatLng = addMetrosToLatLng(array[i].PontoControle.getCenter(), direcao, 0.001);
                array[i].PontoControle.setCenter(nvLatLng);
            }
        }
    };

    mSelf.bounds = null;

    mSelf.exibirMapa = function () {
        mSelf.permiteAcaoAdicionar(true);

        mSelf.adicionarToggle();

        vm.Terminais.Lista.removeAll();

        var listaTodosTerminais = Object.keys(vm.TodosTerminaisSelecionados);

        for (var i in listaTodosTerminais) {
            vm.TodosTerminaisSelecionados[listaTodosTerminais[i]].setMap(null);
        }

        var selecionados = ko.utils.arrayFilter(vm.Terminais.Todos(), function (item) {
            return item.Selected()
        });

        if (selecionados) {

            mSelf.bounds = new google.maps.LatLngBounds();
            for (var item in selecionados) {

                if (listaTodosTerminais.indexOf(selecionados[item].CodigoGrupoEntidade().toString()) < 0) {
                    selecionados[item].Codigo(0);
                }

                vm.Terminais.Lista.push(selecionados[item]);


                selecionados[item].setMap(map, selecionados[item]);

                mSelf.bounds.union(selecionados[item].bounds);

            }
            map.fitBounds(mSelf.bounds);
        }

        //adiciona na lista de terminais os pontos de onibus da linha
        var listaTodosPontos = Object.keys(vm.PontosOnibusSelecionados);

        for (var i in listaTodosPontos) {
            vm.Terminais.Lista.push(vm.PontosOnibusSelecionados[listaTodosPontos[i]]);
        }

        mSelf.Filtro("");

        mSelf.focarTodosTerminais();
    };

    mSelf.ClonarTerminais = function () {

        vm.CloneTerminais = []

        for (var item in vm.TodosTerminaisSelecionados) {
            vm.CloneTerminais.push(vm.TodosTerminaisSelecionados[item]);
        }
    };

    mSelf.cancelarAdicionarTerminal = function () {
        mSelf.permiteAcaoAdicionar(true);
        mSelf.adicionarToggle();
    };

    mSelf.mostrarTerminaisEnlace = function (enlace) {
        var ocultaTerminal = false;
        if (enlace.TerminalInicial() != null && enlace.TerminalFinal()) {
            for (var term in mSelf.Lista()) {
                if (mSelf.Lista()[term].Codigo() != enlace.TerminalFinal().Codigo() &&
                    mSelf.Lista()[term].Codigo() != enlace.TerminalInicial().Codigo()) {

                    ocultaTerminal = true;
                }

                if (ocultaTerminal) {
                    mSelf.Lista()[term].forma.setMap(null);
                    mSelf.Lista()[term].labelMap.setMap(null);
                    if (mSelf.Lista()[term].PontoControle != null)
                        mSelf.Lista()[term].PontoControle.setMap(null);
                }
                ocultaTerminal = false;
            }
        }
    };

    mSelf.mostrarTodosTerminais = function (cor, naoAlteraCor) {
        var lista = mSelf.Lista(),
            color = null;
        for (var term in lista) {
            if (lista[term].forma) {
                lista[term].forma.setMap(map);
                lista[term].labelMap.setMap(map);
                if (cor) {
                    color = cor;
                    for (var t in naoAlteraCor) {
                        if (lista[term].CodigoGrupoEntidade() === naoAlteraCor[t]) {
                            color = null;
                            break;
                        }
                    }
                    if (color) {
                        lista[term].setCor(color);
                    }
                }
            }
        }

        if (mSelf.bounds)
            map.fitBounds(mSelf.bounds);
    };

    mSelf.focarTodosTerminais = function () {
        //mSelf.bounds = new google.maps.LatLngBounds();
        //for (var i in mSelf.Lista()) {
        //    mSelf.bounds.union(mSelf.Lista()[i].bounds);
        //}

        //map.fitBounds(mSelf.bounds);

        //var listaTodosTerminais = Object.keys(vm.TodosTerminaisSelecionados);

        //for (var i in listaTodosTerminais) {
        //    vm.TodosTerminaisSelecionados[listaTodosTerminais[i]].setMap(null);
        //}

        //var listaTerminais = ko.utils.arrayFilter(vm.Terminais.ListaSelecionada(), function (item) {
        //    return item.Selected();
        //});
        //if (listaTerminais.length > 0) {
        //    mSelf.bounds = new google.maps.LatLngBounds();

        //    ko.utils.arrayForEach(listaTerminais, function (terminal) {
        //        terminal.setMap(map, terminal);
        //        mSelf.bounds.union(terminal.bounds);
        //    });

        //    vm.Terminais.ListaSelecionada.removeAll();
        //    vm.Terminais.ListaSelecionada.push.apply(mSelf.ListaSelecionada, listaTerminais);

        //    map.fitBounds(mSelf.bounds);
        //} else {
        //    vm.Terminais.ListaSelecionada.removeAll();
        //}
    }

    mSelf.setarCoresTerminais = function () {

        for (var term in mSelf.Lista()) {

            mSelf.Lista()[term].setCor();
        }
    };

    mSelf.toggleIsVisible = ko.observable(false);

    mSelf.toggle = function () {
        var fn = mSelf.show;

        if (mSelf.toggleIsVisible()) {
            fn = mSelf.hide;
        }

        fn();

        return mSelf.toggleIsVisible();
    };

    // métodos para utilizar o toggle
    mSelf.show = function () {
        mSelf.toggleProperties(true);
    };

    mSelf.hide = function () {
        mSelf.toggleProperties(false);
    };

    mSelf.toggleProperties = function (visible) {
        mSelf.toggleIsVisible(visible);
        mSelf.clickTerminais(visible);
    }

    function AdicionarTerminais() {
        var mSelfAdicionar = this;

        mSelfAdicionar.toggleIsVisible = ko.observable(false);

        mSelfAdicionar.toggle = function () {
            var fn = mSelfAdicionar.show;

            if (mSelfAdicionar.toggleIsVisible()) {
                fn = mSelfAdicionar.hide;
            }

            fn();

            return mSelfAdicionar.toggleIsVisible();
        };

        // métodos para utilizar o toggle
        mSelfAdicionar.show = function () {
            mSelfAdicionar.toggleProperties(true);
        };

        mSelfAdicionar.hide = function () {
            mSelfAdicionar.toggleProperties(false);
        };

        mSelfAdicionar.toggleProperties = function (visible) {
            mSelfAdicionar.toggleIsVisible(visible);
        }
    }

    mSelf.Adicionar = new AdicionarTerminais();


    mSelf.adicionarToggleEnabled = ko.observable(true);
    mSelf.botesEnabled = ko.observable(true);
    mSelf.listaToggleEnabled = ko.observable(true);
    mSelf.listaToggleEnabled.subscribe(function (value) {
        ko.utils.arrayForEach(mSelf.Lista(), function (terminal) {
            terminal.toggleEnabled(value);
        });
    });

    mSelf.adicionarToggle = function () {
        if (mSelf.adicionarToggleEnabled() && mSelf.permiteAcaoAdicionar()) {
            var show = mSelf.Adicionar.toggle();
            mSelf.disableOptions(!show);
            mSelf.permiteAcaoAdicionar(!show);
            if (show) {
                mSelf.adicionarToggleEnabled(show);
            }
        }
    };

    var listaIsOpen = null,
        terminalOriginal = null;
    mSelf.listaToggle = function (obj, irPara) {
        if (mSelf.listaToggleEnabled() || listaIsOpen === obj) {
            if (obj.removerTerminalToggleEnabled()) {
                if (listaIsOpen) {
                    terminalOriginal = null
                    mSelf.disableOptions(true);
                    listaIsOpen = null;
                    obj.editar();
                    return;
                }

                terminalOriginal = obj.salvar();
                listaIsOpen = obj;
                mSelf.disableOptions(false);
                obj.toggleEnabled(true);
                obj.editar(irPara);
            }
        }
    };

    mSelf.disableOptions = function (disable) {
        mSelf.adicionarToggleEnabled(disable);
        mSelf.listaToggleEnabled(disable);
        mSelf.botesEnabled(disable);
    }

    mSelf.cssBtn = ko.computed(function () {
        var className = "btn";
        if (mSelf.botesEnabled()) {
            className += " btn-info";
        }
        className += " btn-small";
        return className;
    });

    mSelf.salvar = function () {
        var toSave = null,
            lista = ko.utils.arrayFilter(mSelf.Lista(), function (ent) {
                return ent.Codigo() > 0 || (ent.Codigo() < 0 && !ent.IsApagado());
            });

        toSave = ko.utils.arrayMap(lista, function (ent) {
            return ent.salvar();
        });

        return toSave;
    };

    mSelf.salvarAlteracaoTerminal = function (obj) {
        mSelf.listaToggle(obj, false);
    };

    mSelf.cancelarAlteracaoTerminal = function (obj) {
        if (obj) {
            obj.LinhaEntidade = new LinhaEntidadeModel(terminalOriginal);
            if (obj.PontoControle && !obj.LinhaEntidade.HasPossuiPontoControle()) {
                obj.PontoControle.setCenter(obj.pontoReferencia());
                obj.PontoControle.setRadius(20);
                obj.PontoControle.setMap(null);
                obj.PontoControle.IsApagado = true;
            }
        }
        //for (var i in terminalOriginal) {
        //    if (obj[i]) {
        //        obj[i](terminalOriginal[i]);
        //    } else if (obj.LinhaEntidade && obj.LinhaEntidade[i]) {
        //        if (obj.LinhaEntidade) {
        //            obj.LinhaEntidade[i](terminalOriginal[i]);
        //        } else {
        //            obj.LinhaEntidade = new LinhaEntidadeModel(terminalOriginal);
        //        }
        //    }
        //}

        if (obj.LinhaEntidade) {
            obj.LinhaEntidade.updateLatlng();

            if (obj.LinhaEntidade.HasPossuiPontoControle() && obj.LinhaEntidade.latlng.toString() != "(0, 0)") {
                obj.PontoControle.setCenter(obj.LinhaEntidade.latlng);
                obj.PontoControle.setRadius(obj.LinhaEntidade.PontoControleRaio());
                obj.PontoControle.setMap(map);
            }
        } else {
            obj.PontoControle.setCenter(obj.pontoReferencia());
            obj.PontoControle.setRadius(20);
            obj.PontoControle.setMap(null);
            obj.PontoControle.IsApagado = true;
        }
        mSelf.listaToggle(obj, false);
    };
}