<!doctype html>
<html ng-app="ngResourceDemo">
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="./style.css">
</head>
<body ng-controller="MainCtrl">
  <nav>
    <button ng-click="service.addNewTicket()">Neues Ticket anlegen</button>
    <button ng-click="service.loadAllTickets()">Alle Tickets laden</button>
    <div>
      <input type="text" ng-model="ticketToSearch" />
      <button ng-click="service.loadTicketById(ticketToSearch)">Lade Ticket</button>
    </div>
  </nav>
  <div class="ticket" ng-repeat="ticket in service.getTickets()">
    <button ng-click="service.saveTicket(ticket)">Speichern</button>
    <button ng-click="service.deleteTicket(ticket)">Löschen</button>
    <h3>({{ticket.id}}) <input ng-model="ticket.title" /></h3>
    <span class="assigned-to">Zugeordnet zu: <input ng-model="ticket.assignedTo" /></span>
    <span class="percentage-complete">Erledigt %: <input ng-model="ticket.percentageComplete"</span><br />
    <textarea ng-model="ticket.description" class="description"></textarea>
  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular-resource.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular-mocks.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
  <script src="./script.js"></script>
</body>
</html>
'use strict';

var app = angular.module('ngResourceDemo', ['ngResource', 'ngMockE2E']);

app.constant('baseUrl', '/ticket/:id');

app.factory('TicketService', function($resource, baseUrl) {
  // den $resource-Service initialisieren
  var TicketResource = $resource(baseUrl);

  // Ticket-Container für bereits geladene Tickets
  var tickets = [];

  var loadAllTickets = function() {
    TicketResource.query(function(data) {
      tickets = data;
    });
  };

  var loadTicketById = function(id) {
    if(!id || !angular.isNumber(parseInt(id, 10)))
      return;

    // Zugriff auf das Promise des zugrundeliegenden $http-Services
    TicketResource.get({id: id}).$promise.then(function(ticket) {
      tickets = [ticket];
    });
  };

  var saveTicket = function(ticket) {
    ticket.$save();
  };

  var addNewTicket = function() {
    var newTicket = new TicketResource({
      id: 0,
      title: 'Neues Ticket',
      assignedTo: 'Niemand',
      description: 'Ticket Nachricht hier einfügen',
      percentageComplete: 0
    });

    newTicket.$save(function(repliedTicket) {
      tickets.unshift(repliedTicket);
    });
  };

  var deleteTicket = function(ticket) {
    TicketResource.delete({ id: ticket.id }, function() {
      var pos = _.indexOf(_.pluck(tickets, 'id'), ticket.id);
      tickets.splice(pos, 1);
    });
  };

  // Revealing Module Pattern
  return {
    getTickets: function() {
      return tickets;
    },
    loadAllTickets: loadAllTickets,
    loadTicketById: loadTicketById,
    saveTicket: saveTicket,
    addNewTicket: addNewTicket,
    deleteTicket: deleteTicket
  };
});

app.controller('MainCtrl', function($scope, TicketService) {
  $scope.service = TicketService;
  $scope.ticketToSearch = "";
});

// httpBackend um Delay erweitern
app.config(function ($provide) {
  $provide.decorator('$httpBackend', function($delegate) {
    // $delegate ==>
    // function $httpBackend(method, url, data, callback,
    //                       headers, timeout, withCredentials)
    // Parameter callback soll verzögert ausgeführt werden

    // Proxy für $httpBackend erstellen
    var wrapper = function(method, url, data, callback,
                           headers, timeout, withCredentials) {
      // Proxy für Parameter callback erstellen
      var delayedCallback = function() {
        // Zufällige Netzwerklast simulieren
        var delay = Math.random(0, 10) * 1000;

        // Verzögerter Aufruf
        setTimeout((function() {
          console.log('Antwortdauer: ' + delay);
          callback.apply(this, arguments[0]);
        }.bind(this, arguments)), delay);
      };
      $delegate(method, url, data, delayedCallback,
                headers, timeout, withCredentials);
    };

    // Der äußere Proxy muss auch alle Eigenschaften und Methoden
    // wie das Original aufweisen
    angular.extend(wrapper, $delegate);

    // Dekorierten Service zurückgeben
    return wrapper;
  });
});

