jQuery(function($){
  
  // Podstawowy model Todo posiada atrybuty `content` oraz `done`.
  window.Todo = Backbone.Model.extend({
    defaults: {
      done: false
    },

    toggle: function() {
      this.save({done: !this.get("done")});
    }
  });
  
  window.TodoList = Backbone.Collection.extend({
    model: Todo,

    // Zapisanie wszystkich czynności do zrobienia w przestrzeni nazw `"todos"`.
    localStorage: new Store("todos"),

    // Filtrowanie wszystkich pozycji na liście, które zostały ukończone.
    done: function() {
      return this.filter(function(todo){ return todo.get('done'); });
    },

    remaining: function() {
      return this.without.apply(this, this.done());
    }
  });

  // Utworzenie globalnej kolekcji czynności do zrobienia Todos.
  window.Todos = new TodoList;
  
  window.TodoView = Backbone.View.extend({

    // Widok jest znacznikiem listy.
    tagName:  "li",

    // Zapamiętanie funkcji szablonu dla pojedynczej czynności.
    template: $("#item-template").template(),

	// Zdelegowanie zdarzeń do funkcji widoku.
    events: {
      "change   .check"        : "toggleDone",
      "dblclick .todo-content" : "edit",
      "click    .todo-destroy" : "destroy",
      "keypress .todo-input"   : "updateOnEnter",
      "blur     .todo-input"   : "close"
    },

	// Zapewnienie, by wszystkie funkcje były wykonywane w odpowiednim zasięgu
    initialize: function() {
      _.bindAll(this, 'render', 'close', 'remove', 'edit');
	  
	  // Nasłuchiwanie zmian w modelu.
      this.model.bind('change', this.render);
      this.model.bind('destroy', this.remove);
    },

    render: function() {
	  // Uaktualnienie el na podstawie przechowywanego szablonu.
      var element = jQuery.tmpl(this.template, this.model.toJSON());
      $(this.el).html(element);
      this.input = this.$(".todo-input");
      return this;
    },

	// Przełączanie statusu done czynności, gdy kliknięte zostanie jej pole wyboru.
    toggleDone: function() {
      this.model.toggle();
    },    

    // Przełączenie widoku w tryb `"editing"` i wyświetlenie pól wejściowych.
    edit: function() {
      $(this.el).addClass("editing");
      this.input.focus();
    },

    // Zamknięcie trybu `"editing"` i zapisanie zmian w czynności do wykonania.
    close: function(e) {
      this.model.save({content: this.input.val()});
      $(this.el).removeClass("editing");
    },

    // Naciśnięcie Enter kończy edycję czynności.
	// Wyzwolenie zdarzenia blur na polu wejściowym i wyzwolenie close().
    updateOnEnter: function(e) {
      if (e.keyCode == 13) e.target.blur();
    },

	// Usunięcie elementu w momencie zniszczenia modelu.
    remove: function() {
      $(this.el).remove();
    },

	// Zniszczenie modelu po kliknięciu '.todo-destroy'
    destroy: function() {
      this.model.destroy();
    }
  });
  
  // Ogólny **AppView** to element UI najwyższego poziomu.
  window.AppView = Backbone.View.extend({

    // Zamiast generować nowy element, dowiązujemy istniejący szkielet App
    // już teraz obecny w kodzie HTML.
    el: $("#todoapp"),

    statsTemplate: $("#stats-template").template(),

    events: {
      "keypress #new-todo":  "createOnEnter",
      "click .todo-clear a": "clearCompleted"
    },

    // W momencie inicjalizacji wykonujemy dowiązanie do odpowiednich zdarzeń na kolekcji `Todos`,
    // gdy kolejne pozycje są dodawane lub zmieniane. Na początek ładujemy czynności,
    // które zostały wcześniej zapisane w *localStorage*.
    initialize: function() {
      _.bindAll(this, 'addOne', 'addAll', 'render');

      this.input    = this.$("#new-todo");

      Todos.bind('add',     this.addOne);
      Todos.bind('refresh', this.addAll);
      Todos.bind('all',     this.render);

      Todos.fetch();
    },

    // Ponowne wygenerowanie App oznacza odświeżenie statystyk --
    // reszta aplikacji się nie zmienia.
    render: function() {
      var done = Todos.done().length;
      var element = jQuery.tmpl(this.statsTemplate, {
        total:      Todos.length,
        done:       Todos.done().length,
        remaining:  Todos.remaining().length
      });
      this.$('#todo-stats').html(element);
    },

    // Dodanie jednej czynności do listy przez utworzenie dla niej widoku
    // oraz dołączenie jej elementu do listy `<ul>`.
    addOne: function(todo) {
      var view = new TodoView({model: todo});
      this.$("#todo-list").append(view.render().el);
    },

    // Dodanie wszystkich pozycji z kolekcji **Todos**.
    addAll: function() {
      Todos.each(this.addOne);
    },

    // Jeżeli w głównym polu wejściowym naciśnięty zostanie Enter, utworzony zostanie nowy model **Todo**.
    createOnEnter: function(e) {
      if (e.keyCode != 13) return;

      var value = this.input.val();
      if ( !value ) return;

      Todos.create({content: value});
      this.input.val('');
    },

    clearCompleted: function() {
      _.each(Todos.done(), function(todo){ todo.destroy(); });
      return false;
    }
  });

  // Na koniec uruchamiamy całą aplikację przez utworzenie **App**.
  window.App = new AppView;
});