<?php

/**
 * @file
 * Commerce Product Reference Flag module.
 */

/**
 * Implements hook_field_extra_fields().
 *
 * This implementation will define "extra fields" on entities with product
 * reference fields to correspond with enabled flag extra fields on products.
 * These fields will then also be present in the entity view.
 */
function commerce_product_reference_flag_field_extra_fields() {
  $flag_fields = flag_field_extra_fields();

  // Only look at product flags.
  if (empty($flag_fields['commerce_product'])) {
    return array();
  }

  $extra = array();

  // Loop through the product reference fields.
  foreach (commerce_info_fields('commerce_product_reference') as $field_name => $field) {
    foreach ($field['bundles'] as $entity_type => $bundles) {
      if ($entity_type == 'commerce_line_item' || $entity_type == 'commerce_product') {
        // We do not currently support the injection of product fields into the
        // display of line items or other products.
        continue;
      }

      foreach ($bundles as $bundle_name) {
        // Get the instance settings for the field on this entity bundle.
        $instance_settings = field_info_instance($entity_type, $field['field_name'], $bundle_name);

        // If field injection is turned off for this instance, skip adding the
        // flag fields to this bundle's view modes.
        if (empty($instance_settings['settings']['field_injection'])) {
          continue;
        }

        foreach ($flag_fields['commerce_product'] as $key => $value) {
          foreach ($value['display'] as $product_flag_name => $product_flag) {
            $product_flag['label'] = t('Product: @label', array('@label' => $product_flag['label']));

            $product_flag['display']['default'] = array(
              'weight' => 0,
              'visible' => FALSE,
            );

            $extra[$entity_type][$bundle_name]['display']['product:' . $product_flag_name] = $product_flag;
          }
        }
      }
    }
  }

  return $extra;
}

/**
 * Implements hook_entity_view().
 *
 * This implementation adds flag fields to the content array of an entity on
 * display if the product contains a non-empty product reference field.
 *
 * @see commerce_product_reference_entity_view()
 */
function commerce_product_reference_flag_entity_view($entity, $entity_type, $view_mode, $langcode) {
  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
  $instances = NULL;

  if ($entity_type == 'commerce_line_item' || $entity_type == 'commerce_product') {
    // We do not currently support the injection of product fields into the
    // display of line items or other products.
    return;
  }

  // An entity metadata wrapper will be loaded on demand if it is determined
  // this entity has a product reference field instance attached to it.
  $wrapper = NULL;

  // Loop through product reference fields to see if any exist on this entity
  // bundle that is either hidden or displayed with the Add to Cart form display
  // formatter.
  foreach (commerce_info_fields('commerce_product_reference', $entity_type) as $field_name => $field) {
    // Load the instances array only if the entity has product reference fields.
    if (empty($instances)) {
      $instances = field_info_instances($entity_type, $bundle);
    }

    if (isset($instances[$field_name])) {
      // Load a wrapper for the entity being viewed.
      if (empty($wrapper)) {
        $wrapper = entity_metadata_wrapper($entity_type, $entity);
      }

      // Find the default product based on the cardinality of the field.
      $product = NULL;

      if ($field['cardinality'] == 1) {
        $product = $wrapper->{$field_name}->value();
      }
      elseif (count($wrapper->{$field_name}) > 0) {
        $product = commerce_product_reference_default_product($wrapper->{$field_name}->value());

        // If the product is disabled, attempt to find one that is active and
        // use that as the default product instead.
        if (!empty($product) && $product->status == 0) {
          foreach ($wrapper->{$field_name} as $delta => $product_wrapper) {
            if ($product_wrapper->status->value() != 0) {
              $product = $product_wrapper->value();
              break;
            }
          }
        }
      }

      // If we found a product and the reference field enables field injection...
      if (!empty($product) && $instances[$field_name]['settings']['field_injection']) {
        // Add the display context for these field to the product.
        $product->display_context = array(
          'entity_type' => $entity_type,
          'entity_id' => $id,
          'entity' => $entity,
          'view_mode' => $view_mode,
          'language' => $langcode,
        );

        // Provide the referencing entity context.
        $entity_uri = entity_uri($entity_type, $entity);

        $link_variables = array(
          'context' => array(
            'display_path' => $entity_uri['path'],
            'entity' => array(
              'entity_type' => $entity_type,
              'entity_id' => $id,
              'product_reference_field_name' => $field_name,
            ),
          ),
        );

        // Determine if the referenced product type specifies custom settings
        // for the reference view mode.
        $view_mode_settings = field_view_mode_settings('commerce_product', $product->type);

        if (!empty($view_mode_settings[$entity_type . '_' . $view_mode]['custom_settings'])) {
          $reference_view_mode = $entity_type . '_' . $view_mode;
        }
        else {
          $reference_view_mode = 'default';
        }

        // Attach flag "extra fields" to the bundle representing all the extra fields
        // currently attached to products.
        foreach (field_info_extra_fields('commerce_product', $product->type, 'display') as $product_flag_name => $product_flag) {
          $display = field_extra_fields_get_display('commerce_product', $product->type, $reference_view_mode);

          if (strpos($product_flag_name, 'flag_') !== 0) {
            continue;
          }

          $flag_name = substr($product_flag_name, 5);

          // Only include flag fields that are visible on the current view mode.
          if (!empty($display[$product_flag_name]['visible'])) {
            // Add the product flag field to the entity's content array.
            $content_key = 'product:' . $product_flag_name;

            $entity->content[$content_key]['#markup'] = flag_create_link($flag_name, $product->product_id, $link_variables);

            // For multiple value references, add context information so the cart
            // form can do dynamic replacement of fields.
            if ($field['cardinality'] != 1) {
              // Construct an array of classes that will be used to theme and
              // target the rendered field for AJAX replacement.
              $classes = array(
                'commerce-product-extra-field',
                drupal_html_class('commerce-product-extra-field-' . $product_flag_name),
                drupal_html_class(implode('-', array($entity_type, $id, 'product', $product_flag_name))),
              );

              // Add an extra class to distinguish empty fields.
              if (empty($entity->content[$content_key])) {
                $classes[] = 'commerce-product-extra-field-empty';
              }

              // Ensure the flag field's content array has a prefix and suffix key.
              $entity->content[$content_key] += array(
                '#prefix' => '',
                '#suffix' => '',
              );

              // Add the custom div before and after the prefix and suffix.
              $entity->content[$content_key]['#prefix'] = '<div class="' . implode(' ', $classes) . '">' . $entity->content[$content_key]['#prefix'];
              $entity->content[$content_key]['#suffix'] .= '</div>';
            }
          }
        }
      }
    }
  }
}

