// [BShop]
//
// - BShop.Currency - Currency formating
// - BShop.Cart - Shopping cart storage, uses ajax to manage shopping
//   cart and communication between client and server.
// - BShop.View - Shopping cart view
//
// Use BShop.Cart.getInstance().init(args?) to initialize cart data

// ----------------------------------------------------------------------------
// [BShop.Currency]
// ----------------------------------------------------------------------------

BLite.Object.define("BShop.Currency",
{
  extend: BLite.Object,
  type: "singleton",

  properties:
  {
    code: {},
    leftSymbol: {},
    rightSymbol: {},
    decimalPoint: {},
    roundDecimals: {},
    displayDecimals: {},
    rate: {}
  },

  construct: function()
  {
    // [SuperClass]
    this.base(arguments);

    // [Properties]
    this._code = "";
    this._leftSymbol = "";
    this._rightSymbol = "";
    this._decimalPoint = ".";
    this._roundDecimals = 2;
    this._displayDecimals = 2;
    this._rate = 1.0;
  },

  members:
  {
    round: function(n)
    {
      // Calculate rounding constant
      var p = Math.pow(10, this._roundDecimals)
      // round(n*p)/p will produce correct result
      return (Math.round(n * p) / p);
    },

    format: function(n)
    {
      var res;
      if (n == null) return "";

      // use currency rate
      n *= this._rate;

      // round and convert to string
      res = this.round(n).toFixed(this._displayDecimals);

      // Set correct decimal point
      if (this._decimalPoint !== ".") res = res.replace(/\./, this._decimalPoint);

      // Apply left symbol and right symbol rules
      return this._leftSymbol + res + this._rightSymbol;
    }
  }
});

// ----------------------------------------------------------------------------
// [BShop.Cart]
// ----------------------------------------------------------------------------

