<?php

/**
 * @file
 * Ajax add to cart module.
 *
 * Allows you to add products AJAX-ically to cart. You can remove products
 * AJAX-ically from cart.
 */

// Variable constants.
define('DC_AJAX_ADD_CART_CHECKOUT_REDIRECT', 'dc_ajax_add_cart_checkout_redirect');
define('DC_AJAX_ADD_CART_SHOW_LABELS', 'dc_ajax_add_cart_show_labels');
define('DC_AJAX_ADD_CART_EMPTY_CART_MESSAGE', 'dc_ajax_add_cart_empty_cart_message');
define('DC_AJAX_ADD_CART_REMOVE_CART', 'dc_ajax_add_cart_remove_cart');
define('DC_AJAX_ADD_CART_HIDE_EMPTY_CART', 'dc_ajax_add_cart_hide_empty_cart');

define('DC_AJAX_ADD_CART_EMPTY_CART_TEASER_MESSAGE', 'dc_ajax_add_cart_empty_cart_teaser_message');
define('DC_AJAX_ADD_CART_ITEM_SUFFIX_TEXT', 'dc_ajax_add_cart_item_suffix_text');

define('DC_AJAX_ADD_CART_DISPLAY_POPUP', 'dc_ajax_add_cart_display_popup');
define('DC_AJAX_ADD_CART_SUCCESS_MESSAGE', 'dc_ajax_add_cart_success_message');
define('DC_AJAX_ADD_CART_POPUP_PRODUCT_NAME_DISPLAY', 'dc_ajax_add_cart_popup_product_name_display');
define('DC_AJAX_ADD_CART_POPUP_PRODUCT_NAME_LABEL', 'dc_ajax_add_cart_popup_product_name_label');
define('DC_AJAX_ADD_CART_POPUP_PRODUCT_QUANTITY_DISPLAY', 'dc_ajax_add_cart_popup_product_quantity_display');
define('DC_AJAX_ADD_CART_POPUP_PRODUCT_QUANTITY_LABEL', 'dc_ajax_add_cart_popup_product_quantity_label');
define('DC_AJAX_ADD_CART_POPUP_PRODUCT_PRICE_DISPLAY', 'dc_ajax_add_cart_popup_product_price_display');
define('DC_AJAX_ADD_CART_POPUP_PRODUCT_PRICE_LABEL', 'dc_ajax_add_cart_popup_product_price_label');
define('DC_AJAX_ADD_CART_POPUP_PRODUCT_TOTAL_DISPLAY', 'dc_ajax_add_cart_popup_product_total_display');
define('DC_AJAX_ADD_CART_POPUP_PRODUCT_TOTAL_LABEL', 'dc_ajax_add_cart_popup_product_total_label');
define('DC_AJAX_ADD_CART_POPUP_CHECKOUT', 'dc_ajax_add_cart_popup_checkout');
define('DC_AJAX_ADD_CART_POPUP_CONTINUE_SHOPPING', 'dc_ajax_add_cart_popup_continue_shopping');

/**
 * Implements hook_permission().
 */
