/**
 * @fileOverview Kapitola 6 – skript příkladu Internetový obchod.
 * V tomto skriptu se nachází komentáře ve formátu dokumentačních bloků
 * nástroje JsDoc Toolkit.
 * Oficiální stránky projektu JsDoc Toolkit jsou k dispozici na adrese
 * http://code.google.com/p/jsdoc-toolkit/.
 * @author Ondřej Baše
 */

/**
 * Seznam produktů v podobě objektu, jehož vlastnostmi jsou identifikátory
 * produktů a hodnotami objekty s informacemi o daných produktech.
 * Ve skutečné aplikaci bychom tento seznam načítali z databáze na straně
 * serveru.
 * @type Object
 */
var produkty =
  {
    1: {
      nazev: 'Apple iPhone 4S',
      cena: 16386,
      cenaProZobrazeni: '16 386 Kč',
      nahled: 'obrazky/nahled-apple-iphone-4s.png'
    },
    2: {
      nazev: 'Nokia 500',
      cena: 4319,
      cenaProZobrazeni: '4 319 Kč',
      nahled: 'obrazky/nahled-nokia-500.png'
    },
    3: {
      nazev: 'Samsung Galaxy Note',
      cena: 15490,
      cenaProZobrazeni: '15 490 Kč',
      nahled: 'obrazky/nahled-samsung-galaxy-note.png'
    },
    4: {
      nazev: 'Sony Ericson Xperia mini pro',
      cena: 5590,
      cenaProZobrazeni: '5 590 Kč',
      nahled: 'obrazky/nahled-sony-ericson-xperia-mini-pro.png'
    }
  },

  /**
   * Objekt, do kterého ukládáme počty položek v košíku. Vlastnostmi jsou
   * identifikátory produktů a hodnotami jsou počty daných produktů v košíku.
   * @type Object
   */
  zboziVKosiku = {};

$(inicializujSkript);

/**
 * Inicializuje skript. Volá se, když webový prohlížeč načte model DOM. V Této
 * funkci svazujeme elementy dokumentu s událostmi a obsluhujícími funkcemi
 * a vytváříme a nastavujeme instance komponent knihovny jQuery UI.
 */
function inicializujSkript() {
  $('img.obrazek-produktu').draggable({
    containment: 'document',
    helper: 'clone'
  });

  $('#kosik').droppable({
    accept: '.obrazek-produktu, .nahled-produktu',
    tolerance: 'touch',
    drop: uzivatelPustilProduktNaKosik,
    activate: uzivatelZacinaPresouvatProdukt,
	deactivate: uzivatelPresunulProdukt,
	over: uzivatelPresouvaProduktNadKosik,
    out: uzivatelPresouvaProduktMimoKosik
  });

  $(document)
    .on('keypress', 'div.produkt-v-kosiku input.pocet', overPlatnostPoctu)
    .on('change', 'div.produkt-v-kosiku input.pocet', upravPocetProduktu);
}

/**
 * Zobrazí všechny produkty v košíku. Aktuálně zobrazené (jsou-li nějaké)
 * produkty v košíku nahradíme novými podle obsahu globální proměnné
 * {@link zboziVKosiku}. Následně vytváříme instanci interakce Draggable pro
 * nové náhledy produktů (obrázky s třídou nahled-produktu).
 */
function zobrazProduktyVKosiku() {
  var obsah = '',
    produkt;

  for (var id in zboziVKosiku) {
    produkt = produkty[id];
    obsah += '\
      <div class="produkt-v-kosiku" id="produkt-v-kosiku-' + id + '">\
	    <input type="text" name="pocet-' + id + '" value="' +
          zboziVKosiku[id] + '" class="pocet" />\
        <img src="' + produkt.nahled + '" alt="' + produkt.nazev +
          '" title="' + produkt.nazev + ' (' + produkt.cenaProZobrazeni +
          ')' + '" class="nahled-produktu" />\
      </div>';
  }

  $('#kosik').html(obsah);

  $('img.nahled-produktu').draggable({
    containment: 'document',
    helper: 'clone',
    start: function() {
      $(this).hide();
    },
    stop: function() {
      var $nahled = $(this);
      if (!$nahled.data('probihaOdstranovani')) {
        $nahled.show();
      }
    }
  });
}

/**
 * Obsluhující funkce pro událost drop interakce Droppable, která se spouští
 * na elementu s identifikátorem kosik. Pokud uživatel pustil produkt
 * z katalogu, vložíme jej do košíku. V případě, že uživatel pustil produkt
 * z košíku, neděláme nic, protože přesunutí takového produktu zpracováváme
 * už ve funkci {@link uzivatelPresunulProdukt()}.
 * @param {Object} ev Objekt události.
 * @param {Object} ui Objekt ui pro událost drop interakce Droppable.
 */
function uzivatelPustilProduktNaKosik(ev, ui) {
  if (jeProduktZKatalogu(ui.draggable)) {
     vlozProduktDoKosiku(urciIdProduktu(ui.draggable));
  }
}