//! Shopping cart singleton.
BLite.Object.define("BShop.Cart",
{
  extend: BLite.Object,
  type: "singleton",

  construct: function()
  {
    // [SuperClass]
    this.base(arguments)

    // [Shopping Cart]
    this._items = null;
    this._summary = null;

    // [Views]
    this._views = [];

    // [States]
    this._working = 0;
    this._refreshRequest = null;
    this._refreshAgain = false;
  },

  destruct: function()
  {
  },

  members:
  {
    // [Initialization]

    init: function(cart)
    {
      this._items = cart.items;
      this._summary = cart.summary;
      this._shipping = cart.shipping;
      this._payment = cart.payment;
      this._shippingData = cart.shippingData;
      this.fireDataEvent("change", this.getCart());
    },

    // [Views]

    registerView: function(view)
    {
      if (this._views.indexOf(view) === -1)
      {
        // add to views list
        this._views.push(view);

        view._registered(this);

        // manually dispatch change event for view, because we don't
        // want to dispatch this event for all listeners.
        // TODO: should BLite handle this ?
        var e = BLite.Pool.create(BLite.Event.Data);
        e._name = "change";
        e._data = this.getCart();

        view._onChange(e);
        BLite.Pool.recycle(e);
      }
    },

    unregisterView: function(view)
    {
      var i;
      if ((i = this._views.indexOf(view)) !== -1)
      {
        // remove from views list
        this._views.splice(i, 1);

        view._unregistered(this);
      }
    },

    getViews: function()
    {
      return this._views;
    },

    // [Shopping Cart]

    getCart: function()
    {
      return {
        items   : this._items,
        summary : this._summary
      };
    },

    getItems: function()
    {
      return this._items;
    },

    getSummary: function()
    {
      return this._summary;
    },

    getShipping: function()
    {
      return this._shipping;
    },

    setShipping: function(shipping)
    {
      if (this._shipping === shipping) return;

      this._shipping = shipping;
      this._modifiedShippingPayment();
    },

    getPayment: function()
    {
      return this._payment;
    },

    setPayment: function(payment)
    {
      if (this._payment === payment) return;

      this._payment = payment;
      this._modifiedShippingPayment();
    },

    _modifiedShippingPayment: function()
    {
      BLite.Request.send({
        url: "/shop/" + bLang + "/cart/shipping_payment/",
        method: "POST",
        data: {"params": BLite.Json.encode(
        {
          "shippingId": this._shipping || null,
          "paymentId": this._payment || null
        })},
        success: this._onSuccess,
        failure: this._onFailure,
        context: this
      });
    },

    getShippingData: function()
    {
      return this._shippingData;
    },

    // [Actions]

    assign: function(items)
    {
      if (!(items instanceof Array)) items = [items];
      this._normalizeItems(items);

      this._startWorking();
      this.fireDataEvent("action", { action: "setting", items: items });

      BLite.Request.send({
        url: "/shop/" + bLang + "/cart/set/",
        method: "POST",
        data: {"params": BLite.Json.encode(items)},
        success: this._onSuccess,
        failure: this._onFailure,
        context: this
      });
    },

    add: function(items)
    {
      if (!(items instanceof Array)) items = [items];
      this._normalizeItems(items);

      this._startWorking();
      this.fireDataEvent("action", { action: "adding", items: items });

      BLite.Request.send({
        url: "/shop/" + bLang + "/cart/add/",
        method: "POST",
        data: {"params": BLite.Json.encode(items)},
        success: this._onAddSuccess,
        failure: this._onFailure,
        context: this
      });
    },

    remove: function(items)
    {
      if (!(items instanceof Array)) items = [items];
      this._normalizeItems(items);

      this._startWorking();
      this.fireDataEvent("action", { action: "removing", items: items });

      BLite.Request.send({
        url: "/shop/" + bLang + "/cart/remove/",
        method: "POST",
        data: {"params": BLite.Json.encode(items)},
        success: this._onSuccess,
        failure: this._onFailure,
        context: this
      });
    },

    erase: function()
    {
      this._startWorking();
      this.fireDataEvent("action", { action: "erasing" });

      BLite.Request.send({
        url: "/shop/" + bLang + "/cart/erase/",
        method: "POST",
        success: this._onSuccess,
        failure: this._onFailure,
        context: this
      });
    },

    refresh: function()
    {
      // Don't refresh more times
      if (this._refreshRequest) { this._refreshAgain = true; return; }

      this._startWorking();
      this.fireDataEvent("action", { action: "refreshing" });

      this._refreshRequest = BLite.Request.send({
        url: "/shop/" + bLang + "/cart/view/",
        method: "POST",
        success: function(req)
        {
          this._refreshRequest = null;
          this._onSuccess(req);
        },
        failure: function(req)
        {
          this._refreshRequest = null;
          this._onFailure(req);
        },
        context: this
      });
    },

    //

    _normalizeItems: function(items)
    {
      for (var i = 0; i < items.length; i++)
      {
        var item = items[i];
        if (item.variant === undefined) item.variant = "";
        if (item.count === undefined) item.count = 1;
      }
    },

    // [Handlers]

    _onAddSuccess: function(req)
    {
      this._onSuccess(req);
      var n = this.getSummary().itemsCount;
      var text;

      if (n == 1)
        text = "položku";
      else if (n >= 2 && n <= 4)
        text = "položky";
      else
        text = "položek";

      alert("Zboží bylo přidáno do košíku.\n\nVáš košík nyní obsahuje " +
        n + " " + text + ".");
    },

    _onSuccess: function(req)
    {
      this._endWorking();

      var json = BLite.Json.decode(req.responseText);
      var result = json ? json.result : null;

      if (result)
      {
        this._items = result.items;
        this._summary = result.summary;
        this._shipping = result.shipping;
        this._payment = result.payment;
        this._shippingData = result.shippingData;
        this.fireDataEvent("change", this.getCart());
      }
    },

    _onFailure: function(req)
    {
      this._endWorking();
      alert("Požadavek na server se nezdařil.")
    },

    // [Working]
    _startWorking: function()
    {
      if (++this._working === 1) this.fireDataEvent("working", true);
    },

    _endWorking: function()
    {
      if (--this._working === 0) this.fireDataEvent("working", false);
    }
  }
});

// ----------------------------------------------------------------------------
// [BShop.View.Abstract]
// ----------------------------------------------------------------------------