function dc_ajax_add_cart_permission() {
  return array(
    'administer commerce ajax add to cart' => array(
      'title' => t('Administer Commerce AJAX Add to Cart'),
      'description' => t('Administer Commerce AJAX Add to Cart'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function dc_ajax_add_cart_menu() {
  $items = array();

  $items['remove-product/nojs/%'] = array(
    'title' => 'Remove commerce line item',
    'description' => 'Remove commerce line item',
    'page callback' => 'dc_ajax_add_cart_remove_commerce_line_item',
    'page arguments' => array(1, 2),
    'access arguments' => array('view any commerce_product entity'),
    'type' => MENU_CALLBACK,
  );

  $items['remove-product/ajax/%'] = array(
    'delivery callback' => 'ajax_deliver',
  ) + $items['remove-product/nojs/%'];

  $items['admin/commerce/config/ajax-cart'] = array(
    'title' => 'AJAX Shopping Cart',
    'description' => 'AJAX Shopping Cart',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('dc_ajax_add_cart_ajax_cart_settings_form'),
    'access arguments' => array('administer commerce ajax add to cart'),
    'file' => 'dc_ajax_add_cart.admin.inc',
    'type' => MENU_NORMAL_ITEM,
    'weight' => 10,
  );

  $items['admin/commerce/config/ajax-cart/default'] = array(
    'title' => 'General settings',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file' => 'dc_ajax_add_cart.admin.inc',
  );

  $items['admin/commerce/config/ajax-cart/cart-teaser'] = array(
    'title' => 'Shopping cart teaser',
    'description' => 'Shopping cart teaser',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('dc_ajax_add_cart_cart_teaser_settings_form'),
    'access arguments' => array('administer commerce ajax add to cart'),
    'file' => 'dc_ajax_add_cart.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 20,
  );

  $items['admin/commerce/config/ajax-cart/pop-up-message'] = array(
    'title' => 'Pop up Message',
    'description' => 'Pop up Message',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('dc_ajax_add_cart_popup_message_settings_form'),
    'access arguments' => array('administer commerce ajax add to cart'),
    'file' => 'dc_ajax_add_cart.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 30,
  );

  return $items;
}

/**
 * Menu callback: Removes the product item from cart.
 *
 * @param string $ajax
 *   Page argument, ajaxifies the removal of product from cart.
 * @param int $line_item_id
 *   Line item ID.
 *
 * @return array
 *   Ajax command array for product removal.
 *
 * @see dc_ajax_add_cart_menu()
 */
function dc_ajax_add_cart_remove_commerce_line_item($ajax, $line_item_id) {
  // Initially quantity of products in cart will be zero.
  $quantity = 0;

  // Check whether this is AJAX callback.
  $is_ajax = $ajax === 'ajax';

  // Delete the product line item from cart.
  commerce_line_item_delete($line_item_id);

  // Get the current status of cart and other information.
  $commerce_cart = dc_ajax_add_cart_get_commerce_cart_details();
  $line_items = $commerce_cart['wrapper']->commerce_line_items;
  $quantity = commerce_line_items_quantity($line_items, commerce_product_line_item_types());
  $total = commerce_line_items_total($line_items);

  if ($is_ajax) {
    // Update the cart content.
    $commands = array();

    // Check whether the user has any product in cart.
    if (variable_get(DC_AJAX_ADD_CART_HIDE_EMPTY_CART, 0) && ($quantity == 0)) {
      $content_cart = theme('html_tag', array(
        'element' => array(
          '#tag' => 'div',
          '#attributes' => array(
            'class' => 'ajax-shopping-cart-wrapper',
          ),
        ),
      ));
    }
    else {
      $content_cart = theme('dc_ajax_shopping_cart', array(
        'order' => $commerce_cart['order'],
        'line_items' => $line_items,
        'quantity' => $quantity,
        'total' => $total,
      ));
    }
    $commands[] = ajax_command_replace('div.ajax-shopping-cart-wrapper', $content_cart);

    $content_teaser = theme('dc_ajax_shopping_cart_teaser', array(
      'order' => $commerce_cart['order'],
      'quantity' => $quantity,
      'total' => $total,
    ));
    $commands[] = ajax_command_replace('div.ajax-shopping-cart-teaser', $content_teaser);

    // Ajaxifies the updation of cart.
    return array(
      '#type' => 'ajax',
      '#commands' => $commands,
    );
  }
  else {
    drupal_set_message(t('Product removed from cart'));
    drupal_goto();
  }
}

/**
 * Implements hook_form_alter().
 */
function dc_ajax_add_cart_form_alter(&$form, &$form_state, $form_id) {
  if (strpos($form_id, 'commerce_cart_add_to_cart_form') !== FALSE) {
    $form['submit']['#ajax'] = array(
      'callback' => 'dc_ajax_add_cart_ajax_cart_form',
      'method' => 'replace',
      'effect' => 'slide',
    );
    $form['#attached']['css'][] = drupal_get_path('module', 'dc_ajax_add_cart') . '/css/dc_ajax_add_cart.css';
    $form['#attached']['js'][] = drupal_get_path('module', 'dc_ajax_add_cart') . '/js/dc_ajax_add_cart.js';

    // Rebuild form.
    $form['#submit'][] = 'dc_ajax_add_cart_rebuild_add_to_cart_form';
  }
}

/**
 * AJAX-ify the product add to cart.
 *
 * AJAX callback for all Commerce add to cart form.
 *
 * @ingroup callbacks
 */
function dc_ajax_add_cart_ajax_cart_form(&$form, &$form_state) {
  // Get the current status of commerce cart.
  $commerce_cart = dc_ajax_add_cart_get_commerce_cart_details();

  // AJAX commands array.
  $commands = array();

  // If the user has ordered items.
  if ($commerce_cart['order']) {
    // Get the line items in cart with their quantity and total.
    $line_items = $commerce_cart['wrapper']->commerce_line_items;
    $quantity = commerce_line_items_quantity($line_items, commerce_product_line_item_types());
    $total = commerce_line_items_total($line_items);

    $ajax_shopping_cart_content = theme('dc_ajax_shopping_cart', array(
      'order' => $commerce_cart['order'],
      'line_items' => $line_items,
      'quantity' => $quantity,
      'total' => $total,
    ));
    $commands[] = ajax_command_replace('div.ajax-shopping-cart-wrapper', $ajax_shopping_cart_content);

    // Update the contents of shopping cart.
    $ajax_shopping_cart_teaser_content = theme('dc_ajax_shopping_cart_teaser', array(
      'order' => $commerce_cart['order'],
      'quantity' => $quantity,
      'total' => $total,
    ));
    $commands[] = ajax_command_replace('div.ajax-shopping-cart-teaser', $ajax_shopping_cart_teaser_content);

    // Display add to cart message.
    if (variable_get(DC_AJAX_ADD_CART_DISPLAY_POPUP, 'display_popup_message') == 'display_popup_message') {
      // Gather information to display product information in popup message.
      $last_line_item = $form_state['line_item'];
      $last_line_item_product = commerce_product_load($form_state['values']['product_id']);
      $last_line_item_quantity = $form_state['values']['quantity'];

      $content = theme('dc_ajax_add_to_cart_message', array(
        'line_item' => $last_line_item,
        'product' => $last_line_item_product,
        'quantity' => $last_line_item_quantity,
      ));
      $commands[] = ajax_command_prepend('body', $content);
    }

    return array(
      '#type' => 'ajax',
      '#commands' => $commands,
    );
  }
}

/**
 * Implements hook_block_info().
 */
function dc_ajax_add_cart_block_info() {
  $blocks['ajax_shopping_cart'] = array(
    'info' => t('AJAX shopping cart'),
    'cache' => DRUPAL_NO_CACHE,
  );

  $blocks['ajax_shopping_cart_teaser'] = array(
    'info' => t('AJAX shopping cart teaser'),
    'cache' => DRUPAL_NO_CACHE,
  );

  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function dc_ajax_add_cart_block_view($delta = '') {
  $block = array();

  switch ($delta) {
    case 'ajax_shopping_cart':
      // Get the current status of cart and other details.
      $commerce_cart = dc_ajax_add_cart_get_commerce_cart_details();
      $hide_empty_cart = FALSE;

      if ($commerce_cart['order']) {
        $line_items = $commerce_cart['wrapper']->commerce_line_items;
        $quantity = commerce_line_items_quantity($line_items, commerce_product_line_item_types());
        $total = commerce_line_items_total($line_items);

        if (variable_get(DC_AJAX_ADD_CART_HIDE_EMPTY_CART, 0) && ($quantity == 0)) {
          $hide_empty_cart = TRUE;
        }
      }
      else {
        $line_items = NULL;
        $quantity = 0;
        $total = NULL;

        if (variable_get(DC_AJAX_ADD_CART_HIDE_EMPTY_CART, 0)) {
          $hide_empty_cart = TRUE;
        }
      }

      // Hide cart if it is empty.
      if ($hide_empty_cart) {
        $block['content'] = theme('html_tag', array(
          'element' => array(
            '#tag' => 'div',
            '#attributes' => array(
              'class' => 'ajax-shopping-cart-wrapper',
            ),
          ),
        ));
      }
      else {
        $block['subject'] = t('Shopping cart');

        $block['content'] = array(
          '#theme' => 'dc_ajax_shopping_cart',
          '#order' => $commerce_cart['order'],
          '#line_items' => $line_items,
          '#quantity' => $quantity,
          '#total' => $total,
        );

        drupal_add_library('system', 'drupal.ajax', TRUE);
        drupal_add_css(drupal_get_path('module', 'dc_ajax_add_cart') . '/css/dc_ajax_add_cart.css');
      }

      break;

    case 'ajax_shopping_cart_teaser':
      // Get the current status of cart and other details.
      $commerce_cart = dc_ajax_add_cart_get_commerce_cart_details();

      if ($commerce_cart['order']) {
        $line_items = $commerce_cart['wrapper']->commerce_line_items;
        $quantity = commerce_line_items_quantity($line_items, commerce_product_line_item_types());
        $total = commerce_line_items_total($line_items);
      }
      else {
        $line_items = NULL;
        $quantity = 0;
        $total = NULL;
      }

      $block['content'] = array(
        '#theme' => 'dc_ajax_shopping_cart_teaser',
        '#order' => $commerce_cart['order'],
        '#quantity' => $quantity,
        '#total' => $total,
      );

      drupal_add_css(drupal_get_path('module', 'dc_ajax_add_cart') . '/css/dc_ajax_add_cart.css');

      break;
  }

  return $block;
}

/**
 * This function returns the current status of cart.
 *
 * @return array
 *   Array containing the current status of cart.
 */
function dc_ajax_add_cart_get_commerce_cart_details() {
  global $user;
  $output = array();

  $order = commerce_cart_order_load($user->uid);

  if ($order) {
    $wrapper = entity_metadata_wrapper('commerce_order', $order);

    $output['order'] = $order;
    $output['wrapper'] = $wrapper;
  }
  else {
    $output['order'] = NULL;
    $output['wrapper'] = NULL;
  }

  return $output;
}

/**
 * Implements hook_theme().
 */
function dc_ajax_add_cart_theme() {
  return array(
    'dc_ajax_shopping_cart' => array(
      'variables' => array(
        'order' => NULL,
        'line_items' => NULL,
        'quantity' => NULL,
        'total' => NULL,
      ),
      'template' => 'templates/dc-ajax-shopping-cart',
    ),
    'dc_ajax_shopping_cart_teaser' => array(
      'variables' => array(
        'order' => NULL,
        'quantity' => NULL,
        'total' => NULL,
      ),
      'template' => 'templates/dc-ajax-shopping-cart-teaser',
    ),
    'dc_ajax_add_to_cart_message' => array(
      'variables' => array(
        'line_item' => NULL,
        'product' => NULL,
        'quantity' => NULL,
      ),
      'template' => 'templates/dc-ajax-add-to-cart-message',
    ),
  );
}

/**
 * Add more variables to shopping cart block.
 *
 * Default template: ajax-shopping-cart.tpl.php.
 *
 * @param array $variables
 *   An associative array containing:
 *   - order: Order object of the current user.
 *   - line_items: Line items wrapper.
 *   - quantity: Number of items in the cart.
 *   - total: Array containing the total amount and the default currency you
 *     are in the site.
 */
function template_preprocess_dc_ajax_shopping_cart(&$variables) {
  // Check whether order object exists.
  if ($variables['order']) {
    $header = array();
    $row = array();
    $image_remove_cart_path = drupal_get_path('module', 'dc_ajax_add_cart') . '/images/remove-from-cart.png';
    $image_remove_cart = theme('image', array(
      'path' => $image_remove_cart_path,
      'alt' => t('Remove from cart'),
      'title' => t('Remove from cart'),
    ));

    // Default currency array.
    $currency = commerce_currency_load(commerce_default_currency());

    // Output arrays.
    $line_item_list = array();
    $products = array();
    $product_prices = array();
    $shipping = array();

    $product_price_total = commerce_currency_format($variables['total']['amount'], $currency['code']);

    // Create the line item list and products array.
    foreach ($variables['line_items'] as $line_item_wrapper) {
      $line_item = $line_item_wrapper->value();

      // If the line item is a product.
      if (property_exists($line_item, 'commerce_product')) {
        $product = commerce_product_load($line_item->commerce_product[LANGUAGE_NONE][0]['product_id']);
        $products[$product->product_id] = $product;
        $product_prices[$product->product_id] = commerce_currency_format($line_item->commerce_unit_price[LANGUAGE_NONE][0]['amount'], $currency['code']);
      }
      // If line item is shipping service.
      elseif (property_exists($line_item, 'commerce_shipping_service')) {
        $shipping['service'] = $line_item->line_item_label;
        $shipping['price'] = commerce_currency_format($line_item->commerce_unit_price[LANGUAGE_NONE][0]['amount'], $currency['code']);
      }

      $line_item_list[$line_item->line_item_id] = $line_item;
    }

    // Create checkout url.
    if (variable_get(DC_AJAX_ADD_CART_CHECKOUT_REDIRECT, 'cart_page') == 'cart_page') {
      $checkout_url = l(t('Checkout'), 'cart');
    }
    else {
      $checkout_url = l(t('Checkout'), 'checkout');
    }

    // Shopping cart.
    if (variable_get(DC_AJAX_ADD_CART_SHOW_LABELS, 'label') == 'label') {
      $header = array(
        array('data' => t('Quantity'), 'class' => array('quantity-label')),
        array('data' => t('Items'), 'class' => array('item-label')),
        array('data' => t('Price'), 'class' => array('price-label')),
        '',
      );
    }

    foreach ($line_item_list as $line_item) {
      if (property_exists($line_item, 'commerce_product')) {
        $product = commerce_product_load($line_item->commerce_product[LANGUAGE_NONE][0]['product_id']);

        if (variable_get('dc_ajax_add_cart_update_quantity', 0) == 1) {
          $updateable_quantity = drupal_get_form('dc_ajax_add_cart_update_quantity_' . $line_item->line_item_id, $line_item);
          $quantity = drupal_render($updateable_quantity);
        }
        else {
          $quantity = intval($line_item->quantity);
        }

        $row[] = array(
          array('data' => $quantity, 'class' => array('quantity')),
          array('data' => $product->title, 'class' => array('name')),
          array('data' => $product_prices[$product->product_id], 'class' => array('price')),
          array(
            'data' => l(variable_get(DC_AJAX_ADD_CART_REMOVE_CART, 'link') == 'link' ? t('Remove from cart') : $image_remove_cart,
            'remove-product/nojs/' . $line_item->line_item_id,
            array(
              'attributes' => array(
                'class' => array('use-ajax'),
              ), 'html' => TRUE)), 'class' => array('remove-from-cart')),
        );
      }
    }

    // Create new theme variables.
    $variables['line_item_list'] = $line_item_list;
    $variables['products'] = $products;
    $variables['product_prices'] = $product_prices;
    $variables['product_price_total'] = $product_price_total;
    $variables['checkout_url'] = $checkout_url;
    $variables['products_list_html'] = theme('table', array(
      'header' => $header,
      'rows' => $row,
      'attributes' => array(
        'class' => array('ajax-shopping-cart-table'),
      ),
      'sticky' => FALSE,
    ));
    if (!empty($shipping)) {
      $variables['shipping'] = $shipping;
    }
  }

  // Create new theme variables related to configuration.
  $variables['configuration']['show_labels'] = variable_get(DC_AJAX_ADD_CART_SHOW_LABELS, 'label');
  $variables['configuration']['remove_cart'] = variable_get(DC_AJAX_ADD_CART_REMOVE_CART, 'link');
  $variables['configuration']['empty_cart_message'] = check_plain(variable_get(DC_AJAX_ADD_CART_EMPTY_CART_MESSAGE, t('Shopping cart is empty')));
}

/**
 * Add more variables to shopping cart teaser.
 *
 * Default template: ajax-shopping-cart-teaser.tpl.php.
 *
 * @param array $variables
 *   An associative array containing:
 *   - order: Order object for the current user.
 *   - quantity: Number of items present in the cart.
 *   - total: Array containing the total amount and default currency code used
 *     in the site.
 */
function template_preprocess_dc_ajax_shopping_cart_teaser(&$variables) {
  // Default currency array.
  $currency = commerce_currency_load(commerce_default_currency());
  $image_cart_icon_path = drupal_get_path('module', 'dc_ajax_add_cart') . '/images/shopping-cart.png';

  $variables['total_amount'] = commerce_currency_format($variables['total']['amount'], $currency['code']);
  $variables['cart_icon'] = theme('image', array(
    'path' => $image_cart_icon_path,
  ));

  // Create new theme variables related to configuration.
  $variables['configuration']['empty_cart_teaser_message'] = check_plain(variable_get(DC_AJAX_ADD_CART_EMPTY_CART_TEASER_MESSAGE, t('Empty')));
  $cart_link_text = format_plural($variables['quantity'], '1 ' . check_plain(variable_get(DC_AJAX_ADD_CART_ITEM_SUFFIX_TEXT, t('item'))), '@count ' . check_plain(variable_get(DC_AJAX_ADD_CART_ITEM_SUFFIX_TEXT, t('items'))));
  $variables['cart_link'] = l($cart_link_text, 'cart', array('attributes' => array('class' => array('quantity'))));
}

/**
 * Add more variables to add to cart message popup message.
 *
 * Default template: add-to-cart-message.tpl.php.
 *
 * @param array $variables
 *   An associative array containing:
 *   - order: Order object for the current user.
 *   - quantity: Number of items present in the cart.
 *   - total: Array containing the total amount and default currency code used
 *     in the site.
 */
function template_preprocess_dc_ajax_add_to_cart_message(&$variables) {
  // Default currency array.
  $currency = commerce_currency_load(commerce_default_currency());

  $product_price = commerce_product_calculate_sell_price($variables['product']);

  $variables['product_per_unit_price'] = commerce_currency_format($product_price['amount'], $currency['code']);
  $variables['product_price_total'] = commerce_currency_format($variables['quantity'] * $product_price['amount'], $currency['code']);

  // Create new theme variables related to configuration.
  $variables['configuration']['success_message'] = check_plain(variable_get(DC_AJAX_ADD_CART_SUCCESS_MESSAGE, t('Item successfully added to cart')));
  $variables['configuration']['popup_checkout'] = check_plain(variable_get(DC_AJAX_ADD_CART_POPUP_CHECKOUT, t('Go to checkout')));
  $variables['checkout_link'] = l(variable_get(DC_AJAX_ADD_CART_POPUP_CHECKOUT, t('Go to checkout')), 'cart');
  $variables['configuration']['popup_continue_shopping'] = check_plain(variable_get(DC_AJAX_ADD_CART_POPUP_CONTINUE_SHOPPING, t('Continue shopping')));
  $variables['configuration']['popup_product_name_display'] = variable_get(DC_AJAX_ADD_CART_POPUP_PRODUCT_NAME_DISPLAY, 1);
  $variables['configuration']['popup_product_name_label'] = variable_get(DC_AJAX_ADD_CART_POPUP_PRODUCT_NAME_LABEL, 'no_display_label');
  $variables['product_name'] = check_plain($variables['product']->title);
  $variables['configuration']['popup_product_price_display'] = variable_get(DC_AJAX_ADD_CART_POPUP_PRODUCT_PRICE_DISPLAY, 1);
  $variables['configuration']['popup_product_price_label'] = variable_get(DC_AJAX_ADD_CART_POPUP_PRODUCT_PRICE_LABEL, 'display_label');
  $variables['configuration']['popup_product_quantity_display'] = variable_get(DC_AJAX_ADD_CART_POPUP_PRODUCT_QUANTITY_DISPLAY, 1);
  $variables['configuration']['popup_product_quantity_label'] = variable_get(DC_AJAX_ADD_CART_POPUP_PRODUCT_QUANTITY_LABEL, 'display_label');
  $variables['configuration']['popup_product_total_display'] = variable_get(DC_AJAX_ADD_CART_POPUP_PRODUCT_TOTAL_DISPLAY, 1);
  $variables['configuration']['popup_product_total_label'] = variable_get(DC_AJAX_ADD_CART_POPUP_PRODUCT_TOTAL_LABEL, 'display_label');
}

/**
 * Rebuild add to cart form.
 *
 * @ingroup forms
 */
function dc_ajax_add_cart_rebuild_add_to_cart_form($form, &$form_state) {
  $form_state['rebuild'] = TRUE;
}

/**
 * Update line item quantity.
 *
 * @param object $line_item
 *   Line item.
 *
 * @see dc_ajax_add_cart_update_quantity_refresh()
 *
 * @ingroup forms
 */
function dc_ajax_add_cart_update_quantity($form, &$form_state, $line_item) {
  $form = array();

  $form['quantity_' . $line_item->line_item_id] = array(
    '#type' => 'textfield',
    '#size' => 3,
    '#maxlength' => 3,
    '#default_value' => intval($line_item->quantity),
  );

  $form['submit_' . $line_item->line_item_id] = array(
    '#type' => 'submit',
    '#value' => t('Update'),
    '#line_item_id' => $line_item->line_item_id,
    '#ajax' => array(
      'callback' => 'dc_ajax_add_cart_update_quantity_refresh',
      'method' => 'replace',
    ),
  );

  $form['#submit'][] = 'dc_ajax_add_cart_rebuild_update_quantity_form';

  return $form;
}

/**
 * Callback that will update the quantity of line item.
 *
 * @ingroup callbacks
 */
function dc_ajax_add_cart_update_quantity_refresh($form, &$form_state) {
  // AJAX commands array.
  $commands = array();

  // If altered quantity is not zero, then update line item's quantity.
  // Otherwise delete line item.
  if ($form_state['values']['quantity_' . $form_state['triggering_element']['#line_item_id']] != 0) {
    $line_item = commerce_line_item_load($form_state['triggering_element']['#line_item_id']);
    $line_item->quantity = $form_state['values']['quantity_' . $form_state['triggering_element']['#line_item_id']];
    commerce_line_item_save($line_item);
  }
  else {
    commerce_line_item_delete($form_state['triggering_element']['#line_item_id']);
  }

  // Get the current status of commerce cart.
  $commerce_cart = dc_ajax_add_cart_get_commerce_cart_details();

  // If the user has ordered items.
  if ($commerce_cart['order']) {
    // Get the line items in cart with their quantity and total.
    $line_items = $commerce_cart['wrapper']->commerce_line_items;
    $quantity = commerce_line_items_quantity($line_items, commerce_product_line_item_types());
    $total = commerce_line_items_total($line_items);

    $ajax_shopping_cart_content = theme('dc_ajax_shopping_cart', array(
      'order' => $commerce_cart['order'],
      'line_items' => $line_items,
      'quantity' => $quantity,
      'total' => $total,
    ));
    $commands[] = ajax_command_replace('div.ajax-shopping-cart-wrapper', $ajax_shopping_cart_content);

    // Update the contents of shopping cart.
    $ajax_shopping_cart_teaser_content = theme('dc_ajax_shopping_cart_teaser', array(
      'order' => $commerce_cart['order'],
      'quantity' => $quantity,
      'total' => $total,
    ));
    $commands[] = ajax_command_replace('div.ajax-shopping-cart-teaser', $ajax_shopping_cart_teaser_content);

    return array(
      '#type' => 'ajax',
      '#commands' => $commands,
    );
  }
}

/**
 * Rebuild update quantity form.
 *
 * @ingroup forms
 */
function dc_ajax_add_cart_rebuild_update_quantity_form($form, &$form_state) {
  $form_state['rebuild'] = TRUE;
}

/**
 * Implements hook_forms().
 */
function dc_ajax_add_cart_forms($form_id, $args) {
  $forms = array();

  if (strpos($form_id, 'dc_ajax_add_cart_update_quantity_') !== FALSE) {
    $forms[$form_id] = array(
      'callback' => 'dc_ajax_add_cart_update_quantity',
    );
  }

  return $forms;
}