/**
 * Rozhodne, zda je daný produkt produktem z katalogu, a to podle jeho obrázku.
 * Obrázek produktu z katalogu má třídu obrazek-produktu.
 * @param {Object} $obrazek Objekt knihovny jQuery obsahující obrázek produktu.
 * @returns {Boolean} Hodnota true, pokud je specifikovaný produkt produktem
 *   z katalogu; jinak hodnota false.
 */
function jeProduktZKatalogu($obrazek) {
  return $obrazek.hasClass('obrazek-produktu');
}

/**
 * Určí identifikátor produktu z objektu knihovny jQuery. Tento identifikátor
 * zjišťujeme z atributu id rodičovského elementu pro uvedený element. Hodnota
 * tohoto atributu má předponu produkt- pro produkt z katalogu, zatímco pro
 * produkt z košíku obsahuje předponu produkt-v-kosiku-.
 * @param {Object} $element Objekt knihovny jQuery, jenž obaluje element
 *   dokumentu.
 * @returns {Number} Identifikátor produktu.
 */
function urciIdProduktu($element) {
  var $rodicovskyElement = $element.parent(),
    id;

  if ($rodicovskyElement.hasClass('produkt')) {
    id = $rodicovskyElement.attr('id').substr('produkt-'.length);
  } else if ($rodicovskyElement.hasClass('produkt-v-kosiku')) {
    id = $rodicovskyElement.attr('id').substr('produkt-v-kosiku-'.length);
  }

  return parseInt(id);
}

/**
 * Vloží produkt do košíku. Pokud už daný produkt v košíku je, zvýšíme pouze
 * počet. Posléze zobrazíme všechny produkty v košíku a spočítáme celkovou cenu
 * zboží. Pro přístup k položkám v košíku používáme globální proměnnou
 * {@link zboziVKosiku}.
 * @param {Number} id Identifikátor produktu.
 */
function vlozProduktDoKosiku(id) {
  if (zboziVKosiku[id]) {
    zboziVKosiku[id]++;
  } else {
    zboziVKosiku[id] = 1;
  }

  zobrazProduktyVKosiku();
  spocitejCenuZboziVKosiku();
}

/**
 * Spočítá cenu zboží v košíku. Na základě globálních proměnných
 * {@link zboziVKosiku} a {@link produkty} spočítáme celkovou cenu zboží.
 * Výslednou cenu zobrazíme do elementu s identifikátorem cena-zbozi. Pokud
 * je cena větší než nula, povolíme tlačítko s identifikátorem objednat; jinak
 * jej zakážeme.
 */
function spocitejCenuZboziVKosiku() {
  var cena = 0;

  for (var id in zboziVKosiku) {
    cena += zboziVKosiku[id] * produkty[id].cena;
  }

  $('#cena-zbozi').text(cena.toLocaleString() + ' Kč');

  if (cena > 0) {
    $('#objednat')
      .removeAttr('disabled')
      .removeClass('ui-state-disabled');
  } else {
    $('#objednat')
      .attr('disabled', 'disabled')
      .addClass('ui-state-disabled');
  }
}

/**
 * Obsluhující funkce pro událost activate interakce Droppable, která vzniká
 * na elementu s identifikátorem kosik. Pokud uživatel přesouvá produkt
 * z katalogu, přidáme elementu košíku třídu produkt-se-presouva a nastavíme
 * všechny elementy v košíku jako částečně průhledné.
 * @param {Object} ev Objekt události.
 * @param {Object} ui Objekt ui pro událost activate interakce Droppable.
 */
function uzivatelZacinaPresouvatProdukt(ev, ui) {
  if (jeProduktZKatalogu(ui.draggable)) {
    $(this).addClass('produkt-se-presouva');
    $('*', this).css('opacity', 0.35);
    $(ui.draggable).effect('transfer', {to: '#kosik'}, 'slow');
  }
}

/**
 * Obsluhující funkce pro událost deactivate interakce Droppable, jež nastává
 * na elementu s identifikátorem kosik. Jestliže uživatel přesunul produkt
 * z katalogu, odstraníme elementu košíku třídy produkt-se-presouva
 * a produkt-nad (nemusí je nutně mít) a všechny elementy v košíku nastavíme
 * jako neprůhledné. V případě, že uživatel přesunul produkt umístěný v košíku
 * mimo košík, provedeme animaci výbuchu a odstraníme daný produkt z košíku.
 * @param {Object} ev Objekt události.
 * @param {Object} ui Objekt ui pro událost deactivate interakce Droppable.
 */
function uzivatelPresunulProdukt(ev, ui) {
  if (jeProduktZKatalogu(ui.draggable)) {
    $(this).removeClass('produkt-se-presouva produkt-nad');
    $('*', this).css('opacity', 1);
  } else {
    if (ui.draggable.jeMimoKosik) {
      ui.draggable.data('probihaOdstranovani', true);
      vyhodDoPovetri(ui.helper, function() {
        odstranProduktZKosiku(urciIdProduktu(ui.draggable));
      });
    }
  }
}