//! Abstract cart view.
BLite.Object.define("BShop.View.Abstract",
{
  extend: BLite.Object,

  construct: function(elements)
  {
    // [SuperClass]
    this.base(arguments)

    // [Elements]
    this._elements = elements || {};
  },

  destruct: function()
  {
  },

  members:
  {
    // [Elements]

    getElements: function()
    {
      return this._elements;
    },

    // [Helpers]

    getDisplayName: function(item)
    {
      var str = item.name;
      if (item.variant)
      {
        str += " (" + item.variant + ")";
      }
      return str;
    },

    // [Cart]

    //! Called when this view was registered.
    _registered: function(cart)
    {
      // register events
      cart.addListener("change", this._onChange, this);
      cart.addListener("action", this._onAction, this);
      cart.addListener("working", this._onWorking, this);
    },

    //! Called when this view was unregistered.
    _unregistered: function(cart)
    {
      // unregister events
      cart.removeListener("change",  this._onChange, this);
      cart.removeListener("action",  this._onAction, this);
      cart.removeListener("working", this._onWorking, this);
    },

    // [Event Handlers]

    _onChange: function(e) {},
    _onAction: function(e) {},
    _onWorking: function(e) {}
  }
});

// ----------------------------------------------------------------------------
// [BShop.View.Small]
// ----------------------------------------------------------------------------

BLite.Object.define("BShop.View.Small",
{
  extend: BShop.View.Abstract,

  construct: function(args)
  {
    // [SuperClass]
    this.base(arguments, args.elements)

    // [Elements]
    var z = this.getElements();

    // [Items]
    if (z.items)
    {
      z.itemsTable = BLite.Dom.TABLE();
      z.itemsTBody = BLite.Dom.TBODY();

      z.items.appendChild(z.itemsTable);
      z.itemsTable.appendChild(z.itemsTBody);
    }
  },

  destruct: function()
  {
  },

  members:
  {
    // [Update]

    update: function(data)
    {
      this._updateItems(data);
      this._updateSummary(data);
    },

    // [UI]

    // TODO: Stejne se nepouziva, jsou tam chyby
    _createItem: function(item)
    {
      return BLite.Dom.TR({
        children: [
          BLite.Dom.TD({
            className: "cart-item-row-name",
            children: [BLite.Dom.A({
              className: "cart-item-row-name-link",
              href: item.href,
              text: this.getDisplayName(item)
            })]
          }),
          BLite.Dom.TD({
            className: "cart-item-row-count",
            text: "" + item.count
          }),
          BLite.Dom.TD({
            className: "cart-item-row-actions",
            children: [
              BLite.Dom.A({
                className: "action-remove",
                href: "#",
                children: [
                  BLite.Dom.IMG({
                    className: "cart-item-del",
                    src: "/res/bfly/icon/16/actions/edit-delete.png",
                    alt: "Odstranit",
                    styles: { border: "none" }})
                ],
                events: {
                  click: function(e)
                  {
                    BShop.Cart.getInstance().remove({productId:item.id, variant: item.variant});
                    BLite.Dom.preventDefault(e);
                  }
                }
              })
            ]
          })
        ]
      });
    },

    _updateItems: function(data)
    {
      if (!data.items) return;

      // Elements
      var z = this.getElements();
      if (!z.items) return;

      var tbody = z.itemsTBody;
      var tr;
      while ((tr = BLite.Dom.first(tbody, "tr")))
        tbody.removeChild(tr);

      // Items
      var items = data.items;
      var i = 0, len = items.length;

      while (i < len)
      {
        var item = items[i++];
        tbody.appendChild(this._createItem(item));
      }

      // Now, if 'tr' exists, remove it and all next TRs, becuase they
      // are invalid (not contained in items[])
      while (tr)
      {
        var next = BLite.Dom.next(tr, "tr");
        tbody.removeChild(tr);
        tr = next;
      }

      // AJAX calls will set delete image to "working", so set these
      // images to delete
      var elms = BLite.Dom.elmsByClass("cart-item-del", "img", this._eItemsTBody);
      for (i = 0; i < elms.length; i++)
      {
        elms[i].src = "/res/bfly/icon/16/actions/edit-delete.png";
      }
    },

    _updateSummary: function(data)
    {
      if (!data.summary) return;

      var z = this.getElements();
      var currency = BShop.Currency.getInstance();

      if (z.summary.quantity       ) BLite.Dom.setText(z.summary.quantity       , data.summary.itemsCount);
      if (z.summary.priceWithoutTax) BLite.Dom.setText(z.summary.priceWithoutTax, currency.format(data.summary.itemsPriceWithoutTax));
      if (z.summary.priceWithTax   ) BLite.Dom.setText(z.summary.priceWithTax   , currency.format(data.summary.itemsPriceWithTax));
      if (z.summary.authorDuty     ) BLite.Dom.setText(z.summary.authorDuty     , currency.format(data.summary.itemsAuthorDuty));
      if (z.summary.recycleDuty    ) BLite.Dom.setText(z.summary.recycleDuty    , currency.format(data.summary.itemsRecycleDuty));
      if (z.summary.total          ) BLite.Dom.setText(z.summary.total          , currency.format(data.summary.itemsTotal));
    },

    // [Event Handlers]

    _onChange: function(e)
    {
      this.update(e.getData());
    },

    _onAction: function(e)
    {
      /*
      switch (e.getData().action)
      {
        case "removing":
          var items = e.getData().items;
          for (var i = 0; i < items.length; i++)
          {
            var id = items[i].productId;
            var tr = BLite.$("cart-item-" + id);
            var img = tr ? BLite.Dom.elmsByClass("cart-item-del", "img", tr)[0] : null;
            if (img) img.setAttribute("src", "/res/bfly/icon/16/animations/working.gif");
          }
          break;
      }
      */
    }
  }
});

