<?php

/**
 * @file
 * Defines a field type for referencing an Amazon product.
 */

 /**
 * Implementation of hook_menu().
 */
function asin_menu() {
  $items = array();
  $items['asin/autocomplete'] = array(
    'page callback' => 'asin_autocomplete_callback',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
  );
  return $items;
}


/**
 * Implementation of hook_field_info().
 */
function asin_field_info() {
  return array(
    'asin' => array(
      'label' => t('Amazon item'),
      'description' => t('Store the id of a product listed on Amazon.com.'),
      'default_widget' => 'asin_text',
      'default_formatter' => 'asin_default',
    ),
  );
}


///**
// * @todo: This is the leftover from D6 hook_field. Seems that there's some
// * views stuff yet to be done?
// */
//function asin_field_settings($op, $field) {
//// TODO: Views data stuff
//      $data = content_views_field_views_data($field);
//      $db_info = content_database_info($field);
//      $table_alias = content_views_tablename($field);
//
//      // Filter: Add a 'many to one' filter.
//      $copy = $data[$table_alias][$field['field_name'] . '_asin'];
//      $copy['title'] = t('@label (!name) - Allowed values', array('@label' => $field['widget']['label'], '!name' => $field['field_name']));
//      $copy['filter']['handler'] = 'views_handler_filter_many_to_one';
//      unset($copy['field'], $copy['argument'], $copy['sort']);
//      $data[$table_alias][$field['field_name'] . '_value_many_to_one'] = $copy;
//      // Argument : swap the handler to the 'many to one' operator.
//      $data[$table_alias][$field['field_name'] . '_value']['argument']['handler'] = 'views_handler_argument_many_to_one';
//
//      // Add a relationship for related node.
//      $data[$table_alias][$field['field_name'] . '_asin']['relationship'] = array(
//        'base' => 'amazon_item',
//        'field' => $db_info['columns']['asin']['column'],
//        'handler' => 'views_handler_relationship',
//      );
//      return $data;
//}


/**
 * Trim spaces from the front/back of each ASIN in an array.
 *
 * @param $items
 *   Array of ASINS
 * @return
 *   Same array of ASINs, trimmed
 */
function _asin_trim_items(&$items) {
  foreach ($items as $delta => &$item) {
    $item = trim($item);
  }
}

/**
 * Implements of hook_field_is_empty().
 */