// Verwendung von MockE2E um eine Backend-Attrappe zu erzeugen
app.run(function($httpBackend) {
  // Beispielhafte Ticket-Entität
  var ticket = function(
    id, title, assignedTo, description, percentageComplete) {
    this.id = id;
    this.title = title;
    this.assignedTo = assignedTo;
    this.description = description;
    this.percentageComplete = percentageComplete;
  };

  // Exemplarische Datenbank mit Sammlung von Tickets
  var tickets = [
    new ticket(
      '1', 'Fehlende Exception', 'Max Mustermann',
      'Die Methode XYZ liefert keine Exception im Fehlerfall ABC', 0),
    new ticket(
      '2', 'Ticket-Service einbinden', 'Max Mustermann',
      'Fehler sollen im Ticketing-Service protokolliert werden', 24),
    new ticket(
      '3', 'AngularJS updaten', 'Max Mustermann',
      'Es soll auf die aktuelle Version 1.3 upgedated werden', 0),
    new ticket(
      '4', 'Rahmen fehlt', 'Susi Sorglos',
      'Das div.demo hat keinen Border definiert', 100),
    new ticket(
      '5', 'Responsive Grid einführen', 'Susi Sorglos',
      'Die Applikation sollte ein Responsive Grid verwenden', 17),
  ];

  // Regex-Ausdruck um auf das Vorhandensein des ID Parameters abzuprüfen
  var regexGetTicket = new RegExp('/ticket/([0-9]+)');

  // alle Tickets retournieren
  // GET: /ticket
  $httpBackend.whenGET('/ticket').respond(tickets);

  // ein Ticket mittels übereinstimmender ID retournieren
  // GET: /ticket/:id
  $httpBackend.whenGET({
    test: function(url) {
      return regexGetTicket.test(url);
    }
  }).respond(function(method, url) {
    var id = url.match(regexGetTicket)[1];
    var match = _.find(tickets, function(t) {
      return t.id === id;
    });

    // Die Antwort ist ein HTTP-Response-Status mit einer Payload.
    // Abhängig davon ob das Objekt gefunden wurde, liefert die Methode
    // den passenden Status und Wert zurück
    return match === undefined ? [404, 'Not found'] : [200, match];
  });

  // Hinzufügen oder Überschreiben eines Tickets
  // POST: /ticket
  $httpBackend.whenPOST('/ticket').respond(function(method, url, data) {
    var newTicket = angular.fromJson(data);
    var existingTicket = _.find(tickets, function(t) {
      return t.id === newTicket.id;
    });

    if(existingTicket) {
      _.extend(existingTicket, newTicket);
      return [200, existingTicket];
    } else {
      newTicket.id = parseInt(_.max(tickets, function(t) {
        return t.id;
      }).id || 0, 10) + 1 + '';

      tickets.push(newTicket);
      return [200, newTicket];
    }
  });

  // Löchsen eines Tickets
  // DELETE: /ticket/:id
  $httpBackend.whenDELETE({
    test: function(url) {
      return regexGetTicket.test(url);
    }
  }).respond(function(method, url) {
    var id = url.match(regexGetTicket)[1];
    var match = _.find(tickets, function(t) {
      return t.id === id;
    });

    var pos = _.indexOf(_.pluck(tickets, 'id'), id);
    tickets.splice(pos, 1);

    return match === undefined ? [404, 'Not found'] : [200, match];
  });
  // Hinweis auf Attrappe
  console.log('Backend-Attrappe aktiv!');
});
.ticket {
  clear:both;
  border: black solid 1px;
  margin-bottom: 5px;
  padding: 10px;
  width: 500px;
}

.ticket .assigned-to {
  margin-right: 20px;
}

.ticket .percentage-complete input {
  width: 30px;
  text-align: right;
}

.ticket .description {
  width: 400px;
  height: 100px;
  margin-top: 10px;
}

nav * {
  float: left;
}

nav input {
  margin-left: 20px;
}