/**
 * Implements hook_commerce_cart_attributes_refresh_alter().
 *
 * @see commerce_cart_add_to_cart_form_attributes_refresh()
 */
function commerce_product_reference_flag_commerce_cart_attributes_refresh_alter(&$commands, $form, $form_state) {
  // Then render and return the various product fields that might need to be
  // updated on the page.
  if (!empty($form_state['context'])) {
    // Copy the original destination into the form display context.
  //  $form_state['context']['commerce_product_reference_flag_destination'] = $form_state['values']['commerce_product_reference_flag_destination'];
   if (isset($form_state['values']['commerce_product_reference_flag_destination'])) {
      $form_state['context']['commerce_product_reference_flag_destination'] = $form_state['values']['commerce_product_reference_flag_destination'];
    }

    $product = $form_state['default_product'];
    $product->display_context = $form_state['context'];

    $link_variables = array(
      'context' => array(
        'display_path' => $form_state['line_item']->data['context']['display_path'],
        'entity' => $form_state['line_item']->data['context']['entity'],
      ),
    );

    // Then render the flag fields defined for the referenced product.
    foreach (field_info_extra_fields('commerce_product', $product->type, 'display') as $product_flag_name => $product_flag) {
      $display = field_extra_fields_get_display('commerce_product', $product->type, $form_state['context']['view_mode']);

      if (strpos($product_flag_name, 'flag_') !== 0) {
        continue;
      }

      $flag_name = substr($product_flag_name, 5);
      $link_variables['context']['destination'] = $form_state['values']['commerce_product_reference_flag_destination_' . $flag_name];

      // Only include flag fields that are visible on the current view mode.
      if (!empty($display[$product_flag_name]['visible'])) {
        // Rebuild the same array of classes used when the field was first rendered.
        $replacement_class = drupal_html_class(implode('-', array($form_state['context']['class_prefix'], 'product', $product_flag_name)));

        $classes = array(
          'commerce-product-extra-field',
          drupal_html_class('commerce-product-extra-field-' . $product_flag_name),
          $replacement_class,
        );

        // Theme the product flag field to $element.
        $element = array(
          '#markup' => flag_create_link($flag_name, $product->product_id, $link_variables),
          '#prefix' => '<div class="' . implode(' ', $classes) . '">',
          '#suffix' => '</div>',
        );

        // Add an extra class to distinguish empty fields.
        if (empty($element['#markup'])) {
          $classes[] = 'commerce-product-extra-field-empty';
        }

        $commands[] = ajax_command_replace('.' . $replacement_class, drupal_render($element));
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter(): commerce_cart_add_to_cart_form.
 *
 * Injects the current destination path into the add to cart form for later use
 * as the flag link destination.
 */
function commerce_product_reference_flag_form_commerce_cart_add_to_cart_form_alter(&$form, &$form_state) {
 // $product = $form_state['default_product'];
 $product = isset($form_state['default_product']) ? $form_state['default_product'] : NULL;

 // if (!empty($form_state['context'])) {
	if (!empty($form_state['context']) && isset($product)) {
 
    // Inject the destination parameters of all available flag links so that we
    // can retrieve these values if they get altered during the AJAX cart refresh.
    foreach (field_info_extra_fields('commerce_product', $product->type, 'display') as $product_flag_name => $product_flag) {
      $display = field_extra_fields_get_display('commerce_product', $product->type, $form_state['context']['view_mode']);

      if (strpos($product_flag_name, 'flag_') !== 0) {
        continue;
      }

      $flag_name = substr($product_flag_name, 5);

      // Only include flag fields that are visible on the current view mode.
      if (!empty($display[$product_flag_name]['visible'])) {
        $product_id = $product->product_id;
        $flag = flag_get_flag($flag_name);
        $link_type = $flag->get_link_type();
        $action = $flag->is_flagged($product_id) ? 'unflag' : 'flag';
        $link = module_invoke($link_type['module'], 'flag_link', $flag, $action, $product_id);

        $key = 'commerce_product_reference_flag_destination_' . $flag_name;

        if (!empty($form_state['values'][$key])) {
          $path = $form_state['values'][$key];
        }
        else {
          $path = !empty($link['query']['destination']) ? $link['query']['destination'] : '';
        }

        $form[$key] = array(
          '#type' => 'hidden',
          '#value' => $path,
        );
      }
    }
  }
}

/**
 * Implements hook_preprocess_flag().
 */
function commerce_product_reference_flag_preprocess_flag(&$variables) {
  $flag = $variables['flag'];
  $action = $variables['action'];
  $entity_id = $variables['entity_id'];
  $display_context = &$variables['context'];

  // Do not change the link of any non-product flags or if the flag is not
  // set to display as a field.
  if ($flag->entity_type != 'commerce_product' || !$flag->show_as_field) {
    return;
  }

  $link = &$variables['link'];

  if (!empty($link['query']['destination'])) {
    // Some flag links use drupal_get_destination() which returns an invalid
    // path during the cart AJAX refresh. So if an AJAX request was made we
    // replace the destination with the value injected during the initial form
    // load.
    if ($link['query']['destination'] == 'system/ajax') {
      if (!empty($display_context['destination'])) {
        $link['query']['destination'] = $display_context['destination'];
      }
    }
  }

  // At this point the link is now the same as it used to be on page load.
  $context = array(
    'action' => $action,
    'entity_id' => $entity_id,
    'display_context' => $display_context,
  );

  // Allow other modules to alter the product flag link based on the display
  // context.
  drupal_alter('commerce_product_reference_flag_link', $link, $flag, $context);

  // Now update $variables where the altered link affects it.
  // @see template_preprocess_flag().
  if (isset($link['title']) && empty($link['html'])) {
    $link['title'] = check_plain($link['title']);
  }

  // Replace the link with the access denied text if unable to flag.
  if ($action == 'unflag' && !$flag->access($entity_id, 'unflag')) {
    $link['title'] = $flag->get_label('unflag_denied_text', $entity_id);
    unset($link['href']);
  }

  $variables['link_href'] = isset($link['href']) ? check_url(url($link['href'], $link)) : FALSE;
  $variables['link_text'] = isset($link['title']) ? $link['title'] : $flag->get_label($action . '_short', $entity_id);
  $variables['link_title'] = isset($link['attributes']['title']) ? check_plain($link['attributes']['title']) : check_plain(strip_tags($flag->get_label($action . '_long', $entity_id)));

  if (isset($link['href'])) {
    $variables['flag_classes_array'][] = $variables['action'] . '-action';
    $variables['flag_classes_array'][] = 'flag-link-' . $flag->link_type;
  }
  else {
    $variables['flag_classes_array'][] = $variables['action'] . '-disabled';
  }
  if (isset($link['attributes']['class'])) {
    $link['attributes']['class'] = is_string($link['attributes']['class']) ? array_filter(explode(' ', $link['attributes']['class'])) : $link['attributes']['class'];
    $variables['flag_classes_array'] = array_merge($variables['flag_classes_array'], $link['attributes']['class']);
  }
}