function asin_field_is_empty($item, $field) {
  if (empty($item['asin'])) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Implementation of hook_field_formatter_info().
 */
function asin_field_formatter_info() {
  return array(
    'asin_default' => array(
      'label' => t('Thumbnail with title'),
      'field types' => array('asin'),
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
    ),
    'asin_details' => array(
      'label' => t('Thumbnail with details'),
      'field types' => array('asin'),
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
    ),
    'asin_thumbnail' => array(
      'label' => t('Thumbnail image'),
      'field types' => array('asin'),
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
    ),
    'asin_medium' => array(
      'label' => t('Medium image'),
      'field types' => array('asin'),
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
    ),
    'asin_large' => array(
      'label' => t('Large image'),
      'field types' => array('asin'),
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
    ),
    'asin_inline' => array(
      'label' => t('Link to item'),
      'field types' => array('asin'),
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function asin_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();

  foreach ($items as $delta => $value) {
    $asin = trim($value['asin']);
    if (!empty($asin)) {
      $lookup = amazon_item_lookup($asin);
      if (!empty($lookup) && $item = $lookup[$asin]) {
        // TODO: kill off amazon_inline_item. There's no reason for it to clutter the earth.
        $theme_function = $display['type'] == 'asin_inline' ? 'amazon_inline_item' : 'amazon_item';
        // Trim the 'asin_' prefix from the formatter machine name before
        // passing it to the theme function.
        if (strpos($display['type'], 'asin_') === 0) {
          $style = substr($display['type'], 5);
        }
        $element[$delta] = array('#markup' => theme($theme_function, array('item' => $item, 'style' => $style)));
      }
    }
  }
  return $element;
}


/**
 * Implementation of hook_field_widget_info().
 *
 */
function asin_field_widget_info() {
  return array(
    'asin_text' => array(
      'label' => t('Amazon ASIN Text field'),
      'field types' => array('asin'),
    ),
    'asin_autocomplete' => array(
      'label' => t('Product name autocomplete'),
      'field types' => array('asin'),
    ),
  );
}

/**
 * Implements hook_field_widget_form().
 *
 */
function asin_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $base) {
  $element = $base;
  $element['asin'] = $base + array(
    '#type' => 'textfield',
    '#default_value' => !empty($items[$delta]['asin']) ? $items[$delta]['asin'] : '',
  );
  switch ($instance['widget']['type']) {
    case 'asin_text':
      $element['asin']['#size'] = 25;
      $element['asin']['#element_validate'] = array('asin_field_widget_element_validate');
      break;

    case 'asin_autocomplete':
      $element['asin']['#autocomplete_path'] = 'asin/autocomplete';
      $element['asin']['#value_callback'] = 'asin_autocomplete_value';
      $element['asin']['#maxlength'] = 256;
      $element['asin']['#element_validate'][] = 'asin_autocomplete_validate';
      break;
  }

  $form_state['langcode'] = $langcode;
  return $element;
}

/**
 * Widget validation function.
 *
 * Checks to see if we can look up an ASIN, URL, or ISBN-13 using the
 * provided text. If we can, it's OK and we turn it into an ASIN.
 * Otherwise, flag as error.
 */
function asin_field_widget_element_validate($element, &$form_state) {
  $field_name = $element['#field_name'];
  $langcode = $form_state['langcode'];

  foreach ($form_state['values'][$field_name][$langcode] as $delta => &$item) {
    $asin = trim(amazon_convert_to_asin($item['asin']));
    if (!empty($asin) && is_numeric($delta)) {
      $results = amazon_item_lookup(array($asin));
      if (empty($results)) {
        form_set_error("{$field_name}][{$langcode}][{$delta}][asin", t('No Amazon product with the ASIN "%id" could be located.', array('%id' => $asin)));
      }
      else {
        $item['asin'] = (string)key($results);
      }
    }
  }
}


/**
 * Autocomplete callback for the asin_autocomplete widget.
 */
function asin_autocomplete_callback($string = '') {
  $items = $matches = array();
  // Search Amazon.
  $parameters = array(
    'ResponseGroup' => 'Small',
    'SearchIndex' => 'All',
    'Keywords' => urlencode($string)
  );
  $results = amazon_http_request('ItemSearch', $parameters);
  // Process the results.
  foreach($results->Items->Item as $xml) {
    $items[(string) $xml->ASIN] = (string) $xml->ItemAttributes->Title . ' (' . $xml->ItemAttributes->ProductGroup . ')';
  }
  // Create our response.
  foreach ($items as $asin => $title) {
    // Add a class wrapper for a few required CSS overrides.
    $matches[$title . ' [asin:' . $asin . ']'] = '<div class="reference-autocomplete">'. $title . '</div>';
  }
  drupal_json_output($matches);
}

/**
 * Validate the autocomplete widget.
 *
 * This corrects the value (we want just the ASIN, not the whole title).
 */
function asin_autocomplete_validate($element, &$form_state) {
  $value = $element['#value'];
  $asin = NULL;
  if (preg_match('!.*\[asin\:([a-zA-Z0-9]*)\]!', $value, $matches)) {
    $asin = $matches[1];
  }
  else {
    $asin = $value;
  }
  form_set_value($element, $asin, $form_state);
}


/**
 * Implements hook_field_views_data().
 *
 * Through this hook we're given the chance to change the views schema
 * data for the asin field. The primary thing to be done is to add a join
 * on the ASIN type to the amazon_item views base stuff.
 */
function asin_field_views_data($field) {
  $data = field_views_field_default_views_data($field);

  foreach ($data as $table_name => $table_data) {
    foreach ($table_data as $field_name => $field_data) {
      // Check for fieldapi value fields.
      if (isset($field_data['filter']['field_name'])) {
        $data[$table_name][$field_name]['relationship'] = array(
          'handler' => 'views_handler_relationship',
          'base' => 'amazon_item',
          'base_field' => 'asin',
          'label' => t('ASIN from !field_name', array('!field_name' => $field['field_name'])),
        );
      }
    }
  }
  return $data;
}

/**
 * Implementation of hook_feeds_node_processor_targets_alter().
 */
function asin_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
  foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) {
    $info = field_info_field($name);
    if ($info['type'] == 'asin') {
      $targets[$name] = array(
        'name' => $bundle_name . ':' . $name,
        'callback' => 'asin_feeds_set_target',
        'description' => t('The %name field of the %bundle', array('%name' => $name, '%bundle' => $bundle_name)),
        'real_target' => $name,
      );
    }
  }
}

