/* ********** SUBMIT ORDER EVENT LISTENERS ************ */
    /**
     * Submit order to cortex.
     * @param submitOrderLink action-link to submit order to.
     */
    function submitOrder(submitOrderLink) {
      if (!submitOrderLink) {
        // FIXME [CU-92] user feedback??
        ep.logger.warn('checkout.submitOrderRequest called with no submitOrderLink');
        return;
      }

      var ajaxModel = new ep.io.defaultAjaxModel({
        type: 'POST',
        url: submitOrderLink,
        success: function (data, textStatus, XHR) {
          // Remove order link data from sessionStorage
          ep.io.sessionStore.removeItem('orderLink');

          var obj = {
            data: data,
            textStatus: textStatus,
            XHR: XHR
          };
          EventBus.trigger('checkout.submitOrderSuccess', obj);
        },
        customErrorFn: function (response) {
          EventBus.trigger('checkout.submitOrderFailed', response);
        }
      });

      ep.io.ajax(ajaxModel.toJSON());
    }
    EventBus.on('cart.removeLineItemRequest', function (deleteActionLink) {
      var ajaxModel = new ep.io.defaultAjaxModel({
        type: 'DELETE',
        url: deleteActionLink,
        success: function () {
          EventBus.trigger('cart.removeLineItemSuccess');
        },
        customErrorFn: function (response) {
          EventBus.trigger('cart.removeLineItemFailed', response);
        }
      });

      ep.io.ajax(ajaxModel.toJSON());
    });
  EventBus.on('payment.submitPaymentMethodForm', function (data, link) {
    var ajaxModel = new ep.io.defaultAjaxModel({
      type: 'POST',
      url: link,
      data: JSON.stringify(data),
      success: function () {
        EventBus.trigger('payment.submitPaymentFormSuccess');
      },
      customErrorFn: function () {
        EventBus.trigger('payment.submitPaymentFormFailed');
      }
    });

    ep.io.ajax(ajaxModel.toJSON());
  });
    var submitAddToCartForm = function (formDataObj) {
      var formActionLink = formDataObj.actionLink;
      var qty = formDataObj.qty;

      if (formActionLink) {
        if (qty > 0) {

          var obj = '{quantity:' + qty + '}';
          ep.io.ajax({
            type: 'POST',
            contentType: 'application/json',
            url: formActionLink,
            data: obj,
            success: function (response, x, y) {
              // follow link response
              ep.logger.info('Success posting to cart - go to cart view');

              // get the location header
              ep.logger.info(response);
              // ep.logger.info(request);
              ep.logger.info(JSON.stringify(y));
              var lineItemUrl = y.getResponseHeader('Location');
              ep.logger.info(lineItemUrl);
              if (lineItemUrl) {
                EventBus.trigger('category.loadDefaultCartRequest');
              }
              else {
                ep.logger.warn('add to cart success but no cart url returned');
              }


              ep.logger.info('we are done load the cart view');


            },
            error: function (response) {
              ep.logger.error('error posting item to cart: ' + response);
            }
          });

        }
        else {
          ep.logger.warn('add to cart called with no quantity');
        }

      }
    };
    /* ********** CHOOSE PAYMENT METHOD EVENT LISTENERS ************ */
    /**
     * Make a ajax POST request to cortex server with given action links, and trigger corresponding events
     * in case of success or error. Start activity indicator in affected regions.
     *
     * @param actionLink url to post request to server with.
     */
    function updateChosenPaymentMethod(actionLink) {
      if (actionLink) {
        var ajaxModel = new ep.io.defaultAjaxModel({
          type: 'POST',
          url: actionLink,
          success: function() {
            EventBus.trigger('checkout.updateChosenPaymentMethodSuccess');
          },
          customErrorFn: function() {
            EventBus.trigger('checkout.updateChosenPaymentMethodFailed');
          }
        });

        ep.io.ajax(ajaxModel.toJSON());

        ep.ui.startActivityIndicator(checkoutLayout.paymentMethodsRegion.currentView);
        ep.ui.startActivityIndicator(checkoutLayout.checkoutOrderRegion.currentView);
      }
      else {
        ep.logger.error("Trying to update chosen payment method without action link");
      }
    }
  EventBus.on('payment.deletePaymentRequest', function (opts) {
    if (_.isObject(opts)) {
      // Build AJAX request
      var ajaxModel = new ep.io.defaultAjaxModel({
        type: 'DELETE',
        url: opts.href,
        success: function () {
          EventBus.trigger('payment.deletePaymentSuccess', opts.indicatorView);
        },
        customErrorFn: function (response) {
          EventBus.trigger('payment.deletePaymentFailed', {
            status: response.status,
            responseText: response.responseText
          }, opts.indicatorView);
        }
      });

      // Send AJAX request to Cortex
      ep.io.ajax(ajaxModel.toJSON());
    } else {
      ep.logger.error('deletepaymentRequest event triggered without a valid options object');
    }
  });
    /* ********** CHOOSE SHIPPING OPTION EVENT LISTENERS ************ */
    /**
     * Make a ajax POST request to cortex server with given action links, and trigger corresponding events
     * in case of success or error. Start activity indicator in affected regions.
     *
     * @param actionLink url to post request to server with.
     */
    function updateChosenShippingOption(actionLink) {
      if (actionLink) {
        var ajaxModel = new ep.io.defaultAjaxModel({
          type: 'POST',
          url: actionLink,
          success: function() {
            EventBus.trigger('checkout.updateChosenShippingOptionSuccess');
          },
          customErrorFn: function() {
            EventBus.trigger('checkout.updateChosenShippingOptionFailed');
          }
        });

        ep.io.ajax(ajaxModel.toJSON());

        refreshShippingAddressViews();
        ep.ui.startActivityIndicator(checkoutLayout.shippingOptionsRegion.currentView);
        ep.ui.startActivityIndicator(checkoutLayout.checkoutOrderRegion.currentView);
      }
      else {
        ep.logger.error("Trying to update chosen shipping option without action link");
      }
    }
    /**
     * Make request to server to update cart-lineItem quantity.
     * @param actionLink  link to post request to.
     * @param qty         new qty to update with.
     */
    function updateLineItemQty(actionLink, qty) {
      if (reportMissingArgs(arguments, ['actionLink', 'quantity'])) {
        return;
      }

      var ajaxModel = new ep.io.defaultAjaxModel({
        type: 'PUT',
        url: actionLink,
        data: "{quantity:" + qty.changeTo + "}",
        success: function () {
          EventBus.trigger('cart.updateLineItemQtySuccess');
        },
        customErrorFn: function (response) {
          if (response.status === 404) {  // lineItem to update doesn't exist
            EventBus.trigger('cart.updateLineItemQtyFailed.ItemDeleted');
          }
          else {
            EventBus.trigger('cart.updateLineItemQtyFailed', qty.original);
          }
        }
      });

      ep.io.ajax(ajaxModel.toJSON());
    }