/**
 * Obsluhující funkce pro událost over interakce Droppable, která se spouští
 * na elementu s identifikátorem kosik. Pokud uživatel přesouvá produkt
 * z katalogu, přidáme elementu košíku třídu produkt-nad. Když uživatel
 * přesouvá produkt z košíku, uložíme si příznak, že se daný produkt nachází
 * nad košíkem.
 * @param {Object} ev Objekt události.
 * @param {Object} ui Objekt ui pro událost over interakce Droppable.
 */
function uzivatelPresouvaProduktNadKosik(ev, ui) {
  if (jeProduktZKatalogu(ui.draggable)) {
    $(this).addClass('produkt-nad');
  } else {
    ui.draggable.jeMimoKosik = false;
  }
}

/**
 * Obsluhující funkce pro událost out interakce Droppable, jež vzniká na
 * elementu s identifikátorem kosik. Jestliže uživatel přesouvá produkt
 * z katalogu, odstraníme elementu košíku třídu produkt-nad. V případě, že
 * uživatel přesouvá produkt z košíku, uložíme si příznak, že daný produkt
 * se nachází mimo košík.
 * @param {Object} ev Objekt události.
 * @param {Object} ui Objekt ui pro událost out interakce Droppable.
 */
function uzivatelPresouvaProduktMimoKosik(ev, ui) {
  if (jeProduktZKatalogu(ui.draggable)) {
    $(this).removeClass('produkt-nad');
  } else {
    ui.draggable.jeMimoKosik = true;
  }
}

/**
 * Odstraňuje produkt z košíku. Nejprve mažeme hodnotu daného produktu
 * v globální proměnné {@link zboziVKosiku}. Potom odstraňujeme náhled produktu
 * z modelu DOM. Posléze zobrazíme všechny produkty v košíku a na konec znovu
 * spočítáme cenu zboží v košíku.
 * @param {Number} id Identifikátor produktu.
 */
function odstranProduktZKosiku(id) {
  delete zboziVKosiku[id];
  $('img.nahled-produktu').remove();
  zobrazProduktyVKosiku();
  spocitejCenuZboziVKosiku();
}

/**
 * Obsluhující funkce pro událost keypress, která vzniká na elementu input
 * s třídou pocet. Pokud uživatel stiskl klávesu s číslicí (0 až 9), nějakou
 * řídicí klávesu, nebo klávesovou zkratku Ctrl + A, vrátíme hodnotu true.
 * V opačném případě vrátíme hodnotu false.
 * @param {Object} ev Objekt události.
 * @returns {Boolean} Hodnota true, jestliže uživatel stiskl platnou klávesu;
 *   jinak hodnota false.
 */
function overPlatnostPoctu(ev) {
  var kodStisknuteKlavesy = ev.which,
    KOD_ZNAKU_0 = 48,
    KOD_ZNAKU_9 = 57,
    KOD_POSLEDNIHO_RIDICIHO_ZNAKU = 31,
    KOD_ZNAKU_MALE_A = 97,
    KOD_ZNAKU_VELKE_A = 65,
    KOD_KLAVESY_ENTER = 13;

  if ((kodStisknuteKlavesy >= KOD_ZNAKU_0 &&
    kodStisknuteKlavesy <= KOD_ZNAKU_9) ||
    kodStisknuteKlavesy <= KOD_POSLEDNIHO_RIDICIHO_ZNAKU ||
    ((kodStisknuteKlavesy == KOD_ZNAKU_MALE_A ||
	kodStisknuteKlavesy == KOD_ZNAKU_VELKE_A) && ev.ctrlKey)) {
    if (kodStisknuteKlavesy == KOD_KLAVESY_ENTER) {
      ev.preventDefault();
      $(this).change();
    }
    return true;
  } else {
    return false;
  }
}

/**
 * Obsluhující funkce pro událost change, jež nastává na elementu input
 * s třídou pocet. Pokud je počet kusů daného zboží větší než nula, upravíme
 * příslušnou hodnotu v globální proměnné {@link zboziVKosiku}. V opačném
 * případě provedeme animaci výbuchu následovanou odstraněním daného produktu
 * z košíku. Na závěr ještě přepočítáme celkovou cenu zboží v košíku.
 */
function upravPocetProduktu() {
  var $elementPoctu = $(this),
    hodnota = parseInt($elementPoctu.val(), 10),
    id = urciIdProduktu($elementPoctu);

  if (hodnota) {
    zboziVKosiku[id] = hodnota;
    spocitejCenuZboziVKosiku();
  } else {
    var $nahled = $elementPoctu.siblings('img.nahled-produktu');
    vyhodDoPovetri($nahled, function() {
      odstranProduktZKosiku(id);
    });
  }
}

/**
 * Provede animaci výbuchu pro daný element. Jakmile animace skončí, zavolá
 * uvedenou funkci.
 * @param {Object} $element Objekt knihovny jQuery, který obaluje nějaký
 *   element dokumentu.
 * @param {function} funkceZpetnehoVolani Funkce, která se zavolá, když animace
 *   skončí.
 */
function vyhodDoPovetri($element, funkceZpetnehoVolani) {
  $element.hide('explode', null, 'normal', funkceZpetnehoVolani);
}