/**
 * Callback for mapping. Here is where the actual mapping happens.
 *
 * When the callback is invoked, $target contains the name of the field the
 * user has decided to map to and $value contains the value of the feed item
 * element the user has picked as a source.
 *
 * @see my_module_set_target() in feeds.api.php
 */
function asin_feeds_set_target($source, $entity, $target, $value) {
  if (!isset($value)) {
    return;
  }

  // Handle non-multiple value fields.
  if (!is_array($value)) {
    $value = array($value);
  }

  // Iterate over all values.
  $i = 0;
  $info = field_info_field($target);
  $field_name = $target;
  foreach ($value as $v) {
    $field[LANGUAGE_NONE][$i]['asin'] = $v;
    if ($info['cardinality'] == 1) {
      break;
    }
    $i++;
  }
  $entity->{$field_name} = $field;
}


/**
 * Implements hook_devel_generate().
 * Callback for populating ASIN fields with devel_generate module.
 *
 * Picks a random computer-related book.
 */
function asin_devel_generate($object, $field, $instance, $bundle) {
  if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) {
    return devel_generate_multiple('_asin_devel_generate', $object, $field, $instance, $bundle);
  }
  else {
    return _asin_devel_generate($object, $field, $instance, $bundle);
  }
}

/**
 * Utility function that actually provides the values for asin_devel_generate().
 *
 * You can change the SearchIndex and Keywords used for the search by changing
 * the variables amazon_devel_generate_search_index and
 * amazon_devel_generate_keywords.
 */
function _asin_devel_generate($object, $field, $instance, $bundle) {
  $search_index = variable_get('amazon_devel_generate_search_index', 'All');
  $keywords_picker = explode(',', variable_get('asin_devel_generate_keywords', 'computer'));
  $keywords_picker = array_flip($keywords_picker);

  $asins = variable_get('amazon_devel_generate_asins', array());
  $amazon_item_page = variable_get('amazon_devel_generate_item_page', 1);

  if (empty($asins)) {
    $parameters = array(
      'ResponseGroup' => 'ItemAttributes,EditorialReview,Images',
      'SearchIndex' => $search_index,
      'Keywords' => urlencode(array_rand($keywords_picker, 1)),
      'ItemPage' => $amazon_item_page % 10,   // 10 is now max pages from amazon
    );
    $amazon_item_page++;

    $results = amazon_http_request('ItemSearch', $parameters);
    foreach ($results->Items->Item as $xml) {
      $item = amazon_item_clean_xml($xml);
      amazon_item_insert($item);
      $asins[] = $item['asin'];
    }
  }

  $field = array();
  //$field['asin'] = array_shift($asins);
  $asin = array_shift($asins);
  $field['asin'] = $asin;
  variable_set('amazon_devel_generate_asins', $asins);
  variable_set('amazon_devel_generate_item_page', $amazon_item_page);
  return $field;
}

/**
 * Allow selection of the SearchIndex and Keywords to be used
 */
function asin_form_devel_generate_content_form_alter(&$form, $form_state) {
  $form['asin_options'] = array(
    '#type' => 'fieldset',
    '#title' => t('Amazon ASIN Field Configuration'),
    '#expanded' => FALSE,
  );

  $form['asin_options']['asin_devel_generate_search_index'] = array(
    '#type' => 'select',
    '#title' => t('Amazon SearchIndex'),
    '#options' => drupal_map_assoc(array('All', 'Books', 'DVD', 'Electronics', 'Kitchen', 'Software')),
    '#default_value' => variable_get('asin_devel_generate_search_index', 'All'),
  );
  $form['asin_options']['asin_devel_generate_keywords'] = array(
    '#type' => 'textfield',
    '#title' => t('Amazon.com search keywords for ASIN fields'),
    '#description' => t('Comma-separated list of keywords to be used in search to populate Amazon module asin fields'),
    '#default_value' => variable_get('asin_devel_generate_keywords', 'computers'),
  );
  $form['submit']['#weight'] = 99;
  array_unshift($form['#submit'], 'asin_devel_generate_set_values');
}

function asin_devel_generate_set_values($form, &$form_state) {
  variable_set('asin_devel_generate_keywords', $form_state['values']['asin_devel_generate_keywords']);
  variable_set('asin_devel_generate_search_index', $form_state['values']['asin_devel_generate_search_index']);
}
