<?php
/**
 * @file
 * Commerce checkout progress module.
 *
 * This module provides a block that is enabled by default and displayed on
 * checkout pages. The block is a list with each checkout page title as an item.
 * The active checkout page is designated with an "active" class. The purpose is to
 * visually communicate what part of the checkout process a customer is on.
 */

/**
 * Implements hook_block_info().
 */
function commerce_checkout_progress_block_info() {
  return array(
    'indication' => array(
      'info' => t('Checkout progress indication'),
      'cache' => DRUPAL_NO_CACHE,
      'status' => 1, // If you enabled this module, you want the block
      'region' => 'content',
      'weight' => -10, // on top
      'visibility' => BLOCK_VISIBILITY_LISTED, // Show on only the listed pages
      'pages' => "checkout/*\ncart",
    )
  );
}

/**
 * Implements hook_block_view().
 */
function commerce_checkout_progress_block_view($delta = 'indication') {
  if ($delta === 'indication') {
    // Get the current page that was created/stored earlier by commerce checkout.
    $page_id = commerce_checkout_progress_commerce_checkout_router();

    if (empty($page_id) && variable_get('commerce_checkout_progress_cart', FALSE) && $_GET['q'] = 'cart') {
      $page_id = 'cart';
    }

    $block_pages = variable_get('commerce_checkout_progress_block_pages', array());

    // on block configuration page, use select nothing, it mean, we need
    // render the progress bar on all checkout pages.
    $visible = empty($block_pages);

    // user selected some pages
    if (!$visible) {
      // if current page checkout page ID in configured-pages, the progress bar
      // is visible
      foreach ($block_pages as $block_page) {
        if ($block_page == $page_id) {
          $visible = TRUE;
          break;
        }
      }
    }

    if ($visible) {
      // Get non-empty pages.
      $pages = commerce_checkout_progress_get_items();
      $content = array(
        '#theme' => 'commerce_checkout_progress_list',
        '#items' => $pages,
        '#type' => variable_get('commerce_checkout_progress_list_type', 'ol'),
        '#current_page' => $page_id,
        '#link' => variable_get('commerce_checkout_progress_link', TRUE),
      );
      return array(
        'subject' => '',
        'content' => $content,
      );
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Overrule the pages-visibility settings form.
 *   Only for the blocks this module defines.
 */
function commerce_checkout_progress_form_block_admin_configure_alter(&$form, $form_state, $form_id) {
  if ($form['module']['#value'] === 'commerce_checkout_progress') {
    module_load_include('admin.inc', 'commerce_checkout_progress');
    return _commerce_checkout_progress_form_block_admin_configure_alter($form, $form_state, $form_id);
  }
}

/**
 * Implements hook_commerce_checkout_router().
 *
 * Tells us what checkout page we are on. It's called by commerce_checkout first
 * to set the static variable.  This module calls it again to retrieve static
 * variable value. This is being done instead of using arg(2) since the first
 * checkout page doesn't have an arg(2).
 *
 * @see commerce_checkout_router()
 *  The function performs module_invoke_all('commerce_checkout_router', $order, $checkout_page);
 *
 * @return (text)
 *   Page ID of the current checkout page
 */
function commerce_checkout_progress_commerce_checkout_router($order = NULL, $checkout_page = NULL) {
  // There's still no official way to get the current checkout page ID.
  // The function is called before block rendering function.
  // avoid save value to user session, we save it to this function static
  // variable.
  static $page_id = NULL;

  if (isset($checkout_page['page_id'])) {
    $page_id = $checkout_page['page_id'];
  }

  return $page_id;
}

/**
 * Implements hook_theme().
 */
function commerce_checkout_progress_theme($existing, $type, $theme, $path) {
  return array(
    'commerce_checkout_progress_list' => array(
      'variables' => array(
        'items'       => NULL,
        'type'        => NULL,
        'attributes'  => array(),
        'link'        => TRUE,
        'current_page' => NULL,
      ),
    ),
  );
}

/**
 * Theming function for checkout progress item list.
 *
 * @param $variables
 *   An associative array containing:
 *   - items: Array of items to be displayed in the list. The key is
 *     the Page ID.  The value is the Page Title.
 *   - type: The type of list to return (e.g. "ul", "ol").
 *   - attributes: The attributes applied to the list element.
 *   - link: (bool) List should contain links to previously visited pages.
 *   - current_page: The page ID of the current page.
 */
function theme_commerce_checkout_progress_list($variables) {
  $path = drupal_get_path('module', 'commerce_checkout_progress');
  drupal_add_css($path . '/commerce_checkout_progress.css');

  extract($variables);

  // Option to display back pages as links.
  if ($link) {
    // Load the *shopping cart* order. It gets deleted on last page.
    if ($order = commerce_cart_order_load($GLOBALS['user']->uid)) {
      $order_id = $order->order_id;
    }
  }

  // This is where we build up item list that will be themed
  // This variable is used with $variables['link'], see more in inside comment.
  $visited = TRUE;
  // Our list of progress pages.
  $progress = array();
  foreach ($items as $page_id => $page) {
    $class = array();
    if ($page_id === $current_page) {
      $class[] = 'active';
      // Active page and next pages should not be linked.
      $visited = FALSE;
    }
    if ($page_id === $items[$current_page]['prev_page']) {
      $class[] = 'previous';
    }
    if ($page_id === $items[$current_page]['next_page']) {
      $class[] = 'next';
    }
    // @TODO: Calculate width based on 100 / qty of pages.
    $class[] = $page_id;
    $data    = t($page['title']);

    if ($visited) {
      $class[] = 'visited'; // Issue #1345942.

      // On checkout complete page, the checkout order is deleted.
      if (isset($order_id) && $order_id) {
        // If a user is on step 1, clicking a link next steps will be redirect them back.
        // Only render the link on the pages those user has already been on.
        // Make sure the loaded order is the same one found in the URL.
        if (arg(1) == $order_id) {
          $href = isset($page['href']) ? $page['href'] : "checkout/{$order_id}/{$page_id}";
          $data = l(filter_xss($data), $href, array('html' => TRUE));
        }
      }
    }

    $item = array(
      'data'  => $data,
      'class' => $class,
    );
    // Only set li title if the page has help text.
    if (isset($page['help'])) {
      //#1322436 Filter help text to be sure it contains NO html.
      $help = strip_tags($page['help']);
      // Make sure help has text event after filtering html.
      if (!empty($help)) {
        $item['title'] = $help;
      }
    }
    // Add item to progress array.
    $progress[] = $item;
  }

  return theme('item_list', array(
    'items' => $progress,
    'type' => $type,
    'attributes' => array('class' => array('commerce-checkout-progress', 'clearfix', 'inline',)),
  ));
}

/**
 * Implements hook_form_FORM_ID_alter().
 * 
 * @see commerce_checkout_progress_get_items().
 */
function commerce_checkout_progress_form_commerce_checkout_builder_form_alter(&$form, $form_state, $form_id) {
  $form['commerce_checkout_progress'] = array(
    '#type' => 'fieldset',
    '#title' => t('Commerce Checkout Progress'),
    'commerce_checkout_progress_list_type' => array(
      '#type' => 'radios',
      '#title' => t('List type'),
      '#options' => array(
        'ul' => t('Unordered list'),
        'ol' => t('Ordered list'),
      ),
      '#default_value' => variable_get('commerce_checkout_progress_list_type', 'ol'),
    ),
    'commerce_checkout_progress_link' => array(
      '#type' => 'checkbox',
      '#title' => t('Render list as links'),
      '#default_value' => variable_get('commerce_checkout_progress_link', TRUE),
    ),
    'commerce_checkout_progress_cart' => array(
      '#type' => 'checkbox',
      '#title' => t('Include a Cart link as the first step'),
      '#default_value' => variable_get('commerce_checkout_progress_cart', FALSE),
    ),
  );
  $form['actions']['#weight'] = 10;
  $form['actions']['submit']['#submit'][] = 'commerce_checkout_progress_form_commerce_checkout_builder_form_submit';
}

/**
 * Custom submit function to save module settings after form submit.
 * 
 * @see commerce_checkout_progress_form_commerce_checkout_builder_form_alter().
 * @see commerce_checkout_progress_get_items().
 */
function commerce_checkout_progress_form_commerce_checkout_builder_form_submit($form, $form_state) {
  // save module settings
  variable_set('commerce_checkout_progress_list_type', $form_state['values']['commerce_checkout_progress_list_type']);
  variable_set('commerce_checkout_progress_link', $form_state['values']['commerce_checkout_progress_link']);
  variable_set('commerce_checkout_progress_cart', $form_state['values']['commerce_checkout_progress_cart']);
}

/**
 * Get checkout pages that have content and do not redirect.
 * @see commerce_checkout_progress_form_commerce_checkout_builder_form_alter().
 */
function commerce_checkout_progress_get_items() {
  // get checkout pages & panes
  $pages = commerce_checkout_pages();
  $panes = commerce_checkout_panes();

  foreach ($panes as $id => $pane) {
    // Panes could exist in removed pages (through
    // hook_commerce_checkout_page_info_alter()).
    if (!isset($pages[$pane['page']])) {
      continue;
    }

    // special case at commerce_payment_redirect page, this page is not empty
    // but in some cases, it just redirects user to next step.
    // code in this block copied from commerce_payment_redirect_pane_checkout_form().
    // See commerce_payment_redirect_pane_checkout_form() for more detail.
    if ($id === 'commerce_payment_redirect') {
      $order = menu_get_object('commerce_order');
      if (!empty($order->data['payment_method'])) {
        $payment_method = commerce_payment_method_instance_load($order->data['payment_method']);
        if (!empty($payment_method['offsite'])) {
          if (arg(3) !== 'return' || arg(4) == $order->data['payment_redirect_key']) {
            $callback = commerce_payment_method_callback($payment_method, 'redirect_form');
            if ($callback) {
              $pages[$pane['page']]['has_item'] = TRUE;
            }
          }
        }
      }
      continue;
    }

    // pane has page ID, use this value to flag that the page is not empty.
    $pages[$pane['page']]['has_item'] = TRUE;
  }

  $items = array();
  foreach (array_keys($pages) as $page_id) {
    if (!empty($pages[$page_id]['has_item'])) {
      if ($page_id !== 'disabled') {
        $items[$page_id] = $pages[$page_id];
      }
    }
  }

  if (variable_get('commerce_checkout_progress_cart', FALSE) && module_exists('commerce_cart')) {
    reset($items);
    $first = key($items);
    $items[$first]['prev_page'] = 'cart';
    $items = array_reverse($items);
    $items['cart'] = array(
      'title' => t('Cart'),
      'weight' => -60,
      'status_cart' => TRUE,
      'href' => 'cart',
      'prev_page' => NULL,
      'next_page' => $first,
    );
    $items = array_reverse($items);
  }

  return $items;
}