// ----------------------------------------------------------------------------
// [BShop.View.Detail]
// ----------------------------------------------------------------------------

BLite.Object.define("BShop.View.Detail",
{
  extend: BShop.View.Abstract,

  construct: function(args)
  {
    // [SuperClass]
    this.base(arguments, args.elements);

    // [Rows]
    this._rows = [];
    this._editable = args.editable || false;

    // [UI]
    this._createUserInterface();
  },

  destruct: function()
  {

  },

  members:
  {
    // [Update]

    update: function(data)
    {
      this._updateItems(data);
      this._updateSummary(data);
    },

    // [UI]
    _createUserInterface: function()
    {
      var z = this.getElements();

      BLite.Dom.begin(this);

      // containers
      BLite.Dom.append(z.container,
      [
        BLite.Dom.DIV({
          as: "_eEmptyContainer",
          className: "cart-detail-empty",
          styles: {
            //display: "none"
          }
        }),
        BLite.Dom.DIV({
          as: "_eDirtyContainer",
          className: "cart-detail-dirty",
          styles: {
            //display: "none"
          }
        })
      ]);

      // dirty container
      BLite.Dom.append(this._eDirtyContainer,
      [
        BLite.Dom.TABLE({
          as: "_eTable",
          className: "cart-detail-table",
          children:
          [
            BLite.Dom.THEAD({
              as: "_eThead",
              children:
              [
                BLite.Dom.TR({
                  children:
                  [
                    BLite.Dom.TD({ text: "" }),
                    //BLite.Dom.TD({ text: "Kód" }),
                    BLite.Dom.TD({ text: "Název produktu" }),
                    BLite.Dom.TD({ text: "Počet" }),
                    BLite.Dom.TD({ text: "Bez DPH" }),
                    BLite.Dom.TD({ text: "DPH" }),
                    BLite.Dom.TD({ text: "Celkem" })
                  ]
                })
              ]
            }),
            BLite.Dom.TBODY({
              as: "_eTbody",
              children:
              [
                BLite.Dom.TR({
                  as: "_eSummaryBegin",
                  className: "cart-detail-summary-priceWithoutTax",
                  children:
                  [
                    BLite.Dom.TD({ colspan: "5", className: "cell-text"  , text: "Cena celkem bez DPH" }),
                    BLite.Dom.TD({ colspan: "2", className: "cell-number", as: "_eSummaryPriceWithoutTax" })
                  ]
                }),
                BLite.Dom.TR({
                  className: "cart-detail-summary-tax",
                  children:
                  [
                    BLite.Dom.TD({ colspan: "5", className: "cell-text"  , text: "DPH" }),
                    BLite.Dom.TD({ colspan: "2", className: "cell-number", as: "_eSummaryTax" })
                  ]
                }),
                BLite.Dom.TR({
                  as: "_eAuthorDutyContainer",
                  className: "cart-detail-author-duty",
                  children:
                  [
                    BLite.Dom.TD({ colspan: "5", className: "cell-text"  , text: "Autorské poplatky" }),
                    BLite.Dom.TD({ colspan: "2", className: "cell-number", as: "_eSummaryAuthorDuty" })
                  ]
                }),
                BLite.Dom.TR({
                  as: "_eRecycleDutyContainer",
                  className: "cart-detail-recycle-duty",
                  children:
                  [
                    BLite.Dom.TD({ colspan: "5", className: "cell-text"  , text: "Recyklační poplatky" }),
                    BLite.Dom.TD({ colspan: "2", className: "cell-number", as: "_eSummaryRecycleDuty" })
                  ]
                }),
                BLite.Dom.TR({
                  className: "cart-detail-shipping",
                  children:
                  [
                    BLite.Dom.TD({ colspan: "5", className: "cell-text"  , text: "Doprava" }),
                    BLite.Dom.TD({ colspan: "2", className: "cell-number", as: "_eSummaryShipping" })
                  ]
                }),
                BLite.Dom.TR({
                  className: "cart-detail-summary-round",
                  children:
                  [
                    BLite.Dom.TD({ colspan: "5", className: "cell-text"  , text: "Zaokrouhlení" }),
                    BLite.Dom.TD({ colspan: "2", className: "cell-number", as: "_eSummaryRound" })
                  ]
                }),
                BLite.Dom.TR({
                  className: "cart-detail-summary-final",
                  children:
                  [
                    BLite.Dom.TD({ colspan: "5", className: "cell-text"  , text: "Cena celkem s DPH včetně všech poplatků" }),
                    BLite.Dom.TD({ colspan: "2", className: "cell-number cell-final", as: "_eSummaryTotal" })
                  ]
                })
              ]
            })
          ]
        })
      ]);

      BLite.Dom.end();
    },

    _createRow: function()
    {
      var row = {};
      BLite.Dom.begin(row);
      BLite.Dom.TR({
        as: "tr",
        className: "cart-detail-row",
        children: [
          BLite.Dom.TD({
            className: "cell-text",
            children: [ BLite.Dom.INPUT({as: "checkbox", type: "checkbox"}) ]
          }),
          //BLite.Dom.TD({
          //  as: "shopCode",
          //  className: "cell-text"
          //}),
          BLite.Dom.TD({
            className: "cell-text",
            children: [ BLite.Dom.A({ as: "name" }) ]
          }),
          BLite.Dom.TD({
            className: "cell-text",
            children: [ BLite.Dom.INPUT({as: "count", type: "text", className: "cart-detail-quantity" }) ]
          }),
          BLite.Dom.TD({
            as: "priceWithoutTax",
            className: "cell-number"
          }),
          BLite.Dom.TD({
            as: "taxRate",
            className: "cell-number"
          }),
          BLite.Dom.TD({
            as: "priceWithTax",
            className: "cell-number"
          })
        ]
      });
      BLite.Dom.end();
      if (!this._editable) row.count.readonly = true;
      return row;
    },

    _updateItems: function(data)
    {
      if (!data.items) return;

      var currency = BShop.Currency.getInstance();

      var items = data.items || [];
      var rows = this._rows;
      var i, len;

      var tbody = this._eTbody;

      // Save checkbox states.
      var checked = {};
      for (i = 0, len = rows.length; i < len; i++)
      {
        var row = rows[i];
        if (row.checkbox.checked) checked["" + row._id + row._variant] = true;
      }

      // Sync count of objects and items.
      while (rows.length > items.length)
      {
        tbody.removeChild(rows.pop().tr);
      }
      while (rows.length < items.length)
      {
        var row = this._createRow();
        rows.push(row);
        BLite.Dom.insertBefore(this._eSummaryBegin, row.tr);
      }

      // Update items
      for (i = 0, len = items.length; i < len; i++)
      {
        var row = rows[i];
        var item = items[i];

        row._id = item.id;
        row._variant = item.variant;

        row.checkbox.checked = checked["" + item.id + item.variant] || false;

        //BLite.Dom.setText(row.shopCode, item.shopCode);
        BLite.Dom.setText(row.name, this.getDisplayName(item)); row.name.href = item.href;

        row.count.value = item.count;

        BLite.Dom.setText(row.taxRate, "" + (item.taxRate * 100) + "%");

        BLite.Dom.setText(row.priceWithoutTax, currency.format(item.priceWithoutTax * item.count));
        BLite.Dom.setText(row.priceWithTax   , currency.format(item.priceWithTax    * item.count));
      }
    },

    _updateSummary: function(data)
    {
      if (!data.summary) return;

      var currency = BShop.Currency.getInstance();

      BLite.Dom.setText(this._eSummaryPriceWithoutTax, currency.format(data.summary.itemsPriceWithoutTax));
      BLite.Dom.setText(this._eSummaryTax            , currency.format(data.summary.itemsTax));

      this._eAuthorDutyContainer.style.display = (data.summary.itemsAuthorDuty < 0.01) ? "none" : "block";
      this._eRecycleDutyContainer.style.display = (data.summary.itemsRecycleDuty < 0.01) ? "none" : "block";

      BLite.Dom.setText(this._eSummaryAuthorDuty     , currency.format(data.summary.itemsAuthorDuty));
      BLite.Dom.setText(this._eSummaryRecycleDuty    , currency.format(data.summary.itemsRecycleDuty));

      BLite.Dom.setText(this._eSummaryShipping       , currency.format(data.summary.shipping));
      BLite.Dom.setText(this._eSummaryRound          , currency.format(data.summary.round));
      BLite.Dom.setText(this._eSummaryTotal          , currency.format(data.summary.total));
    },

    // [Misc]
    /*
    _getCheckedItems: function()
    {
      var checked = [];
      var rows = this._rows;

      for (var i = 0, len = rows.length; i < len; i++)
      {
        if (rows[i].checkbox.checked) checked.push(rows[i]);
      }
      return checked;
    },
    */

    // [Event Handlers]

    _onChange: function(e)
    {
      this.update(e.getData());
    },

    _onAction: function(e)
    {
      switch (e.getData().action)
      {
        case "removing":
          var items = e.getData().items;
          for (var i = 0; i < items.length; i++)
          {
            var id = items[i].productId;
            var tr = BLite.$("cart-item-" + id);
            var img = tr ? BLite.Dom.elmsByClass("cart-item-del", "img", tr)[0] : null;
            if (img) img.setAttribute("src", "/res/bfly/icon/16/animations/working.gif");
          }
          break;
      }
    }
    /*,

    _onRemoveSelected: function(e)
    {
      BShop.Cart.getInstance().remove(this._getCheckedItems());
      BLite.Dom.preventDefault(e);
    },

    _onClearCart: function(e)
    {
      BShop.Cart.getInstance().erase();
      BLite.Dom.preventDefault(e);
    },

    _onSaveChanges: function(e)
    {
      var rows = this._rows;

      var ids = [];
      var count = [];

      for (var i = 0; i < rows.length; i++)
      {
        ids.push(rows[i]._id);
        count.push(rows[i].count.value);
      }

      BShop.Cart.getInstance().assign(ids, count);
      BLite.Dom.preventDefault(e);
    },

    _onContinue: function(e)
    {
    }*/
  }
});

BLite.Object.define("BShop.Util",
{
  statics:
  {
    getVariant: function(form)
    {
      form = BLite.$(form);
      if (form.variant)
      {
        var variants = form.variant;
        if (variants.length)
        {
          for (var i = 0; i < variants.length; i++)
            if (variants[i].checked) return variants[i].value;
        }
        else
        {
          return variants.value;
        }
      }

      return "";
    },

    buy: function(form, id, ev)
    {
      form = BLite.$(form);
      var count = 1;

      if (form.count)
      {
        count = parseInt(form.count.value);
        if (count == null || count < 1) count = 1;
      }

      BShop.Cart.getInstance().add({
        productId: id,
        variant: BShop.Util.getVariant(form),
        count: count
      });

      if (ev) BLite.Dom.preventDefault(ev);
    },

    initSearchBox: function(el, defaultText)
    {
      el = BLite.$(el);
      if (!el) return;

      if (el.value == "") el.value = defaultText;

      BLite.Dom.addListener(el, "focus", function() {
        if (el.value == defaultText) el.value = "";
      });

      BLite.Dom.addListener(el, "blur", function() {
        if (el.value == "") el.value = defaultText;
      });
    }
  }
});