define(function (require) {
  var ep = require('ep');
  var Backbone = require('backbone');
  var ModelHelper = require('modelHelpers');

  // Array of zoom parameters to pass to Cortex
  var zoomArray = [
    'subscriptions:element',
    'purchases:element',
    'addresses:element',
    'paymentmethods:element'
  ];

  var urlBase = ep.io.getApiContext() + '/profiles/' + ep.app.config.cortexApi.scope + '/default';

  /**
   * Model containing profile information of a registered user.
   * @type Backbone.Model
   */
  var profileModel = Backbone.Model.extend({
    baseUrl: urlBase,
    url: urlBase + '?zoom=' + zoomArray.join(),
    parse: function (response) {
      var profileObj = {
        personalInfo: {},
        subscriptions: [],
        purchaseHistories: [],
        addresses: [],
        paymentMethods: []
      };

      if (response) {
        // Profile Personal Info
        profileObj.personalInfo = {
          familyName : jsonPath(response, 'family-name')[0],
          givenName : jsonPath(response, 'given-name')[0],
          actionLink: jsonPath(response, 'self.href')[0]
        };

        // Payment methods (tokenized only)
        // Only select payment methods with a display-value property (credit cards do not have this property)
        var paymentMethodsArray = jsonPath(response, "$._paymentmethods.._element[?(@.self.type=='application/vnd.elasticpath.paymenttoken')]");
        if(paymentMethodsArray) {
          profileObj.paymentMethods = modelHelpers.parseArray(paymentMethodsArray, modelHelpers.parseTokenPayment);
        }

        // Profile Subscription Info
        var subscriptionsArray = jsonPath(response, '$._subscriptions.._element')[0];
        if (subscriptionsArray) {
          profileObj.subscriptions = modelHelpers.parseArray(subscriptionsArray, modelHelpers.parseSubscription);
        }

        // Profile Purchase History
        var purchaseHistoryArray = jsonPath(response, '$._purchases.._element')[0];
        if (purchaseHistoryArray) {
          profileObj.purchaseHistories = modelHelpers.parseArray(purchaseHistoryArray, modelHelpers.parsePurchaseHistory);
        }

        // Profile addresses
        var addressesArray = jsonPath(response, '$._addresses.._element')[0];
        if (addressesArray) {
          profileObj.addresses = modelHelpers.parseArray(addressesArray, modelHelpers.parseAddress);
        }
      }
      else {
        ep.logger.error("Profile model wasn't able to fetch valid data for parsing. ");
      }

      return profileObj;
    }
  });

  /**
   * Model that returns the action href to which the new payment method form should be submitted.
   * This href can be used either:
   * - when payment methods are added from profile
   * - when payment methods are added from checkout AND the shopper has chosen to save the payment method to profile
   * @type Backbone.Model
   */
  var profilePaymentMethodActionModel = Backbone.Model.extend({
    url: urlBase + '?zoom=paymentmethods:paymenttokenform',
    parse: function (response) {
      if (response) {
        // parse the JSON response and return the href if one is found
        return {
          url: jsonPath(response, "$..links[?(@.rel=='createpaymenttokenaction')].href")[0]
        };
      }
      ep.logger.error("Unable to fetch profile model payment method action link.");
      return false;
    }
  });

  /**
   * Placeholder to keep purchase collection for keeping track of model changes.
   * Do not fetch or parse data itself, Will have data passed in.
   * @type Backbone.Collection
   */
  var profilePurchaseCollection = Backbone.Collection.extend();

  /**
   * Collection of helper functions to parse the model.
   * @type Object collection of modelHelper functions
   */
  var modelHelpers = ModelHelper.extend({
    /**
     * Parses 1 purchase history record with purchase number, date, total, and status.
     * @param rawObject raw JSON object of 1 purchase history record
     * @returns Object  1 parsed purchase history record
     */
    parsePurchaseHistory: function(rawObject) {
      var purchaseRecord = {};

      if(rawObject) {
        purchaseRecord = {
          purchaseNumber: jsonPath(rawObject, '$.purchase-number')[0],
          date: {
            displayValue: jsonPath(rawObject, '$.purchase-date..display-value')[0],
            value: jsonPath(rawObject, '$.purchase-date..value')[0]
          },
          total: modelHelpers.parsePrice(jsonPath(rawObject, '$.monetary-total[0]')[0]),
          status: jsonPath(rawObject, '$.status')[0],
          link: jsonPath(rawObject, '$.self..href')[0]
        };
      }
      else {
        ep.logger.warn('Error building purchase record object: raw purchase record object was undefined.');
      }

      return purchaseRecord;
    }
  });

  var __test_only__ = {
    modelHelpers: modelHelpers
  };

  return {
    /* test-code */
    testVariable: __test_only__,
    /* end-test-code */

    ProfileModel: profileModel,
    ProfilePaymentMethodActionModel: profilePaymentMethodActionModel,
    ProfilePurchaseCollection: profilePurchaseCollection
  };
});
define(function (require) {
    var ep = require('ep');
    var Backbone = require('backbone');
    var ModelHelper = require('modelHelpers');

    // Array of zoom parameters to pass to Cortex
    var zoomArray = [
      'total',
      'lineitems:element',
      'lineitems:element:price',
      'lineitems:element:total',
      'lineitems:element:rate',
      'lineitems:element:availability',
      'lineitems:element:item',
      'lineitems:element:item:definition',
      'lineitems:element:item:definition:assets:element',
      'lineitems:element:item:rate',
      'lineitems:element:item:code',
      'lineitems:element:appliedpromotions:element',
      'appliedpromotions',
      'order',
      'order:purchaseform',
      'order:deliveries:element:shippingoptioninfo'
    ];

    // Cart Model
    var cartModel = Backbone.Model.extend({
      url: ep.io.getApiContext() +
            '/carts/' +
            ep.app.config.cortexApi.scope +
            '/default?zoom=' +
            zoomArray.join(),

      parse: function (response) {

        var cartObj = {};

        /*
         * Cart-lineitems
         */
        var lineItemsArray = [];
        var lineItemArrayLen = 0;
        var lineItemsRoot = jsonPath(response, '$._lineitems.._element')[0];

        // Applied promotions
        var appliedPromotions = "";

        if (lineItemsRoot) {
          lineItemArrayLen = lineItemsRoot.length;
        }

        // Iterate over Lineitems
        for (var x = 0; x < lineItemArrayLen; x++) {
          var currObj = lineItemsRoot[x];
          var lineItemObj = {};

          /*
           * item image by sku
           */
          var assetObj = {};
          var skuName = jsonPath(currObj, "$.['_code'][0]['code']")[0];
          assetObj.absolutePath = ep.app.config.skuImagesS3Url.replace("%sku%",skuName)
          assetObj.name = 'default-image'
          lineItemObj.thumbnail = assetObj;

          /*
           * item display name
           */
          lineItemObj.displayName = jsonPath(currObj, '$._item.._definition..display-name')[0];
          lineItemObj.itemLink = jsonPath(currObj, '$._item..self.href')[0];

          /*
           * availability
           */
          var availabilityObj = jsonPath(currObj, '$._availability')[0];
          lineItemObj.availability = parseAvailability(availabilityObj);

          /*
           * quantity
           */
          lineItemObj.quantity = currObj.quantity;

          /*
           * item unit price
           */
          lineItemObj.unitPrice = {};

          var itemUnitPurchasePrice = jsonPath(currObj, '$._price..purchase-price[0]')[0];
          if (itemUnitPurchasePrice) {
            lineItemObj.unitPrice.purchase = modelHelpers.parsePrice(itemUnitPurchasePrice);
          }

          var itemUnitListPrice = jsonPath(currObj, '$._price..list-price[0]')[0];
          if (itemUnitListPrice) {
            lineItemObj.unitPrice.listed = modelHelpers.parseListPrice(itemUnitListPrice, lineItemObj.unitPrice.purchase);
          }

          /*
           * line-item applied promotions
           */
          var lineItemAppliedPromotion= jsonPath(currObj, '$._appliedpromotions.._element[0].display-name')[0];
          if (lineItemAppliedPromotion) {
            appliedPromotions = appliedPromotions + lineItemAppliedPromotion + "<br/> ";
          }

          /*
           * item-total (list price & purchase price)
           */
          lineItemObj.price = {};

          var lineItemTotal = jsonPath(currObj, '$._total..cost[0]')[0];
          if (lineItemTotal) {
            lineItemObj.price.purchase = modelHelpers.parsePrice(lineItemTotal);
          }

          /*
           * Rates
           */
          // Item unit rates
          var itemRates = jsonPath(currObj, '$._item.._rate..rate')[0];
          lineItemObj.unitRateCollection = parseRates(itemRates);

          // LineItem rates
          var lineItemRates = jsonPath(currObj, '$._rate..rate')[0];
          lineItemObj.rateCollection = parseRates(lineItemRates);

          // fake a price object when neither rate nor price present
          if (!lineItemTotal && lineItemObj.rateCollection.length === 0) {
            lineItemObj.price.purchase = {
              display: 'none'
            };

            lineItemObj.unitPrice.purchase = {
              display: 'none'
            };
          }

          /*
           * LineItem Link (for remove lineitem button)
           */
          lineItemObj.lineitemLink = currObj.self.href;

          lineItemsArray.push(lineItemObj);
        }
        cartObj.lineItems = lineItemsArray;


        /*
         * Cart Summary: total quantity
         */
        cartObj.cartTotalQuantity = jsonPath(response, '$.total-quantity')[0];

         /*
         * Cart applied promotions
         */
        var cartAppliedPromotion = jsonPath(response, '$._appliedpromotions.._element[0].display-name')[0];
        if (cartAppliedPromotion) {
          appliedPromotions = appliedPromotions + cartAppliedPromotion + "<br/> ";
        }
        cartObj.appliedPromotions = appliedPromotions

        /*
         * Shipping applied promotions
         */
        var cartAppliedPromotion = jsonPath(response, '$._shippingoption.._appliedpromotions.._element[0].display-name')[0];
        if (cartAppliedPromotion) {
          appliedPromotions = appliedPromotions + cartAppliedPromotion + "<br/> ";
        }

        cartObj.appliedPromotions = appliedPromotions

        /*
         * Cart Summary: total price (excluding tax)
         */
        cartObj.cartTotal = {};
        var cartTotal = jsonPath(response, '$._total..cost[0]')[0];
        if (cartTotal) {
          cartObj.cartTotal = modelHelpers.parsePrice(cartTotal);
        }


        /*
         * Cart Submit Order Action
         * */
        cartObj.submitOrderActionLink = jsonPath(response, "$..links[?(@.rel=='submitorderaction')].href")[0];

        cartObj.checkoutLink = jsonPath(response, '$.._order[0].self.href')[0];

        return cartObj;
      }
    });

    var cartItemModel = Backbone.Model.extend();
    var cartItemCollection = Backbone.Collection.extend({
      model: cartItemModel,
      parse: function (collection) {
        return collection;
      }
    });



    var modelHelpers = ModelHelper.extend({});

    // function to parse default image
    var parseDefaultImg = function(imgObj) {
      var defaultImg = {};

      if (imgObj) {
        defaultImg = {
          absolutePath: imgObj['content-location'],
          relativePath: imgObj['relative-location'],
          name: imgObj.name
        };
      }

      return defaultImg;
    };

    // function to parse availability (states and release-date)
    var parseAvailability = function(availabilityObj) {
      var availability = {};

      if (availabilityObj) {
        availability.state = jsonPath(availabilityObj, '$..state')[0];
        var releaseDate = jsonPath(availabilityObj, '$..release-date')[0];
        if (releaseDate) {
          availability.releaseDate = {
            displayValue: releaseDate['display-value'],
            value: releaseDate.value
          };
        }
      }

      return availability;
    };

    // function to parse one-time price (list or purchase)
    var parsePrice = function(priceObj) {
      var price = {};

      if (priceObj) {
        price = {
          currency: priceObj[0].currency,
          amount: priceObj[0].amount,
          display: priceObj[0].display
        };
      }

      return price;
    };

    // function to parse rates collection
    var parseRates = function(rates) {
      var ratesArrayLen = 0;
      var rateCollection = [];

      if (rates) {
        ratesArrayLen = rates.length;
      }

      for (var i = 0; i < ratesArrayLen; i++) {
        var rateObj = {};

        rateObj.display = rates[i].display;
        rateObj.cost = {
          amount: jsonPath(rates[i], '$.cost..amount')[0],
          currency: jsonPath(rates[i], '$.cost..currency')[0],
          display: jsonPath(rates[i], '$.cost..display')[0]
        };

        rateObj.recurrence = {
          interval: jsonPath(rates[i], '$.recurrence..interval')[0],
          display: jsonPath(rates[i], '$.recurrence..display')[0]
        };

        rateCollection.push(rateObj);
      }

      return rateCollection;
    };

    return {
      CartModel:cartModel,
      CartItemCollection:cartItemCollection,
      CartItemModel:cartItemModel
    };
  }